cranelift_assembler_x64/
rex.rs

1//! Encoding logic for REX instructions.
2
3use crate::api::CodeSink;
4
5pub(crate) fn 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 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 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/// A small bit field to record a REX prefix specification:
28/// - bit 0 set to 1 indicates REX.W must be 0 (cleared).
29/// - bit 1 set to 1 indicates the REX prefix must always be emitted.
30#[repr(transparent)]
31#[derive(Clone, Copy)]
32pub struct RexFlags(u8);
33
34impl RexFlags {
35    /// By default, set the W field, and don't always emit.
36    #[inline]
37    #[must_use]
38    pub fn set_w() -> Self {
39        Self(0)
40    }
41
42    /// Creates a new REX prefix for which the REX.W bit will be cleared.
43    #[inline]
44    #[must_use]
45    pub fn clear_w() -> Self {
46        Self(1)
47    }
48
49    /// True if 64-bit operands are used.
50    #[inline]
51    #[must_use]
52    pub fn must_clear_w(self) -> bool {
53        (self.0 & 1) != 0
54    }
55
56    /// Require that the REX prefix is emitted.
57    #[inline]
58    pub fn always_emit(&mut self) -> &mut Self {
59        self.0 |= 2;
60        self
61    }
62
63    /// True if the REX prefix must always be emitted.
64    #[inline]
65    #[must_use]
66    pub fn must_always_emit(self) -> bool {
67        (self.0 & 2) != 0
68    }
69
70    /// Force emission of the REX byte if the register is: `rsp`, `rbp`, `rsi`,
71    /// `rdi`.
72    pub fn always_emit_if_8bit_needed(&mut self, enc: u8) {
73        if (4..=7).contains(&enc) {
74            self.always_emit();
75        }
76    }
77
78    /// Emit a unary instruction.
79    #[inline]
80    pub fn emit_one_op(self, sink: &mut impl CodeSink, enc_e: u8) {
81        // Register Operand coded in Opcode Byte
82        // REX.R and REX.X unused
83        // REX.B == 1 accesses r8-r15
84        let w = if self.must_clear_w() { 0 } else { 1 };
85        let r = 0;
86        let x = 0;
87        let b = (enc_e >> 3) & 1;
88        let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
89        if rex != 0x40 || self.must_always_emit() {
90            sink.put1(rex);
91        }
92    }
93
94    /// Emit a binary instruction.
95    #[inline]
96    pub fn emit_two_op(self, sink: &mut impl CodeSink, enc_g: u8, enc_e: u8) {
97        let w = if self.must_clear_w() { 0 } else { 1 };
98        let r = (enc_g >> 3) & 1;
99        let x = 0;
100        let b = (enc_e >> 3) & 1;
101        let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
102        if rex != 0x40 || self.must_always_emit() {
103            sink.put1(rex);
104        }
105    }
106
107    /// Emit a ternary instruction.
108    #[inline]
109    pub fn emit_three_op(self, sink: &mut impl CodeSink, enc_g: u8, enc_index: u8, enc_base: u8) {
110        let w = if self.must_clear_w() { 0 } else { 1 };
111        let r = (enc_g >> 3) & 1;
112        let x = (enc_index >> 3) & 1;
113        let b = (enc_base >> 3) & 1;
114        let rex = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
115        if rex != 0x40 || self.must_always_emit() {
116            sink.put1(rex);
117        }
118    }
119}
120
121#[derive(Copy, Clone)]
122pub enum Imm {
123    None,
124    Imm8(i8),
125    Imm32(i32),
126}
127
128impl Imm {
129    /// Classifies the 32-bit immediate `val` as how this can be encoded
130    /// with ModRM/SIB bytes.
131    ///
132    /// For `evex_scaling` according to Section 2.7.5 of Intel's manual:
133    ///
134    /// > EVEX-encoded instructions always use a compressed displacement scheme
135    /// > by multiplying disp8 in conjunction with a scaling factor N that is
136    /// > determined based on the vector length, the value of EVEX.b bit
137    /// > (embedded broadcast) and the input element size of the instruction
138    ///
139    /// The `evex_scaling` factor provided here is `Some(N)` for EVEX
140    /// instructions.  This is taken into account where the `Imm` value
141    /// contained is the raw byte offset.
142    pub fn new(val: i32, evex_scaling: Option<i8>) -> Imm {
143        if val == 0 {
144            return Imm::None;
145        }
146        match evex_scaling {
147            Some(scaling) => {
148                if val % i32::from(scaling) == 0 {
149                    let scaled = val / i32::from(scaling);
150                    if low8_will_sign_extend_to_32(scaled) {
151                        return Imm::Imm8(scaled as i8);
152                    }
153                }
154                Imm::Imm32(val)
155            }
156            None => match i8::try_from(val) {
157                Ok(val) => Imm::Imm8(val),
158                Err(_) => Imm::Imm32(val),
159            },
160        }
161    }
162
163    /// Forces `Imm::None` to become `Imm::Imm8(0)`, used for special cases
164    /// where some base registers require an immediate.
165    pub fn force_immediate(&mut self) {
166        if let Imm::None = self {
167            *self = Imm::Imm8(0);
168        }
169    }
170
171    /// Returns the two "mod" bits present at the upper bits of the mod/rm
172    /// byte.
173    pub fn m0d(self) -> u8 {
174        match self {
175            Imm::None => 0b00,
176            Imm::Imm8(_) => 0b01,
177            Imm::Imm32(_) => 0b10,
178        }
179    }
180
181    /// Emit the truncated immediate into the code sink.
182    pub fn emit(self, sink: &mut impl CodeSink) {
183        match self {
184            Imm::None => {}
185            Imm::Imm8(n) => sink.put1(n as u8),
186            Imm::Imm32(n) => sink.put4(n as u32),
187        }
188    }
189}