1use crate::api::{AsReg, CodeSink, Constant, KnownOffset, KnownOffsetTable, Label, TrapCode};
4use crate::gpr::{self, NonRspGpr, Size};
5use crate::rex::{encode_modrm, encode_sib, Imm, RexFlags};
6use crate::{RegisterVisitor, Registers};
7
8#[derive(Clone, Debug)]
10#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
11pub enum Amode<R: AsReg> {
12 ImmReg {
13 base: R,
14 simm32: AmodeOffsetPlusKnownOffset,
15 trap: Option<TrapCode>,
16 },
17 ImmRegRegShift {
18 base: R,
19 index: NonRspGpr<R>,
20 scale: Scale,
21 simm32: AmodeOffset,
22 trap: Option<TrapCode>,
23 },
24 RipRelative {
25 target: DeferredTarget,
26 },
27}
28
29impl<R: AsReg> Amode<R> {
30 pub fn trap_code(&self) -> Option<TrapCode> {
32 match self {
33 Amode::ImmReg { trap, .. } | Amode::ImmRegRegShift { trap, .. } => *trap,
34 Amode::RipRelative { .. } => None,
35 }
36 }
37
38 pub fn emit_rex_prefix(&self, rex: RexFlags, enc_g: u8, sink: &mut impl CodeSink) {
40 match self {
41 Amode::ImmReg { base, .. } => {
42 let enc_e = base.enc();
43 rex.emit_two_op(sink, enc_g, enc_e);
44 }
45 Amode::ImmRegRegShift { base, index, .. } => {
46 let enc_base = base.enc();
47 let enc_index = index.enc();
48 rex.emit_three_op(sink, enc_g, enc_index, enc_base);
49 }
50 Amode::RipRelative { .. } => {
51 rex.emit_two_op(sink, enc_g, 0);
53 }
54 }
55 }
56}
57
58pub(crate) fn visit_amode<R: Registers>(
64 amode: &mut Amode<R::ReadGpr>,
65 visitor: &mut impl RegisterVisitor<R>,
66) {
67 match amode {
68 Amode::ImmReg { base, .. } => {
69 visitor.read_gpr(base);
70 }
71 Amode::ImmRegRegShift { base, index, .. } => {
72 visitor.read_gpr(base);
73 visitor.read_gpr(index.as_mut());
74 }
75 Amode::RipRelative { .. } => {}
76 }
77}
78
79#[derive(Clone, Copy, Debug)]
81#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
82pub struct AmodeOffset(i32);
83
84impl AmodeOffset {
85 #[must_use]
86 pub 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
96impl From<i32> for AmodeOffset {
97 fn from(value: i32) -> Self {
98 Self(value)
99 }
100}
101
102impl std::fmt::LowerHex for AmodeOffset {
103 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
104 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#[derive(Clone, Debug)]
131pub struct AmodeOffsetPlusKnownOffset {
132 pub simm32: AmodeOffset,
133 pub offset: Option<KnownOffset>,
134}
135
136impl AmodeOffsetPlusKnownOffset {
137 #[must_use]
141 pub fn value(&self, offsets: &impl KnownOffsetTable) -> i32 {
142 let known_offset = match self.offset {
143 Some(offset) => offsets[offset],
144 None => 0,
145 };
146 known_offset
147 .checked_add(self.simm32.value())
148 .expect("no wrapping")
149 }
150}
151
152impl std::fmt::LowerHex for AmodeOffsetPlusKnownOffset {
153 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
154 if let Some(offset) = self.offset {
155 write!(f, "<offset:{offset}>+")?;
156 }
157 std::fmt::LowerHex::fmt(&self.simm32, f)
158 }
159}
160
161#[derive(Clone, Debug)]
163#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
164pub enum DeferredTarget {
165 Label(Label),
166 Constant(Constant),
167}
168
169impl<R: AsReg> std::fmt::Display for Amode<R> {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 let pointer_width = Size::Quadword;
172 match self {
173 Amode::ImmReg { simm32, base, .. } => {
174 let base = base.to_string(Some(pointer_width));
177 write!(f, "{simm32:x}({base})")
178 }
179 Amode::ImmRegRegShift {
180 simm32,
181 base,
182 index,
183 scale,
184 ..
185 } => {
186 let base = base.to_string(Some(pointer_width));
187 let index = index.to_string(pointer_width);
188 let shift = scale.shift();
189 if shift > 1 {
190 write!(f, "{simm32:x}({base}, {index}, {shift})")
191 } else {
192 write!(f, "{simm32:x}({base}, {index})")
193 }
194 }
195 Amode::RipRelative { .. } => write!(f, "(%rip)"),
196 }
197 }
198}
199
200#[derive(Clone, Debug)]
202#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
203pub enum Scale {
204 One,
205 Two,
206 Four,
207 Eight,
208}
209
210impl Scale {
211 #[must_use]
217 pub fn new(enc: u8) -> Self {
218 match enc {
219 0b00 => Scale::One,
220 0b01 => Scale::Two,
221 0b10 => Scale::Four,
222 0b11 => Scale::Eight,
223 _ => panic!("invalid scale encoding: {enc}"),
224 }
225 }
226
227 fn enc(&self) -> u8 {
229 match self {
230 Scale::One => 0b00,
231 Scale::Two => 0b01,
232 Scale::Four => 0b10,
233 Scale::Eight => 0b11,
234 }
235 }
236
237 fn shift(&self) -> u8 {
243 1 << self.enc()
244 }
245}
246
247#[derive(Clone, Debug)]
249#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
250#[allow(
251 clippy::module_name_repetitions,
252 reason = "'GprMem' indicates this has GPR and memory variants"
253)]
254pub enum GprMem<R: AsReg, M: AsReg> {
255 Gpr(R),
256 Mem(Amode<M>),
257}
258
259impl<R: AsReg, M: AsReg> GprMem<R, M> {
260 pub fn to_string(&self, size: Size) -> String {
262 match self {
263 GprMem::Gpr(gpr) => gpr.to_string(Some(size)),
264 GprMem::Mem(amode) => amode.to_string(),
265 }
266 }
267
268 pub(crate) fn always_emit_if_8bit_needed(&self, rex: &mut RexFlags) {
271 match self {
272 GprMem::Gpr(gpr) => {
273 rex.always_emit_if_8bit_needed(gpr.enc());
274 }
275 GprMem::Mem(_) => {}
276 }
277 }
278}
279
280#[derive(Clone, Debug)]
282#[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))]
283#[allow(
284 clippy::module_name_repetitions,
285 reason = "'XmmMem' indicates this has Xmm and memory variants"
286)]
287pub enum XmmMem<R: AsReg, M: AsReg> {
288 Xmm(R),
289 Mem(Amode<M>),
290}
291
292impl<R: AsReg, M: AsReg> XmmMem<R, M> {
293 pub fn to_string(&self) -> String {
295 match self {
296 XmmMem::Xmm(xmm) => xmm.to_string(None),
297 XmmMem::Mem(amode) => amode.to_string(),
298 }
299 }
300}
301
302pub fn emit_modrm_sib_disp<R: AsReg>(
304 sink: &mut impl CodeSink,
305 offsets: &impl KnownOffsetTable,
306 enc_g: u8,
307 mem_e: &Amode<R>,
308 bytes_at_end: u8,
309 evex_scaling: Option<i8>,
310) {
311 match mem_e.clone() {
312 Amode::ImmReg { simm32, base, .. } => {
313 let enc_e = base.enc();
314 let mut imm = Imm::new(simm32.value(offsets), evex_scaling);
315
316 let enc_e_low3 = enc_e & 7;
320 if enc_e_low3 == gpr::enc::RSP {
321 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
327 sink.put1(0b00_100_100);
328 imm.emit(sink);
329 } else {
330 if enc_e_low3 == gpr::enc::RBP {
334 imm.force_immediate();
335 }
336 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7));
337 imm.emit(sink);
338 }
339 }
340
341 Amode::ImmRegRegShift {
342 simm32,
343 base,
344 index,
345 scale,
346 ..
347 } => {
348 let enc_base = base.enc();
349 let enc_index = index.enc();
350
351 assert!(enc_index != gpr::enc::RSP);
356
357 let mut imm = Imm::new(simm32.value(), evex_scaling);
362 if enc_base & 7 == gpr::enc::RBP {
363 imm.force_immediate();
364 }
365
366 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
369 sink.put1(encode_sib(scale.enc(), enc_index & 7, enc_base & 7));
370 imm.emit(sink);
371 }
372
373 Amode::RipRelative { target } => {
374 sink.put1(encode_modrm(0b00, enc_g & 7, 0b101));
376
377 let offset = sink.current_offset();
378 let target = match target {
379 DeferredTarget::Label(label) => label.clone(),
380 DeferredTarget::Constant(constant) => sink.get_label_for_constant(constant.clone()),
381 };
382 sink.use_label_at_offset(offset, target);
383
384 sink.put4(-(i32::from(bytes_at_end)) as u32);
392 }
393 }
394}