x86_64: add a helper function that displaces an address by a given amount.

The new function will be used in an follow up CL.

Add a gtest that constructs addresses with some initial displacement and
checks that they are equal to addresses constructed in the same way only
without displacement, and then displaced.

Bug: 65872996
Test: m test-art-host-gtest  # changes covered by the new test
Change-Id: Id282707077b513bf5911afad1cdd481a1611b55c
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index e5a9ce4..5bc6a3d 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -112,6 +112,22 @@
     return value;
   }
 
+  int32_t disp() const {
+    switch (mod()) {
+      case 0:
+        // With mod 00b RBP is special and means disp32 (either in r/m or in SIB base).
+        return (rm() == RBP || (rm() == RSP && base() == RBP)) ? disp32() : 0;
+      case 1:
+        return disp8();
+      case 2:
+        return disp32();
+      default:
+        // Mod 11b means reg/reg, so there is no address and consequently no displacement.
+        DCHECK(false) << "there is no displacement in x86_64 reg/reg operand";
+        UNREACHABLE();
+    }
+  }
+
   bool IsRegister(CpuRegister reg) const {
     return ((encoding_[0] & 0xF8) == 0xC0)  // Addressing mode is register only.
         && ((encoding_[0] & 0x07) == reg.LowBits())  // Register codes match.
@@ -122,6 +138,13 @@
     return fixup_;
   }
 
+  inline bool operator==(const Operand &op) const {
+    return rex_ == op.rex_ &&
+        length_ == op.length_ &&
+        memcmp(encoding_, op.encoding_, length_) == 0 &&
+        fixup_ == op.fixup_;
+  }
+
  protected:
   // Operand can be sub classed (e.g: Address).
   Operand() : rex_(0), length_(0), fixup_(nullptr) { }
@@ -224,7 +247,6 @@
     }
   }
 
-
   Address(CpuRegister index_in, ScaleFactor scale_in, int32_t disp) {
     CHECK_NE(index_in.AsRegister(), RSP);  // Illegal addressing mode.
     SetModRM(0, CpuRegister(RSP));
@@ -280,6 +302,50 @@
     return Absolute(addr.Int32Value(), no_rip);
   }
 
+  // Break the address into pieces and reassemble it again with a new displacement.
+  // Note that it may require a new addressing mode if displacement size is changed.
+  static Address displace(const Address &addr, int32_t disp) {
+    const int32_t new_disp = addr.disp() + disp;
+    const bool sib = addr.rm() == RSP;
+    const bool rbp = RBP == (sib ? addr.base() : addr.rm());
+    Address new_addr;
+    if (addr.mod() == 0 && rbp) {
+      // Special case: mod 00b and RBP in r/m or SIB base => 32-bit displacement.
+      // This case includes RIP-relative addressing.
+      new_addr.SetModRM(0, addr.cpu_rm());
+      if (sib) {
+        new_addr.SetSIB(addr.scale(), addr.cpu_index(), addr.cpu_base());
+      }
+      new_addr.SetDisp32(new_disp);
+    } else if (new_disp == 0 && !rbp) {
+      // Mod 00b (excluding a special case for RBP) => no displacement.
+      new_addr.SetModRM(0, addr.cpu_rm());
+      if (sib) {
+        new_addr.SetSIB(addr.scale(), addr.cpu_index(), addr.cpu_base());
+      }
+    } else if (new_disp >= -128 && new_disp <= 127) {
+      // Mod 01b => 8-bit displacement.
+      new_addr.SetModRM(1, addr.cpu_rm());
+      if (sib) {
+        new_addr.SetSIB(addr.scale(), addr.cpu_index(), addr.cpu_base());
+      }
+      new_addr.SetDisp8(new_disp);
+    } else {
+      // Mod 10b => 32-bit displacement.
+      new_addr.SetModRM(2, addr.cpu_rm());
+      if (sib) {
+        new_addr.SetSIB(addr.scale(), addr.cpu_index(), addr.cpu_base());
+      }
+      new_addr.SetDisp32(new_disp);
+    }
+    new_addr.SetFixup(addr.GetFixup());
+    return new_addr;
+  }
+
+  inline bool operator==(const Address& addr) const {
+    return static_cast<const Operand&>(*this) == static_cast<const Operand&>(addr);
+  }
+
  private:
   Address() {}
 };