Path: blob/main/cranelift/assembler-x64/src/rex.rs
1692 views
//! Encoding logic for REX instructions.12use crate::api::CodeSink;34fn low8_will_sign_extend_to_32(xs: i32) -> bool {5xs == ((xs << 24) >> 24)6}78/// Encode the ModR/M byte.9#[inline]10pub(crate) fn encode_modrm(m0d: u8, enc_reg_g: u8, rm_e: u8) -> u8 {11debug_assert!(m0d < 4);12debug_assert!(enc_reg_g < 8);13debug_assert!(rm_e < 8);14((m0d & 3) << 6) | ((enc_reg_g & 7) << 3) | (rm_e & 7)15}1617/// Encode the SIB byte (scale-index-base).18#[inline]19pub(crate) fn encode_sib(scale: u8, enc_index: u8, enc_base: u8) -> u8 {20debug_assert!(scale < 4);21debug_assert!(enc_index < 8);22debug_assert!(enc_base < 8);23((scale & 3) << 6) | ((enc_index & 7) << 3) | (enc_base & 7)24}2526/// Tests whether `enc` is `rsp`, `rbp`, `rsi`, or `rdi`. If 8-bit register27/// sizes are used then it means a REX prefix is required.28///29/// This function is used below in combination with `uses_8bit` booleans to30/// determine the `RexPrefix::must_emit` flag. Table 3-2 in volume 1 of the31/// Intel manual details how referencing `dil`, the low 8-bits of `rdi`,32/// requires the use of the REX prefix as without it it would otherwise33/// reference the `AH` register.34///35/// This is used whenever a register is encoded with a `RexPrefix` and is also36/// only used if the register is referenced in its 8-bit form. That means for37/// example that when encoding addressing modes this function is not used.38/// Addressing modes use 64-bit versions of registers meaning that the 8-bit39/// special case does not apply.40const fn is_special_if_8bit(enc: u8) -> bool {41enc >= 4 && enc <= 742}4344/// Construct and emit the REX prefix byte.45///46/// For more details, see section 2.2.1, "REX Prefixes" in Intel's reference47/// manual.48#[derive(Clone, Copy)]49pub struct RexPrefix {50byte: u8,51must_emit: bool,52}5354impl RexPrefix {55/// Construct the [`RexPrefix`] for a unary instruction.56///57/// Used with a single register operand:58/// - `x` and `r` are unused.59/// - `b` extends the `reg` register, allowing access to r8-r15, or the top60/// bit of the opcode digit.61#[inline]62#[must_use]63pub const fn one_op(enc: u8, w_bit: bool, uses_8bit: bool) -> Self {64let must_emit = uses_8bit && is_special_if_8bit(enc);65let w = if w_bit { 1 } else { 0 };66let r = 0;67let x = 0;68let b = (enc >> 3) & 1;69let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;70Self {71byte: flag,72must_emit,73}74}7576/// Construct the [`RexPrefix`] for a binary instruction.77///78/// Used without a SIB byte or for register-to-register addressing:79/// - `r` extends the `reg` operand, allowing access to r8-r15.80/// - `x` is unused.81/// - `b` extends the `r/m` operand, allowing access to r8-r15.82#[inline]83#[must_use]84pub const fn two_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {85let mut ret = RexPrefix::mem_op(enc_reg, enc_rm, w_bit, uses_8bit);86if uses_8bit && is_special_if_8bit(enc_rm) {87ret.must_emit = true;88}89ret90}9192/// Construct the [`RexPrefix`] for a binary instruction where one operand93/// is a memory address.94///95/// This is the same as [`RexPrefix::two_op`] except that `enc_rm` is96/// guaranteed to address a 64-bit register. This has a slightly different97/// meaning when `uses_8bit` is `true` to omit the REX prefix in more cases98/// than `two_op` would emit.99#[inline]100#[must_use]101pub const fn mem_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {102let must_emit = uses_8bit && is_special_if_8bit(enc_reg);103let w = if w_bit { 1 } else { 0 };104let r = (enc_reg >> 3) & 1;105let x = 0;106let b = (enc_rm >> 3) & 1;107let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;108Self {109byte: flag,110must_emit,111}112}113114/// Construct the [`RexPrefix`] for an instruction using an opcode digit.115///116/// :117/// - `r` extends the opcode digit.118/// - `x` is unused.119/// - `b` extends the `reg` operand, allowing access to r8-r15.120#[inline]121#[must_use]122pub const fn with_digit(digit: u8, enc_reg: u8, w_bit: bool, uses_8bit: bool) -> Self {123Self::two_op(digit, enc_reg, w_bit, uses_8bit)124}125126/// Construct the [`RexPrefix`] for a ternary instruction, typically using a127/// memory address.128///129/// Used with a SIB byte:130/// - `r` extends the `reg` operand, allowing access to r8-r15.131/// - `x` extends the index register, allowing access to r8-r15.132/// - `b` extends the base register, allowing access to r8-r15.133#[inline]134#[must_use]135pub const fn three_op(136enc_reg: u8,137enc_index: u8,138enc_base: u8,139w_bit: bool,140uses_8bit: bool,141) -> Self {142let must_emit = uses_8bit && is_special_if_8bit(enc_reg);143let w = if w_bit { 1 } else { 0 };144let r = (enc_reg >> 3) & 1;145let x = (enc_index >> 3) & 1;146let b = (enc_base >> 3) & 1;147let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;148Self {149byte: flag,150must_emit,151}152}153154/// Possibly emit the REX prefix byte.155///156/// This will only be emitted if the REX prefix is not `0x40` (the default)157/// or if the instruction uses 8-bit operands.158#[inline]159pub fn encode(&self, sink: &mut impl CodeSink) {160if self.byte != 0x40 || self.must_emit {161sink.put1(self.byte);162}163}164}165166/// The displacement bytes used after the ModR/M and SIB bytes.167#[derive(Copy, Clone)]168pub enum Disp {169None,170Imm8(i8),171Imm32(i32),172}173174impl Disp {175/// Classifies the 32-bit immediate `val` as how this can be encoded176/// with ModRM/SIB bytes.177///178/// For `evex_scaling` according to Section 2.7.5 of Intel's manual:179///180/// > EVEX-encoded instructions always use a compressed displacement scheme181/// > by multiplying disp8 in conjunction with a scaling factor N that is182/// > determined based on the vector length, the value of EVEX.b bit183/// > (embedded broadcast) and the input element size of the instruction184///185/// The `evex_scaling` factor provided here is `Some(N)` for EVEX186/// instructions. This is taken into account where the `Imm` value187/// contained is the raw byte offset.188pub fn new(val: i32, evex_scaling: Option<i8>) -> Disp {189if val == 0 {190return Disp::None;191}192match evex_scaling {193Some(scaling) => {194if val % i32::from(scaling) == 0 {195let scaled = val / i32::from(scaling);196if low8_will_sign_extend_to_32(scaled) {197return Disp::Imm8(scaled as i8);198}199}200Disp::Imm32(val)201}202None => match i8::try_from(val) {203Ok(val) => Disp::Imm8(val),204Err(_) => Disp::Imm32(val),205},206}207}208209/// Forces `Imm::None` to become `Imm::Imm8(0)`, used for special cases210/// where some base registers require an immediate.211pub fn force_immediate(&mut self) {212if let Disp::None = self {213*self = Disp::Imm8(0);214}215}216217/// Returns the two "mod" bits present at the upper bits of the mod/rm218/// byte.219pub fn m0d(self) -> u8 {220match self {221Disp::None => 0b00,222Disp::Imm8(_) => 0b01,223Disp::Imm32(_) => 0b10,224}225}226227/// Emit the truncated immediate into the code sink.228pub fn emit(self, sink: &mut impl CodeSink) {229match self {230Disp::None => {}231Disp::Imm8(n) => sink.put1(n as u8),232Disp::Imm32(n) => sink.put4(n as u32),233}234}235}236237238