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() {}
};