1 //! Memory operands to instructions.
2 
3 use crate::api::{AsReg, CodeSink, Constant, KnownOffset, Label, TrapCode};
4 use crate::gpr::{self, NonRspGpr, Size};
5 use crate::rex::{Disp, RexPrefix, encode_modrm, encode_sib};
6 
7 /// x64 memory addressing modes.
8 #[derive(Copy, Clone, Debug, PartialEq)]
9 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
10 pub enum Amode<R: AsReg> {
11     ImmReg {
12         base: R,
13         simm32: AmodeOffsetPlusKnownOffset,
14         trap: Option<TrapCode>,
15     },
16     ImmRegRegShift {
17         base: R,
18         index: NonRspGpr<R>,
19         scale: Scale,
20         simm32: AmodeOffset,
21         trap: Option<TrapCode>,
22     },
23     RipRelative {
24         target: DeferredTarget,
25     },
26 }
27 
28 impl<R: AsReg> Amode<R> {
29     /// Return the [`TrapCode`] associated with this [`Amode`], if any.
30     pub fn trap_code(&self) -> Option<TrapCode> {
31         match self {
32             Amode::ImmReg { trap, .. } | Amode::ImmRegRegShift { trap, .. } => *trap,
33             Amode::RipRelative { .. } => None,
34         }
35     }
36 
37     /// Return the [`RexPrefix`] for each variant of this [`Amode`].
38     #[must_use]
39     pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
40         match self {
41             Amode::ImmReg { base, .. } => {
42                 RexPrefix::mem_op(enc_reg, base.enc(), has_w_bit, uses_8bit)
43             }
44             Amode::ImmRegRegShift { base, index, .. } => {
45                 RexPrefix::three_op(enc_reg, index.enc(), base.enc(), has_w_bit, uses_8bit)
46             }
47             Amode::RipRelative { .. } => RexPrefix::two_op(enc_reg, 0, has_w_bit, uses_8bit),
48         }
49     }
50 
51     /// Emit the ModR/M, SIB, and displacement suffixes as needed for this
52     /// `Amode`.
53     pub(crate) fn encode_rex_suffixes(
54         &self,
55         sink: &mut impl CodeSink,
56         enc_reg: u8,
57         bytes_at_end: u8,
58         evex_scaling: Option<i8>,
59     ) {
60         emit_modrm_sib_disp(sink, enc_reg, self, bytes_at_end, evex_scaling);
61     }
62 
63     /// Return the registers for encoding the `b` and `x` bits (e.g., in a VEX
64     /// prefix).
65     ///
66     /// During encoding, the `b` bit is set by the topmost bit (the fourth bit)
67     /// of either the `reg` register or, if this is a memory address, the `base`
68     /// register. The `x` bit is set by the `index` register, when used.
69     pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
70         match self {
71             Amode::ImmReg { base, .. } => (Some(base.enc()), None),
72             Amode::ImmRegRegShift { base, index, .. } => (Some(base.enc()), Some(index.enc())),
73             Amode::RipRelative { .. } => (None, None),
74         }
75     }
76 }
77 
78 /// A 32-bit immediate for address offsets.
79 #[derive(Clone, Copy, Debug, PartialEq)]
80 pub struct AmodeOffset(i32);
81 
82 impl AmodeOffset {
83     pub const ZERO: AmodeOffset = AmodeOffset::new(0);
84 
85     #[must_use]
86     pub const fn new(value: i32) -> Self {
87         Self(value)
88     }
89 
90     #[must_use]
91     pub fn value(self) -> i32 {
92         self.0
93     }
94 }
95 
96 impl From<i32> for AmodeOffset {
97     fn from(value: i32) -> Self {
98         Self(value)
99     }
100 }
101 
102 impl std::fmt::LowerHex for AmodeOffset {
103     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
104         // This rather complex implementation is necessary to match how
105         // `capstone` pretty-prints memory immediates.
106         if self.0 == 0 {
107             return Ok(());
108         }
109         if self.0 < 0 {
110             write!(f, "-")?;
111         }
112         if self.0 > 9 || self.0 < -9 {
113             write!(f, "0x")?;
114         }
115         let abs = match self.0.checked_abs() {
116             Some(i) => i,
117             None => -2_147_483_648,
118         };
119         std::fmt::LowerHex::fmt(&abs, f)
120     }
121 }
122 
123 /// An [`AmodeOffset`] immediate with an optional known offset.
124 ///
125 /// Cranelift does not know certain offsets until emission time. To accommodate
126 /// Cranelift, this structure stores an optional [`KnownOffset`]. The following
127 /// happens immediately before emission:
128 /// - the [`KnownOffset`] is looked up, mapping it to an offset value
129 /// - the [`Simm32`] value is added to the offset value
130 #[derive(Copy, Clone, Debug, PartialEq)]
131 pub struct AmodeOffsetPlusKnownOffset {
132     pub simm32: AmodeOffset,
133     pub offset: Option<KnownOffset>,
134 }
135 
136 impl AmodeOffsetPlusKnownOffset {
137     pub const ZERO: AmodeOffsetPlusKnownOffset = AmodeOffsetPlusKnownOffset {
138         simm32: AmodeOffset::ZERO,
139         offset: None,
140     };
141 
142     /// # Panics
143     ///
144     /// Panics if the sum of the immediate and the known offset value overflows.
145     #[must_use]
146     pub fn value(&self, sink: &impl CodeSink) -> i32 {
147         let known_offset = match self.offset {
148             Some(offset) => sink.known_offset(offset),
149             None => 0,
150         };
151         known_offset
152             .checked_add(self.simm32.value())
153             .expect("no wrapping")
154     }
155 }
156 
157 impl std::fmt::LowerHex for AmodeOffsetPlusKnownOffset {
158     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
159         if let Some(offset) = self.offset {
160             write!(f, "<offset:{offset}>+")?;
161         }
162         std::fmt::LowerHex::fmt(&self.simm32, f)
163     }
164 }
165 
166 /// For RIP-relative addressing, keep track of the [`CodeSink`]-specific target.
167 #[derive(Copy, Clone, Debug, PartialEq)]
168 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
169 pub enum DeferredTarget {
170     Label(Label),
171     Constant(Constant),
172     None,
173 }
174 
175 impl<R: AsReg> std::fmt::Display for Amode<R> {
176     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177         let pointer_width = Size::Quadword;
178         match self {
179             Amode::ImmReg { simm32, base, .. } => {
180                 // Note: size is always 8; the address is 64 bits,
181                 // even if the addressed operand is smaller.
182                 let base = base.to_string(Some(pointer_width));
183                 write!(f, "{simm32:x}({base})")
184             }
185             Amode::ImmRegRegShift {
186                 simm32,
187                 base,
188                 index,
189                 scale,
190                 ..
191             } => {
192                 let base = base.to_string(Some(pointer_width));
193                 let index = index.to_string(pointer_width);
194                 let shift = scale.shift();
195                 if shift > 1 {
196                     write!(f, "{simm32:x}({base}, {index}, {shift})")
197                 } else {
198                     write!(f, "{simm32:x}({base}, {index})")
199                 }
200             }
201             Amode::RipRelative { .. } => write!(f, "(%rip)"),
202         }
203     }
204 }
205 
206 /// The scaling factor for the index register in certain [`Amode`]s.
207 #[derive(Copy, Clone, Debug, PartialEq)]
208 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
209 pub enum Scale {
210     One,
211     Two,
212     Four,
213     Eight,
214 }
215 
216 impl Scale {
217     /// Create a new [`Scale`] from its hardware encoding.
218     ///
219     /// # Panics
220     ///
221     /// Panics if `enc` is not a valid encoding for a scale (0-3).
222     #[must_use]
223     pub fn new(enc: u8) -> Self {
224         match enc {
225             0b00 => Scale::One,
226             0b01 => Scale::Two,
227             0b10 => Scale::Four,
228             0b11 => Scale::Eight,
229             _ => panic!("invalid scale encoding: {enc}"),
230         }
231     }
232 
233     /// Return the hardware encoding of this [`Scale`].
234     fn enc(&self) -> u8 {
235         match self {
236             Scale::One => 0b00,
237             Scale::Two => 0b01,
238             Scale::Four => 0b10,
239             Scale::Eight => 0b11,
240         }
241     }
242 
243     /// Return how much this [`Scale`] will shift the value in the index
244     /// register of the SIB byte.
245     ///
246     /// This is useful for pretty-printing; when encoding, one usually needs
247     /// [`Scale::enc`].
248     fn shift(&self) -> u8 {
249         1 << self.enc()
250     }
251 }
252 
253 /// A general-purpose register or memory operand.
254 #[derive(Copy, Clone, Debug, PartialEq)]
255 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
256 #[allow(
257     clippy::module_name_repetitions,
258     reason = "'GprMem' indicates this has GPR and memory variants"
259 )]
260 pub enum GprMem<R: AsReg, M: AsReg> {
261     Gpr(R),
262     Mem(Amode<M>),
263 }
264 
265 impl<R: AsReg, M: AsReg> GprMem<R, M> {
266     /// Pretty-print the operand.
267     pub fn to_string(&self, size: Size) -> String {
268         match self {
269             GprMem::Gpr(gpr) => gpr.to_string(Some(size)),
270             GprMem::Mem(amode) => amode.to_string(),
271         }
272     }
273 
274     /// Return the [`RexPrefix`] for each variant of this [`GprMem`].
275     #[must_use]
276     pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
277         match self {
278             GprMem::Gpr(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
279             GprMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
280         }
281     }
282 
283     /// Emit the ModR/M, SIB, and displacement suffixes for this [`GprMem`].
284     pub(crate) fn encode_rex_suffixes(
285         &self,
286         sink: &mut impl CodeSink,
287         enc_reg: u8,
288         bytes_at_end: u8,
289         evex_scaling: Option<i8>,
290     ) {
291         match self {
292             GprMem::Gpr(gpr) => {
293                 sink.put1(encode_modrm(0b11, enc_reg & 0b111, gpr.enc() & 0b111));
294             }
295             GprMem::Mem(amode) => {
296                 amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
297             }
298         }
299     }
300 
301     /// Same as `XmmMem::encode_bx_regs`, but for `GprMem`.
302     pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
303         match self {
304             GprMem::Gpr(reg) => (Some(reg.enc()), None),
305             GprMem::Mem(amode) => amode.encode_bx_regs(),
306         }
307     }
308 }
309 
310 impl<R: AsReg, M: AsReg> From<R> for GprMem<R, M> {
311     fn from(reg: R) -> GprMem<R, M> {
312         GprMem::Gpr(reg)
313     }
314 }
315 
316 impl<R: AsReg, M: AsReg> From<Amode<M>> for GprMem<R, M> {
317     fn from(amode: Amode<M>) -> GprMem<R, M> {
318         GprMem::Mem(amode)
319     }
320 }
321 
322 /// An XMM register or memory operand.
323 #[derive(Copy, Clone, Debug)]
324 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
325 #[allow(
326     clippy::module_name_repetitions,
327     reason = "'XmmMem' indicates this has Xmm and memory variants"
328 )]
329 pub enum XmmMem<R: AsReg, M: AsReg> {
330     Xmm(R),
331     Mem(Amode<M>),
332 }
333 
334 impl<R: AsReg, M: AsReg> XmmMem<R, M> {
335     /// Pretty-print the operand.
336     pub fn to_string(&self) -> String {
337         match self {
338             XmmMem::Xmm(xmm) => xmm.to_string(None),
339             XmmMem::Mem(amode) => amode.to_string(),
340         }
341     }
342 
343     /// Return the [`RexPrefix`] for each variant of this [`XmmMem`].
344     #[must_use]
345     pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
346         match self {
347             XmmMem::Xmm(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
348             XmmMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
349         }
350     }
351 
352     /// Emit the ModR/M, SIB, and displacement suffixes for this [`XmmMem`].
353     pub(crate) fn encode_rex_suffixes(
354         &self,
355         sink: &mut impl CodeSink,
356         enc_reg: u8,
357         bytes_at_end: u8,
358         evex_scaling: Option<i8>,
359     ) {
360         match self {
361             XmmMem::Xmm(xmm) => {
362                 sink.put1(encode_modrm(0b11, enc_reg & 0b111, xmm.enc() & 0b111));
363             }
364             XmmMem::Mem(amode) => {
365                 amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
366             }
367         }
368     }
369 
370     /// Return the registers for encoding the `b` and `x` bits (e.g., in a VEX
371     /// prefix).
372     ///
373     /// During encoding, the `b` bit is set by the topmost bit (the fourth bit)
374     /// of either the `reg` register or, if this is a memory address, the `base`
375     /// register. The `x` bit is set by the `index` register, when used.
376     pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
377         match self {
378             XmmMem::Xmm(reg) => (Some(reg.enc()), None),
379             XmmMem::Mem(amode) => amode.encode_bx_regs(),
380         }
381     }
382 }
383 
384 impl<R: AsReg, M: AsReg> From<R> for XmmMem<R, M> {
385     fn from(reg: R) -> XmmMem<R, M> {
386         XmmMem::Xmm(reg)
387     }
388 }
389 
390 impl<R: AsReg, M: AsReg> From<Amode<M>> for XmmMem<R, M> {
391     fn from(amode: Amode<M>) -> XmmMem<R, M> {
392         XmmMem::Mem(amode)
393     }
394 }
395 
396 /// Emit the ModRM/SIB/displacement sequence for a memory operand.
397 pub fn emit_modrm_sib_disp<R: AsReg>(
398     sink: &mut impl CodeSink,
399     enc_g: u8,
400     mem_e: &Amode<R>,
401     bytes_at_end: u8,
402     evex_scaling: Option<i8>,
403 ) {
404     match *mem_e {
405         Amode::ImmReg { simm32, base, .. } => {
406             let enc_e = base.enc();
407             let mut imm = Disp::new(simm32.value(sink), evex_scaling);
408 
409             // Most base registers allow for a single ModRM byte plus an
410             // optional immediate. If rsp is the base register, however, then a
411             // SIB byte must be used.
412             let enc_e_low3 = enc_e & 7;
413             if enc_e_low3 == gpr::enc::RSP {
414                 // Displacement from RSP is encoded with a SIB byte where
415                 // the index and base are both encoded as RSP's encoding of
416                 // 0b100. This special encoding means that the index register
417                 // isn't used and the base is 0b100 with or without a
418                 // REX-encoded 4th bit (e.g. rsp or r12)
419                 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
420                 sink.put1(0b00_100_100);
421                 imm.emit(sink);
422             } else {
423                 // If the base register is rbp and there's no offset then force
424                 // a 1-byte zero offset since otherwise the encoding would be
425                 // invalid.
426                 if enc_e_low3 == gpr::enc::RBP {
427                     imm.force_immediate();
428                 }
429                 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7));
430                 imm.emit(sink);
431             }
432         }
433 
434         Amode::ImmRegRegShift {
435             simm32,
436             base,
437             index,
438             scale,
439             ..
440         } => {
441             let enc_base = base.enc();
442             let enc_index = index.enc();
443 
444             // Encoding of ModRM/SIB bytes don't allow the index register to
445             // ever be rsp. Note, though, that the encoding of r12, whose three
446             // lower bits match the encoding of rsp, is explicitly allowed with
447             // REX bytes so only rsp is disallowed.
448             assert!(enc_index != gpr::enc::RSP);
449 
450             // If the offset is zero then there is no immediate. Note, though,
451             // that if the base register's lower three bits are `101` then an
452             // offset must be present. This is a special case in the encoding of
453             // the SIB byte and requires an explicit displacement with rbp/r13.
454             let mut imm = Disp::new(simm32.value(), evex_scaling);
455             if enc_base & 7 == gpr::enc::RBP {
456                 imm.force_immediate();
457             }
458 
459             // With the above determined encode the ModRM byte, then the SIB
460             // byte, then any immediate as necessary.
461             sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
462             sink.put1(encode_sib(scale.enc(), enc_index & 7, enc_base & 7));
463             imm.emit(sink);
464         }
465 
466         Amode::RipRelative { target } => {
467             // RIP-relative is mod=00, rm=101.
468             sink.put1(encode_modrm(0b00, enc_g & 7, 0b101));
469 
470             // Inform the code sink about the RIP-relative `target` at the
471             // current offset, emitting a `LabelUse`, a relocation, or etc as
472             // appropriate.
473             sink.use_target(target);
474 
475             // N.B.: some instructions (XmmRmRImm format for example)
476             // have bytes *after* the RIP-relative offset. The
477             // addressed location is relative to the end of the
478             // instruction, but the relocation is nominally relative
479             // to the end of the u32 field. So, to compensate for
480             // this, we emit a negative extra offset in the u32 field
481             // initially, and the relocation will add to it.
482             sink.put4(-(i32::from(bytes_at_end)) as u32);
483         }
484     }
485 }
486