cranelift_codegen/isa/x64/encoding/rex.rs
1//! Encodes instructions in the standard x86 encoding mode. This is called
2//! IA-32E mode in the Intel manuals but corresponds to the addition of the
3//! REX-prefix format (hence the name of this module) that allowed encoding
4//! instructions in both compatibility mode (32-bit instructions running on a
5//! 64-bit OS) and in 64-bit mode (using the full 64-bit address space).
6//!
7//! For all of the routines that take both a memory-or-reg operand (sometimes
8//! called "E" in the Intel documentation, see the Intel Developer's manual,
9//! vol. 2, section A.2) and a reg-only operand ("G" in Intel-ese), the order is
10//! always G first, then E. The term "enc" in the following means "hardware
11//! register encoding number".
12
13use super::ByteSink;
14use crate::isa::x64::inst::args::Amode;
15use crate::isa::x64::inst::{Inst, LabelUse, regs};
16use crate::machinst::{MachBuffer, Reg, RegClass};
17
18pub(crate) fn low8_will_sign_extend_to_32(x: u32) -> bool {
19 let xs = x as i32;
20 xs == ((xs << 24) >> 24)
21}
22
23/// Encode the ModR/M byte.
24#[inline(always)]
25pub fn encode_modrm(m0d: u8, enc_reg_g: u8, rm_e: u8) -> u8 {
26 debug_assert!(m0d < 4);
27 debug_assert!(enc_reg_g < 8);
28 debug_assert!(rm_e < 8);
29 ((m0d & 3) << 6) | ((enc_reg_g & 7) << 3) | (rm_e & 7)
30}
31
32#[inline(always)]
33pub(crate) fn encode_sib(shift: u8, enc_index: u8, enc_base: u8) -> u8 {
34 debug_assert!(shift < 4);
35 debug_assert!(enc_index < 8);
36 debug_assert!(enc_base < 8);
37 ((shift & 3) << 6) | ((enc_index & 7) << 3) | (enc_base & 7)
38}
39
40/// Get the encoding number of a GPR.
41#[inline(always)]
42pub(crate) fn int_reg_enc(reg: impl Into<Reg>) -> u8 {
43 let reg = reg.into();
44 debug_assert!(reg.is_real(), "reg = {reg:?}");
45 debug_assert_eq!(reg.class(), RegClass::Int);
46 reg.to_real_reg().unwrap().hw_enc()
47}
48
49/// Allows using the same opcode byte in different "opcode maps" to allow for more instruction
50/// encodings. See appendix A in the Intel Software Developer's Manual, volume 2A, for more details.
51#[allow(missing_docs)]
52#[derive(PartialEq)]
53pub enum OpcodeMap {
54 None,
55 _0F,
56 _0F38,
57 _0F3A,
58}
59
60impl OpcodeMap {
61 /// Normally the opcode map is specified as bytes in the instruction, but some x64 encoding
62 /// formats pack this information as bits in a prefix (e.g. VEX / EVEX).
63 pub(crate) fn bits(&self) -> u8 {
64 match self {
65 OpcodeMap::None => 0b00,
66 OpcodeMap::_0F => 0b01,
67 OpcodeMap::_0F38 => 0b10,
68 OpcodeMap::_0F3A => 0b11,
69 }
70 }
71}
72
73impl Default for OpcodeMap {
74 fn default() -> Self {
75 Self::None
76 }
77}
78
79/// We may need to include one or more legacy prefix bytes before the REX prefix. This enum
80/// covers only the small set of possibilities that we actually need.
81#[derive(PartialEq)]
82pub enum LegacyPrefixes {
83 /// No prefix bytes.
84 None,
85 /// Operand Size Override -- here, denoting "16-bit operation".
86 _66,
87 /// The Lock prefix.
88 _F0,
89 /// Operand size override and Lock.
90 _66F0,
91 /// REPNE, but no specific meaning here -- is just an opcode extension.
92 _F2,
93 /// REP/REPE, but no specific meaning here -- is just an opcode extension.
94 _F3,
95 /// Operand size override and same effect as F3.
96 _66F3,
97}
98
99impl LegacyPrefixes {
100 /// Emit the legacy prefix as bits (e.g. for EVEX instructions).
101 #[inline(always)]
102 pub(crate) fn bits(&self) -> u8 {
103 match self {
104 Self::None => 0b00,
105 Self::_66 => 0b01,
106 Self::_F3 => 0b10,
107 Self::_F2 => 0b11,
108 _ => panic!(
109 "VEX and EVEX bits can only be extracted from single prefixes: None, 66, F3, F2"
110 ),
111 }
112 }
113}
114
115impl Default for LegacyPrefixes {
116 fn default() -> Self {
117 Self::None
118 }
119}
120
121pub(crate) fn emit_modrm_sib_disp(
122 sink: &mut MachBuffer<Inst>,
123 enc_g: u8,
124 mem_e: &Amode,
125 bytes_at_end: u8,
126 evex_scaling: Option<i8>,
127) {
128 match *mem_e {
129 Amode::ImmReg { simm32, base, .. } => {
130 let enc_e = int_reg_enc(base);
131 let mut imm = Imm::new(simm32, evex_scaling);
132
133 // Most base registers allow for a single ModRM byte plus an
134 // optional immediate. If rsp is the base register, however, then a
135 // SIB byte must be used.
136 let enc_e_low3 = enc_e & 7;
137 if enc_e_low3 != regs::ENC_RSP {
138 // If the base register is rbp and there's no offset then force
139 // a 1-byte zero offset since otherwise the encoding would be
140 // invalid.
141 if enc_e_low3 == regs::ENC_RBP {
142 imm.force_immediate();
143 }
144 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7));
145 imm.emit(sink);
146 } else {
147 // Displacement from RSP is encoded with a SIB byte where
148 // the index and base are both encoded as RSP's encoding of
149 // 0b100. This special encoding means that the index register
150 // isn't used and the base is 0b100 with or without a
151 // REX-encoded 4th bit (e.g. rsp or r12)
152 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
153 sink.put1(0b00_100_100);
154 imm.emit(sink);
155 }
156 }
157
158 Amode::ImmRegRegShift {
159 simm32,
160 base: reg_base,
161 index: reg_index,
162 shift,
163 ..
164 } => {
165 let enc_base = int_reg_enc(*reg_base);
166 let enc_index = int_reg_enc(*reg_index);
167
168 // Encoding of ModRM/SIB bytes don't allow the index register to
169 // ever be rsp. Note, though, that the encoding of r12, whose three
170 // lower bits match the encoding of rsp, is explicitly allowed with
171 // REX bytes so only rsp is disallowed.
172 assert!(enc_index != regs::ENC_RSP);
173
174 // If the offset is zero then there is no immediate. Note, though,
175 // that if the base register's lower three bits are `101` then an
176 // offset must be present. This is a special case in the encoding of
177 // the SIB byte and requires an explicit displacement with rbp/r13.
178 let mut imm = Imm::new(simm32, evex_scaling);
179 if enc_base & 7 == regs::ENC_RBP {
180 imm.force_immediate();
181 }
182
183 // With the above determined encode the ModRM byte, then the SIB
184 // byte, then any immediate as necessary.
185 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
186 sink.put1(encode_sib(shift, enc_index & 7, enc_base & 7));
187 imm.emit(sink);
188 }
189
190 Amode::RipRelative { ref target } => {
191 // RIP-relative is mod=00, rm=101.
192 sink.put1(encode_modrm(0b00, enc_g & 7, 0b101));
193
194 let offset = sink.cur_offset();
195 sink.use_label_at_offset(offset, *target, LabelUse::JmpRel32);
196 // N.B.: some instructions (XmmRmRImm format for example)
197 // have bytes *after* the RIP-relative offset. The
198 // addressed location is relative to the end of the
199 // instruction, but the relocation is nominally relative
200 // to the end of the u32 field. So, to compensate for
201 // this, we emit a negative extra offset in the u32 field
202 // initially, and the relocation will add to it.
203 sink.put4(-(i32::from(bytes_at_end)) as u32);
204 }
205 }
206}
207
208#[derive(Copy, Clone)]
209enum Imm {
210 None,
211 Imm8(i8),
212 Imm32(i32),
213}
214
215impl Imm {
216 /// Classifies the 32-bit immediate `val` as how this can be encoded
217 /// with ModRM/SIB bytes.
218 ///
219 /// For `evex_scaling` according to Section 2.7.5 of Intel's manual:
220 ///
221 /// > EVEX-encoded instructions always use a compressed displacement scheme
222 /// > by multiplying disp8 in conjunction with a scaling factor N that is
223 /// > determined based on the vector length, the value of EVEX.b bit
224 /// > (embedded broadcast) and the input element size of the instruction
225 ///
226 /// The `evex_scaling` factor provided here is `Some(N)` for EVEX
227 /// instructions. This is taken into account where the `Imm` value
228 /// contained is the raw byte offset.
229 fn new(val: i32, evex_scaling: Option<i8>) -> Imm {
230 if val == 0 {
231 return Imm::None;
232 }
233 match evex_scaling {
234 Some(scaling) => {
235 if val % i32::from(scaling) == 0 {
236 let scaled = val / i32::from(scaling);
237 if low8_will_sign_extend_to_32(scaled as u32) {
238 return Imm::Imm8(scaled as i8);
239 }
240 }
241 Imm::Imm32(val)
242 }
243 None => match i8::try_from(val) {
244 Ok(val) => Imm::Imm8(val),
245 Err(_) => Imm::Imm32(val),
246 },
247 }
248 }
249
250 /// Forces `Imm::None` to become `Imm::Imm8(0)`, used for special cases
251 /// where some base registers require an immediate.
252 fn force_immediate(&mut self) {
253 if let Imm::None = self {
254 *self = Imm::Imm8(0);
255 }
256 }
257
258 /// Returns the two "mod" bits present at the upper bits of the mod/rm
259 /// byte.
260 fn m0d(&self) -> u8 {
261 match self {
262 Imm::None => 0b00,
263 Imm::Imm8(_) => 0b01,
264 Imm::Imm32(_) => 0b10,
265 }
266 }
267
268 fn emit<BS: ByteSink + ?Sized>(&self, sink: &mut BS) {
269 match self {
270 Imm::None => {}
271 Imm::Imm8(n) => sink.put1(*n as u8),
272 Imm::Imm32(n) => sink.put4(*n as u32),
273 }
274 }
275}