1use alloc::string::{String, ToString};
4
5use crate::api::{AsReg, CodeSink, Constant, KnownOffset, Label, TrapCode};
6use crate::gpr::{self, NonRspGpr, Size};
7use crate::rex::{Disp, RexPrefix, encode_modrm, encode_sib};
8
9#[derive(Copy, Clone, Debug, PartialEq)]
11#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
12pub enum Amode<R: AsReg> {
13 ImmReg {
14 base: R,
15 simm32: AmodeOffsetPlusKnownOffset,
16 trap: Option<TrapCode>,
17 },
18 ImmRegRegShift {
19 base: R,
20 index: NonRspGpr<R>,
21 scale: Scale,
22 simm32: AmodeOffset,
23 trap: Option<TrapCode>,
24 },
25 RipRelative {
26 target: DeferredTarget,
27 },
28}
29
30impl<R: AsReg> Amode<R> {
31 pub fn trap_code(&self) -> Option<TrapCode> {
33 match self {
34 Amode::ImmReg { trap, .. } | Amode::ImmRegRegShift { trap, .. } => *trap,
35 Amode::RipRelative { .. } => None,
36 }
37 }
38
39 #[must_use]
41 pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
42 match self {
43 Amode::ImmReg { base, .. } => {
44 RexPrefix::mem_op(enc_reg, base.enc(), has_w_bit, uses_8bit)
45 }
46 Amode::ImmRegRegShift { base, index, .. } => {
47 RexPrefix::three_op(enc_reg, index.enc(), base.enc(), has_w_bit, uses_8bit)
48 }
49 Amode::RipRelative { .. } => RexPrefix::two_op(enc_reg, 0, has_w_bit, uses_8bit),
50 }
51 }
52
53 pub(crate) fn encode_rex_suffixes(
56 &self,
57 sink: &mut impl CodeSink,
58 enc_reg: u8,
59 bytes_at_end: u8,
60 evex_scaling: Option<i8>,
61 ) {
62 emit_modrm_sib_disp(sink, enc_reg, self, bytes_at_end, evex_scaling);
63 }
64
65 pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
72 match self {
73 Amode::ImmReg { base, .. } => (Some(base.enc()), None),
74 Amode::ImmRegRegShift { base, index, .. } => (Some(base.enc()), Some(index.enc())),
75 Amode::RipRelative { .. } => (None, None),
76 }
77 }
78}
79
80#[derive(Clone, Copy, Debug, PartialEq)]
82pub struct AmodeOffset(i32);
83
84impl AmodeOffset {
85 pub const ZERO: AmodeOffset = AmodeOffset::new(0);
86
87 #[must_use]
88 pub const fn new(value: i32) -> Self {
89 Self(value)
90 }
91
92 #[must_use]
93 pub fn value(self) -> i32 {
94 self.0
95 }
96}
97
98impl From<i32> for AmodeOffset {
99 fn from(value: i32) -> Self {
100 Self(value)
101 }
102}
103
104impl core::fmt::LowerHex for AmodeOffset {
105 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
106 if self.0 == 0 {
110 return Ok(());
111 }
112 if self.0 < 0 {
113 write!(f, "-")?;
114 }
115 if f.alternate() || self.0 > 9 || self.0 < -9 {
116 write!(f, "0x")?;
117 }
118 let abs = match self.0.checked_abs() {
119 Some(i) => i,
120 None => -2_147_483_648,
121 };
122 write!(f, "{abs:x}")
125 }
126}
127
128#[derive(Copy, Clone, Debug, PartialEq)]
136pub struct AmodeOffsetPlusKnownOffset {
137 pub simm32: AmodeOffset,
138 pub offset: Option<KnownOffset>,
139}
140
141impl AmodeOffsetPlusKnownOffset {
142 pub const ZERO: AmodeOffsetPlusKnownOffset = AmodeOffsetPlusKnownOffset {
143 simm32: AmodeOffset::ZERO,
144 offset: None,
145 };
146
147 #[must_use]
151 pub fn value(&self, sink: &impl CodeSink) -> i32 {
152 let known_offset = match self.offset {
153 Some(offset) => sink.known_offset(offset),
154 None => 0,
155 };
156 known_offset
157 .checked_add(self.simm32.value())
158 .expect("no wrapping")
159 }
160}
161
162impl core::fmt::LowerHex for AmodeOffsetPlusKnownOffset {
163 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
164 if let Some(offset) = self.offset {
165 write!(f, "<offset:{offset}>+")?;
166 }
167 core::fmt::LowerHex::fmt(&self.simm32, f)
168 }
169}
170
171#[derive(Copy, Clone, Debug, PartialEq)]
173#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
174pub enum DeferredTarget {
175 Label(Label),
176 Constant(Constant),
177 None,
178}
179
180impl<R: AsReg> core::fmt::Display for Amode<R> {
181 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182 let pointer_width = Size::Quadword;
183 let xed = f.alternate();
185 match self {
186 Amode::ImmReg { simm32, base, .. } => {
187 let base = base.to_string(Some(pointer_width));
190 if xed {
191 write!(f, "{simm32:#x}({base})")
192 } else {
193 write!(f, "{simm32:x}({base})")
194 }
195 }
196 Amode::ImmRegRegShift {
197 simm32,
198 base,
199 index,
200 scale,
201 ..
202 } => {
203 let base = base.to_string(Some(pointer_width));
204 let index = index.to_string(pointer_width);
205 let shift = scale.shift();
206 if xed {
207 write!(f, "{simm32:#x}({base},{index},{shift})")
208 } else if shift > 1 {
209 write!(f, "{simm32:x}({base}, {index}, {shift})")
210 } else {
211 write!(f, "{simm32:x}({base}, {index})")
212 }
213 }
214 Amode::RipRelative { .. } => write!(f, "(%rip)"),
215 }
216 }
217}
218
219#[derive(Copy, Clone, Debug, PartialEq)]
221#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
222pub enum Scale {
223 One,
224 Two,
225 Four,
226 Eight,
227}
228
229impl Scale {
230 #[must_use]
236 pub fn new(enc: u8) -> Self {
237 match enc {
238 0b00 => Scale::One,
239 0b01 => Scale::Two,
240 0b10 => Scale::Four,
241 0b11 => Scale::Eight,
242 _ => panic!("invalid scale encoding: {enc}"),
243 }
244 }
245
246 fn enc(&self) -> u8 {
248 match self {
249 Scale::One => 0b00,
250 Scale::Two => 0b01,
251 Scale::Four => 0b10,
252 Scale::Eight => 0b11,
253 }
254 }
255
256 fn shift(&self) -> u8 {
262 1 << self.enc()
263 }
264}
265
266#[derive(Copy, Clone, Debug, PartialEq)]
268#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
269#[allow(
270 clippy::module_name_repetitions,
271 reason = "'GprMem' indicates this has GPR and memory variants"
272)]
273pub enum GprMem<R: AsReg, M: AsReg> {
274 Gpr(R),
275 Mem(Amode<M>),
276}
277
278impl<R: AsReg, M: AsReg> GprMem<R, M> {
279 #[must_use]
281 pub fn is_memory(&self) -> bool {
282 matches!(self, GprMem::Mem(_))
283 }
284
285 pub fn to_string(&self, size: Size) -> String {
287 match self {
288 GprMem::Gpr(gpr) => gpr.to_string(Some(size)),
289 GprMem::Mem(amode) => amode.to_string(),
290 }
291 }
292
293 pub fn to_string_xed(&self, size: Size) -> String {
295 match self {
296 GprMem::Gpr(gpr) => gpr.to_string(Some(size)),
297 GprMem::Mem(amode) => alloc::format!("{amode:#}"),
298 }
299 }
300
301 #[must_use]
303 pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
304 match self {
305 GprMem::Gpr(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
306 GprMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
307 }
308 }
309
310 pub(crate) fn encode_rex_suffixes(
312 &self,
313 sink: &mut impl CodeSink,
314 enc_reg: u8,
315 bytes_at_end: u8,
316 evex_scaling: Option<i8>,
317 ) {
318 match self {
319 GprMem::Gpr(gpr) => {
320 sink.put1(encode_modrm(0b11, enc_reg & 0b111, gpr.enc() & 0b111));
321 }
322 GprMem::Mem(amode) => {
323 amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
324 }
325 }
326 }
327
328 pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
330 match self {
331 GprMem::Gpr(reg) => (Some(reg.enc()), None),
332 GprMem::Mem(amode) => amode.encode_bx_regs(),
333 }
334 }
335}
336
337impl<R: AsReg, M: AsReg> From<R> for GprMem<R, M> {
338 fn from(reg: R) -> GprMem<R, M> {
339 GprMem::Gpr(reg)
340 }
341}
342
343impl<R: AsReg, M: AsReg> From<Amode<M>> for GprMem<R, M> {
344 fn from(amode: Amode<M>) -> GprMem<R, M> {
345 GprMem::Mem(amode)
346 }
347}
348
349#[derive(Copy, Clone, Debug)]
351#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
352#[allow(
353 clippy::module_name_repetitions,
354 reason = "'XmmMem' indicates this has Xmm and memory variants"
355)]
356pub enum XmmMem<R: AsReg, M: AsReg> {
357 Xmm(R),
358 Mem(Amode<M>),
359}
360
361impl<R: AsReg, M: AsReg> XmmMem<R, M> {
362 #[must_use]
364 pub fn is_memory(&self) -> bool {
365 matches!(self, XmmMem::Mem(_))
366 }
367
368 pub fn to_string(&self) -> String {
370 match self {
371 XmmMem::Xmm(xmm) => xmm.to_string(None),
372 XmmMem::Mem(amode) => amode.to_string(),
373 }
374 }
375
376 pub fn to_string_xed(&self) -> String {
378 match self {
379 XmmMem::Xmm(xmm) => xmm.to_string(None),
380 XmmMem::Mem(amode) => alloc::format!("{amode:#}"),
381 }
382 }
383
384 #[must_use]
386 pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix {
387 match self {
388 XmmMem::Xmm(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit),
389 XmmMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit),
390 }
391 }
392
393 pub(crate) fn encode_rex_suffixes(
395 &self,
396 sink: &mut impl CodeSink,
397 enc_reg: u8,
398 bytes_at_end: u8,
399 evex_scaling: Option<i8>,
400 ) {
401 match self {
402 XmmMem::Xmm(xmm) => {
403 sink.put1(encode_modrm(0b11, enc_reg & 0b111, xmm.enc() & 0b111));
404 }
405 XmmMem::Mem(amode) => {
406 amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling);
407 }
408 }
409 }
410
411 pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) {
418 match self {
419 XmmMem::Xmm(reg) => (Some(reg.enc()), None),
420 XmmMem::Mem(amode) => amode.encode_bx_regs(),
421 }
422 }
423}
424
425impl<R: AsReg, M: AsReg> From<R> for XmmMem<R, M> {
426 fn from(reg: R) -> XmmMem<R, M> {
427 XmmMem::Xmm(reg)
428 }
429}
430
431impl<R: AsReg, M: AsReg> From<Amode<M>> for XmmMem<R, M> {
432 fn from(amode: Amode<M>) -> XmmMem<R, M> {
433 XmmMem::Mem(amode)
434 }
435}
436
437pub fn emit_modrm_sib_disp<R: AsReg>(
439 sink: &mut impl CodeSink,
440 enc_g: u8,
441 mem_e: &Amode<R>,
442 bytes_at_end: u8,
443 evex_scaling: Option<i8>,
444) {
445 match *mem_e {
446 Amode::ImmReg { simm32, base, .. } => {
447 let enc_e = base.enc();
448 let mut imm = Disp::new(simm32.value(sink), evex_scaling);
449
450 let enc_e_low3 = enc_e & 7;
454 if enc_e_low3 == gpr::enc::RSP {
455 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
461 sink.put1(0b00_100_100);
462 imm.emit(sink);
463 } else {
464 if enc_e_low3 == gpr::enc::RBP {
468 imm.force_immediate();
469 }
470 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7));
471 imm.emit(sink);
472 }
473 }
474
475 Amode::ImmRegRegShift {
476 simm32,
477 base,
478 index,
479 scale,
480 ..
481 } => {
482 let enc_base = base.enc();
483 let enc_index = index.enc();
484
485 assert!(enc_index != gpr::enc::RSP);
490
491 let mut imm = Disp::new(simm32.value(), evex_scaling);
496 if enc_base & 7 == gpr::enc::RBP {
497 imm.force_immediate();
498 }
499
500 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
503 sink.put1(encode_sib(scale.enc(), enc_index & 7, enc_base & 7));
504 imm.emit(sink);
505 }
506
507 Amode::RipRelative { target } => {
508 sink.put1(encode_modrm(0b00, enc_g & 7, 0b101));
510
511 sink.use_target(target);
515
516 sink.put4(-(i32::from(bytes_at_end)) as u32);
524 }
525 }
526}