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