[x64] Refactor representation of {Operand}

Make the separation between Operands pointing to Label locations and
standard memory operands more clear.
Also provide a separate method for emitting "label operands", so this
does not get inlined everywhere (label operands are used much less often
than memory operands).

R=jkummerow@chromium.org

Bug: v8:13570
Change-Id: I3482598cbf47eea878e06acc1ce2465325a597e0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4088644
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84763}
This commit is contained in:
Clemens Backes 2022-12-09 15:00:51 +01:00 committed by V8 LUCI CQ
parent 2635ba5a7f
commit 748e6d7c45
2 changed files with 48 additions and 36 deletions

View File

@ -584,6 +584,12 @@ void Assembler::GrowBuffer() {
}
void Assembler::emit_operand(int code, Operand adr) {
// Redirect to {emit_label_operand} if {adr} contains a label.
if (adr.data().is_label_operand) {
emit_label_operand(code, adr.data().label, adr.data().addend);
return;
}
DCHECK(is_uint3(code));
const unsigned length = adr.data().len;
DCHECK_GT(length, 0);
@ -591,29 +597,27 @@ void Assembler::emit_operand(int code, Operand adr) {
// Emit updated ModR/M byte containing the given register.
DCHECK_EQ(adr.data().buf[0] & 0x38, 0);
*pc_++ = adr.data().buf[0] | code << 3;
// Emit the rest of the encoded operand.
for (unsigned i = 1; i < length; i++) *pc_++ = adr.data().buf[i];
}
// Recognize RIP relative addressing.
if (adr.data().buf[0] == 5) {
DCHECK_EQ(9u, length);
Label* label = ReadUnalignedValue<Label*>(
reinterpret_cast<Address>(&adr.data().buf[1]));
if (label->is_bound()) {
int offset =
label->pos() - pc_offset() - sizeof(int32_t) + adr.data().addend;
DCHECK_GE(0, offset);
emitl(offset);
} else if (label->is_linked()) {
emitl(label->pos());
label->link_to(pc_offset() - sizeof(int32_t));
} else {
DCHECK(label->is_unused());
int32_t current = pc_offset();
emitl(current);
label->link_to(current);
}
void Assembler::emit_label_operand(int code, Label* label, int addend) {
DCHECK(addend == 0 || (is_int8(addend) && label->is_bound()));
V8_ASSUME(0 <= code && code <= 7);
*pc_++ = 5 | (code << 3);
if (label->is_bound()) {
int offset = label->pos() - pc_offset() - sizeof(int32_t) + addend;
DCHECK_GE(0, offset);
emitl(offset);
} else if (label->is_linked()) {
emitl(label->pos());
label->link_to(pc_offset() - sizeof(int32_t));
} else {
// Emit the rest of the encoded operand.
for (unsigned i = 1; i < length; i++) *pc_++ = adr.data().buf[i];
DCHECK(label->is_unused());
int32_t current = pc_offset();
emitl(current);
label->link_to(current);
}
}

View File

@ -161,11 +161,22 @@ enum ScaleFactor : int8_t {
class V8_EXPORT_PRIVATE Operand {
public:
struct Data {
byte rex = 0;
byte buf[9] = {0};
byte len = 1; // number of bytes of buf_ in use.
int8_t addend = 0; // for rip + offset + addend.
union {
// {label} is set if {is_label_operand} is true.
Label* label;
// Buffer for encoded memory operand:
// Register (1 byte) + SIB (0 or 1 byte) + displacement (0, 1, or 4 byte).
byte buf[6] = {0};
};
byte len = 1; // Number of bytes of buf in use.
bool is_label_operand = false;
byte rex = 0; // Rex prefix, only for memory operands.
int8_t addend = 0; // For label-relative operand: rip + offset + addend.
};
// {Data::len} is 8-byte aligned, which makes it fast to access.
static_assert(offsetof(Data, len) % kSystemPointerSize == 0);
// [base + disp/r]
V8_INLINE constexpr Operand(Register base, int32_t disp) {
@ -221,8 +232,8 @@ class V8_EXPORT_PRIVATE Operand {
data_.addend = addend;
DCHECK_NOT_NULL(label);
DCHECK(addend == 0 || (is_int8(addend) && label->is_bound()));
set_modrm(0, rbp);
set_disp64(reinterpret_cast<intptr_t>(label));
data_.is_label_operand = true;
data_.label = label;
}
Operand(const Operand&) V8_NOEXCEPT = default;
@ -268,13 +279,6 @@ class V8_EXPORT_PRIVATE Operand {
data_.len += sizeof(int32_t);
}
V8_INLINE void set_disp64(int64_t disp) {
DCHECK_EQ(1, data_.len);
Address p = reinterpret_cast<Address>(&data_.buf[data_.len]);
WriteUnalignedValue(p, disp);
data_.len += sizeof(disp);
}
Data data_;
};
@ -2287,10 +2291,14 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
}
// Emit the ModR/M byte, and optionally the SIB byte and
// 1- or 4-byte offset for a memory operand. Also used to encode
// a three-bit opcode extension into the ModR/M byte.
// 1- or 4-byte offset for a memory operand.
// Also used to encode a three-bit opcode extension into the ModR/M byte.
void emit_operand(int rm, Operand adr);
// Emit a RIP-relative operand.
// Also used to encode a three-bit opcode extension into the ModR/M byte.
V8_NOINLINE void emit_label_operand(int rm, Label* label, int addend = 0);
// Emit a ModR/M byte with registers coded in the reg and rm_reg fields.
void emit_modrm(Register reg, Register rm_reg) {
emit(0xC0 | reg.low_bits() << 3 | rm_reg.low_bits());