cranelift_assembler_x64/
rex.rs

1//! Encoding logic for REX instructions.
2
3use crate::api::CodeSink;
4
5fn low8_will_sign_extend_to_32(xs: i32) -> bool {
6    xs == ((xs << 24) >> 24)
7}
8
9/// Encode the ModR/M byte.
10#[inline]
11pub(crate) fn encode_modrm(m0d: u8, enc_reg_g: u8, rm_e: u8) -> u8 {
12    debug_assert!(m0d < 4);
13    debug_assert!(enc_reg_g < 8);
14    debug_assert!(rm_e < 8);
15    ((m0d & 3) << 6) | ((enc_reg_g & 7) << 3) | (rm_e & 7)
16}
17
18/// Encode the SIB byte (scale-index-base).
19#[inline]
20pub(crate) fn encode_sib(scale: u8, enc_index: u8, enc_base: u8) -> u8 {
21    debug_assert!(scale < 4);
22    debug_assert!(enc_index < 8);
23    debug_assert!(enc_base < 8);
24    ((scale & 3) << 6) | ((enc_index & 7) << 3) | (enc_base & 7)
25}
26
27/// Force emission of the REX byte if the register is: `rsp`, `rbp`, `rsi`,
28/// `rdi`.
29const fn is_special(enc: u8) -> bool {
30    enc >= 4 && enc <= 7
31}
32
33/// Construct and emit the REX prefix byte.
34///
35/// For more details, see section 2.2.1, "REX Prefixes" in Intel's reference
36/// manual.
37#[derive(Clone, Copy)]
38pub struct RexPrefix {
39    byte: u8,
40    must_emit: bool,
41}
42
43impl RexPrefix {
44    /// Construct the [`RexPrefix`] for a unary instruction.
45    ///
46    /// Used with a single register operand:
47    /// - `x` and `r` are unused.
48    /// - `b` extends the `reg` register, allowing access to r8-r15, or the top
49    ///   bit of the opcode digit.
50    #[inline]
51    #[must_use]
52    pub const fn one_op(enc: u8, w_bit: bool, uses_8bit: bool) -> Self {
53        let must_emit = uses_8bit && is_special(enc);
54        let w = if w_bit { 1 } else { 0 };
55        let r = 0;
56        let x = 0;
57        let b = (enc >> 3) & 1;
58        let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
59        Self {
60            byte: flag,
61            must_emit,
62        }
63    }
64
65    /// Construct the [`RexPrefix`] for a binary instruction.
66    ///
67    /// Used without a SIB byte or for register-to-register addressing:
68    /// - `r` extends the `reg` operand, allowing access to r8-r15.
69    /// - `x` is unused.
70    /// - `b` extends the `r/m` operand, allowing access to r8-r15.
71    #[inline]
72    #[must_use]
73    pub const fn two_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {
74        let must_emit = uses_8bit && (is_special(enc_rm) || is_special(enc_reg));
75        let w = if w_bit { 1 } else { 0 };
76        let r = (enc_reg >> 3) & 1;
77        let x = 0;
78        let b = (enc_rm >> 3) & 1;
79        let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
80        Self {
81            byte: flag,
82            must_emit,
83        }
84    }
85
86    /// Construct the [`RexPrefix`] for an instruction using an opcode digit.
87    ///
88    /// :
89    /// - `r` extends the opcode digit.
90    /// - `x` is unused.
91    /// - `b` extends the `reg` operand, allowing access to r8-r15.
92    #[inline]
93    #[must_use]
94    pub const fn with_digit(digit: u8, enc_reg: u8, w_bit: bool, uses_8bit: bool) -> Self {
95        Self::two_op(digit, enc_reg, w_bit, uses_8bit)
96    }
97
98    /// Construct the [`RexPrefix`] for a ternary instruction, typically using a
99    /// memory address.
100    ///
101    /// Used with a SIB byte:
102    /// - `r` extends the `reg` operand, allowing access to r8-r15.
103    /// - `x` extends the index register, allowing access to r8-r15.
104    /// - `b` extends the base register, allowing access to r8-r15.
105    #[inline]
106    #[must_use]
107    pub const fn three_op(
108        enc_reg: u8,
109        enc_index: u8,
110        enc_base: u8,
111        w_bit: bool,
112        uses_8bit: bool,
113    ) -> Self {
114        let must_emit =
115            uses_8bit && (is_special(enc_reg) || is_special(enc_base) || is_special(enc_index));
116        let w = if w_bit { 1 } else { 0 };
117        let r = (enc_reg >> 3) & 1;
118        let x = (enc_index >> 3) & 1;
119        let b = (enc_base >> 3) & 1;
120        let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
121        Self {
122            byte: flag,
123            must_emit,
124        }
125    }
126
127    /// Possibly emit the REX prefix byte.
128    ///
129    /// This will only be emitted if the REX prefix is not `0x40` (the default)
130    /// or if the instruction uses 8-bit operands.
131    #[inline]
132    pub fn encode(&self, sink: &mut impl CodeSink) {
133        if self.byte != 0x40 || self.must_emit {
134            sink.put1(self.byte);
135        }
136    }
137}
138
139/// The displacement bytes used after the ModR/M and SIB bytes.
140#[derive(Copy, Clone)]
141pub enum Disp {
142    None,
143    Imm8(i8),
144    Imm32(i32),
145}
146
147impl Disp {
148    /// Classifies the 32-bit immediate `val` as how this can be encoded
149    /// with ModRM/SIB bytes.
150    ///
151    /// For `evex_scaling` according to Section 2.7.5 of Intel's manual:
152    ///
153    /// > EVEX-encoded instructions always use a compressed displacement scheme
154    /// > by multiplying disp8 in conjunction with a scaling factor N that is
155    /// > determined based on the vector length, the value of EVEX.b bit
156    /// > (embedded broadcast) and the input element size of the instruction
157    ///
158    /// The `evex_scaling` factor provided here is `Some(N)` for EVEX
159    /// instructions.  This is taken into account where the `Imm` value
160    /// contained is the raw byte offset.
161    pub fn new(val: i32, evex_scaling: Option<i8>) -> Disp {
162        if val == 0 {
163            return Disp::None;
164        }
165        match evex_scaling {
166            Some(scaling) => {
167                if val % i32::from(scaling) == 0 {
168                    let scaled = val / i32::from(scaling);
169                    if low8_will_sign_extend_to_32(scaled) {
170                        return Disp::Imm8(scaled as i8);
171                    }
172                }
173                Disp::Imm32(val)
174            }
175            None => match i8::try_from(val) {
176                Ok(val) => Disp::Imm8(val),
177                Err(_) => Disp::Imm32(val),
178            },
179        }
180    }
181
182    /// Forces `Imm::None` to become `Imm::Imm8(0)`, used for special cases
183    /// where some base registers require an immediate.
184    pub fn force_immediate(&mut self) {
185        if let Disp::None = self {
186            *self = Disp::Imm8(0);
187        }
188    }
189
190    /// Returns the two "mod" bits present at the upper bits of the mod/rm
191    /// byte.
192    pub fn m0d(self) -> u8 {
193        match self {
194            Disp::None => 0b00,
195            Disp::Imm8(_) => 0b01,
196            Disp::Imm32(_) => 0b10,
197        }
198    }
199
200    /// Emit the truncated immediate into the code sink.
201    pub fn emit(self, sink: &mut impl CodeSink) {
202        match self {
203            Disp::None => {}
204            Disp::Imm8(n) => sink.put1(n as u8),
205            Disp::Imm32(n) => sink.put4(n as u32),
206        }
207    }
208}