1 //! Encoding logic for REX instructions.
2 
3 use crate::api::CodeSink;
4 
5 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]
11 pub(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]
20 pub(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 /// Tests whether `enc` is `rsp`, `rbp`, `rsi`, or `rdi`. If 8-bit register
28 /// sizes are used then it means a REX prefix is required.
29 ///
30 /// This function is used below in combination with `uses_8bit` booleans to
31 /// determine the `RexPrefix::must_emit` flag. Table 3-2 in volume 1 of the
32 /// Intel manual details how referencing `dil`, the low 8-bits of `rdi`,
33 /// requires the use of the REX prefix as without it, it would otherwise
34 /// reference the `AH` register.
35 ///
36 /// This is used whenever a register is encoded with a `RexPrefix` and is also
37 /// only used if the register is referenced in its 8-bit form. That means for
38 /// example that when encoding addressing modes this function is not used.
39 /// Addressing modes use 64-bit versions of registers meaning that the 8-bit
40 /// special case does not apply.
41 const fn is_special_if_8bit(enc: u8) -> bool {
42     enc >= 4 && enc <= 7
43 }
44 
45 /// Construct and emit the REX prefix byte.
46 ///
47 /// For more details, see section 2.2.1, "REX Prefixes" in Intel's reference
48 /// manual.
49 #[derive(Clone, Copy)]
50 pub struct RexPrefix {
51     byte: u8,
52     must_emit: bool,
53 }
54 
55 impl RexPrefix {
56     /// Construct the [`RexPrefix`] for a unary instruction.
57     ///
58     /// Used with a single register operand:
59     /// - `x` and `r` are unused.
60     /// - `b` extends the `reg` register, allowing access to r8-r15, or the top
61     ///   bit of the opcode digit.
62     #[inline]
63     #[must_use]
64     pub const fn one_op(enc: u8, w_bit: bool, uses_8bit: bool) -> Self {
65         let must_emit = uses_8bit && is_special_if_8bit(enc);
66         let w = if w_bit { 1 } else { 0 };
67         let r = 0;
68         let x = 0;
69         let b = (enc >> 3) & 1;
70         let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
71         Self {
72             byte: flag,
73             must_emit,
74         }
75     }
76 
77     /// Construct the [`RexPrefix`] for a binary instruction.
78     ///
79     /// Used without a SIB byte or for register-to-register addressing:
80     /// - `r` extends the `reg` operand, allowing access to r8-r15.
81     /// - `x` is unused.
82     /// - `b` extends the `r/m` operand, allowing access to r8-r15.
83     #[inline]
84     #[must_use]
85     pub const fn two_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {
86         let mut ret = RexPrefix::mem_op(enc_reg, enc_rm, w_bit, uses_8bit);
87         if uses_8bit && is_special_if_8bit(enc_rm) {
88             ret.must_emit = true;
89         }
90         ret
91     }
92 
93     /// Construct the [`RexPrefix`] for a binary instruction where one operand
94     /// is a memory address.
95     ///
96     /// This is the same as [`RexPrefix::two_op`] except that `enc_rm` is
97     /// guaranteed to address a 64-bit register. This has a slightly different
98     /// meaning when `uses_8bit` is `true` to omit the REX prefix in more cases
99     /// than `two_op` would emit.
100     #[inline]
101     #[must_use]
102     pub const fn mem_op(enc_reg: u8, enc_rm: u8, w_bit: bool, uses_8bit: bool) -> Self {
103         let must_emit = uses_8bit && is_special_if_8bit(enc_reg);
104         let w = if w_bit { 1 } else { 0 };
105         let r = (enc_reg >> 3) & 1;
106         let x = 0;
107         let b = (enc_rm >> 3) & 1;
108         let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
109         Self {
110             byte: flag,
111             must_emit,
112         }
113     }
114 
115     /// Construct the [`RexPrefix`] for an instruction using an opcode digit.
116     ///
117     /// :
118     /// - `r` extends the opcode digit.
119     /// - `x` is unused.
120     /// - `b` extends the `reg` operand, allowing access to r8-r15.
121     #[inline]
122     #[must_use]
123     pub const fn with_digit(digit: u8, enc_reg: u8, w_bit: bool, uses_8bit: bool) -> Self {
124         Self::two_op(digit, enc_reg, w_bit, uses_8bit)
125     }
126 
127     /// Construct the [`RexPrefix`] for a ternary instruction, typically using a
128     /// memory address.
129     ///
130     /// Used with a SIB byte:
131     /// - `r` extends the `reg` operand, allowing access to r8-r15.
132     /// - `x` extends the index register, allowing access to r8-r15.
133     /// - `b` extends the base register, allowing access to r8-r15.
134     #[inline]
135     #[must_use]
136     pub const fn three_op(
137         enc_reg: u8,
138         enc_index: u8,
139         enc_base: u8,
140         w_bit: bool,
141         uses_8bit: bool,
142     ) -> Self {
143         let must_emit = uses_8bit && is_special_if_8bit(enc_reg);
144         let w = if w_bit { 1 } else { 0 };
145         let r = (enc_reg >> 3) & 1;
146         let x = (enc_index >> 3) & 1;
147         let b = (enc_base >> 3) & 1;
148         let flag = 0x40 | (w << 3) | (r << 2) | (x << 1) | b;
149         Self {
150             byte: flag,
151             must_emit,
152         }
153     }
154 
155     /// Possibly emit the REX prefix byte.
156     ///
157     /// This will only be emitted if the REX prefix is not `0x40` (the default)
158     /// or if the instruction uses 8-bit operands.
159     #[inline]
160     pub fn encode(&self, sink: &mut impl CodeSink) {
161         if self.byte != 0x40 || self.must_emit {
162             sink.put1(self.byte);
163         }
164     }
165 }
166 
167 /// The displacement bytes used after the ModR/M and SIB bytes.
168 #[derive(Copy, Clone)]
169 pub enum Disp {
170     None,
171     Imm8(i8),
172     Imm32(i32),
173 }
174 
175 impl Disp {
176     /// Classifies the 32-bit immediate `val` as how this can be encoded
177     /// with ModRM/SIB bytes.
178     ///
179     /// For `evex_scaling` according to Section 2.7.5 of Intel's manual:
180     ///
181     /// > EVEX-encoded instructions always use a compressed displacement scheme
182     /// > by multiplying disp8 in conjunction with a scaling factor N that is
183     /// > determined based on the vector length, the value of EVEX.b bit
184     /// > (embedded broadcast) and the input element size of the instruction
185     ///
186     /// The `evex_scaling` factor provided here is `Some(N)` for EVEX
187     /// instructions.  This is taken into account where the `Imm` value
188     /// contained is the raw byte offset.
189     pub fn new(val: i32, evex_scaling: Option<i8>) -> Disp {
190         if val == 0 {
191             return Disp::None;
192         }
193         match evex_scaling {
194             Some(scaling) => {
195                 if val % i32::from(scaling) == 0 {
196                     let scaled = val / i32::from(scaling);
197                     if low8_will_sign_extend_to_32(scaled) {
198                         return Disp::Imm8(scaled as i8);
199                     }
200                 }
201                 Disp::Imm32(val)
202             }
203             None => match i8::try_from(val) {
204                 Ok(val) => Disp::Imm8(val),
205                 Err(_) => Disp::Imm32(val),
206             },
207         }
208     }
209 
210     /// Forces `Imm::None` to become `Imm::Imm8(0)`, used for special cases
211     /// where some base registers require an immediate.
212     pub fn force_immediate(&mut self) {
213         if let Disp::None = self {
214             *self = Disp::Imm8(0);
215         }
216     }
217 
218     /// Returns the two "mod" bits present at the upper bits of the mod/rm
219     /// byte.
220     pub fn m0d(self) -> u8 {
221         match self {
222             Disp::None => 0b00,
223             Disp::Imm8(_) => 0b01,
224             Disp::Imm32(_) => 0b10,
225         }
226     }
227 
228     /// Emit the truncated immediate into the code sink.
229     pub fn emit(self, sink: &mut impl CodeSink) {
230         match self {
231             Disp::None => {}
232             Disp::Imm8(n) => sink.put1(n as u8),
233             Disp::Imm32(n) => sink.put4(n as u32),
234         }
235     }
236 }
237