machinst x64: refactor REX prefix emission;
This commit is contained in:
@@ -76,17 +76,38 @@ impl Rex {
|
||||
self
|
||||
}
|
||||
|
||||
/// Return whether the W bit in the REX prefix is zero.
|
||||
#[inline(always)]
|
||||
fn must_clear_w(&self) -> bool {
|
||||
(self.0 & 1) != 0
|
||||
}
|
||||
/// Return whether we need to emit the REX prefix byte even if it appears
|
||||
/// to be redundant (== 0x40).
|
||||
#[inline(always)]
|
||||
fn must_always_emit(&self) -> bool {
|
||||
(self.0 & 2) != 0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn emit_two_op(&self, sink: &mut MachBuffer<Inst>, enc_g: u8, enc_e: u8) {
|
||||
let w = if self.must_clear_w() { 0 } else { 1 };
|
||||
let r = (enc_g >> 3) & 1;
|
||||
let x = 0;
|
||||
let b = (enc_e >> 3) & 1;
|
||||
let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
|
||||
if rex != 0x40 || self.must_always_emit() {
|
||||
sink.put1(rex);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn emit_three_op(&self, sink: &mut MachBuffer<Inst>, enc_g: u8, enc_index: u8, enc_base: u8) {
|
||||
let w = if self.must_clear_w() { 0 } else { 1 };
|
||||
let r = (enc_g >> 3) & 1;
|
||||
let x = (enc_index >> 3) & 1;
|
||||
let b = (enc_base >> 3) & 1;
|
||||
let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
|
||||
if rex != 0x40 || self.must_always_emit() {
|
||||
sink.put1(rex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For specifying the legacy prefixes (or `None` if no prefix required) to
|
||||
@@ -140,26 +161,17 @@ fn emit_modrm_sib_enc_ge(
|
||||
mem_e: &Addr,
|
||||
rex: Rex,
|
||||
) {
|
||||
// General comment for this function: the registers in `memE` must be
|
||||
// General comment for this function: the registers in `mem_e` must be
|
||||
// 64-bit integer registers, because they are part of an address
|
||||
// expression. But `enc_g` can be derived from a register of any class.
|
||||
let clear_rex_w = rex.must_clear_w();
|
||||
let retain_redundant = rex.must_always_emit();
|
||||
|
||||
prefix.emit(sink);
|
||||
|
||||
match mem_e {
|
||||
Addr::ImmReg { simm32, base } => {
|
||||
// First, cook up the REX byte. This is easy.
|
||||
// First, the REX byte.
|
||||
let enc_e = int_reg_enc(*base);
|
||||
let w = if clear_rex_w { 0 } else { 1 };
|
||||
let r = (enc_g >> 3) & 1;
|
||||
let x = 0;
|
||||
let b = (enc_e >> 3) & 1;
|
||||
let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
|
||||
if rex != 0x40 || retain_redundant {
|
||||
sink.put1(rex);
|
||||
}
|
||||
rex.emit_two_op(sink, enc_g, enc_e);
|
||||
|
||||
// Now the opcode(s). These include any other prefixes the caller
|
||||
// hands to us.
|
||||
@@ -220,14 +232,7 @@ fn emit_modrm_sib_enc_ge(
|
||||
let enc_index = int_reg_enc(*reg_index);
|
||||
|
||||
// The rex byte.
|
||||
let w = if clear_rex_w { 0 } else { 1 };
|
||||
let r = (enc_g >> 3) & 1;
|
||||
let x = (enc_index >> 3) & 1;
|
||||
let b = (enc_base >> 3) & 1;
|
||||
let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
|
||||
if rex != 0x40 || retain_redundant {
|
||||
sink.put1(rex);
|
||||
}
|
||||
rex.emit_three_op(sink, enc_g, enc_index, enc_base);
|
||||
|
||||
// All other prefixes and opcodes.
|
||||
while num_opcodes > 0 {
|
||||
@@ -268,21 +273,12 @@ fn emit_modrm_enc_ge(
|
||||
// don't even have to be from the same class. For example, for an
|
||||
// integer-to-FP conversion insn, one might be RegClass::I64 and the other
|
||||
// RegClass::V128.
|
||||
let clear_rex_w = rex.must_clear_w();
|
||||
let retain_redundant = rex.must_always_emit();
|
||||
|
||||
// The operand-size override.
|
||||
prefix.emit(sink);
|
||||
|
||||
// The rex byte.
|
||||
let w = if clear_rex_w { 0 } else { 1 };
|
||||
let r = (enc_g >> 3) & 1;
|
||||
let x = 0;
|
||||
let b = (enc_e >> 3) & 1;
|
||||
let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
|
||||
if rex != 0x40 || retain_redundant {
|
||||
sink.put1(rex);
|
||||
}
|
||||
rex.emit_two_op(sink, enc_g, enc_e);
|
||||
|
||||
// All other prefixes and opcodes.
|
||||
while num_opcodes > 0 {
|
||||
|
||||
Reference in New Issue
Block a user