1use crate::api::{AsReg, CodeSink, Constant, KnownOffsetTable, Label, TrapCode};
4use crate::imm::{Simm32, Simm32PlusKnownOffset};
5use crate::reg::{self, NonRspGpr, Size};
6use crate::rex::{encode_modrm, encode_sib, Imm, RexFlags};
7
8#[derive(Clone, Debug)]
10#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
11pub enum Amode<R: AsReg> {
12 ImmReg {
13 base: R,
14 simm32: Simm32PlusKnownOffset,
15 trap: Option<TrapCode>,
16 },
17 ImmRegRegShift {
18 base: R,
19 index: NonRspGpr<R>,
20 scale: Scale,
21 simm32: Simm32,
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 pub fn registers_mut(&mut self) -> Vec<&mut R> {
62 match self {
63 Amode::ImmReg { base, .. } => {
64 vec![base]
65 }
66 Amode::ImmRegRegShift { base, index, .. } => {
67 vec![base, index.as_mut()]
68 }
69 Amode::RipRelative { .. } => {
70 vec![]
71 }
72 }
73 }
74}
75
76#[derive(Clone, Debug)]
78#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
79pub enum DeferredTarget {
80 Label(Label),
81 Constant(Constant),
82}
83
84impl<R: AsReg> std::fmt::Display for Amode<R> {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 match self {
87 Amode::ImmReg { simm32, base, .. } => {
88 let base = reg::enc::to_string(base.enc(), Size::Quadword);
91 write!(f, "{simm32:x}({base})")
92 }
93 Amode::ImmRegRegShift {
94 simm32,
95 base,
96 index,
97 scale,
98 ..
99 } => {
100 let base = reg::enc::to_string(base.enc(), Size::Quadword);
101 let index = reg::enc::to_string(index.enc(), Size::Quadword);
102 let shift = scale.shift();
103 if shift > 1 {
104 write!(f, "{simm32:x}({base}, {index}, {shift})")
105 } else {
106 write!(f, "{simm32:x}({base}, {index})")
107 }
108 }
109 Amode::RipRelative { .. } => write!(f, "(%rip)"),
110 }
111 }
112}
113
114#[derive(Clone, Debug)]
116#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
117pub enum Scale {
118 One,
119 Two,
120 Four,
121 Eight,
122}
123
124impl Scale {
125 #[must_use]
131 pub fn new(enc: u8) -> Self {
132 match enc {
133 0b00 => Scale::One,
134 0b01 => Scale::Two,
135 0b10 => Scale::Four,
136 0b11 => Scale::Eight,
137 _ => panic!("invalid scale encoding: {enc}"),
138 }
139 }
140
141 fn enc(&self) -> u8 {
143 match self {
144 Scale::One => 0b00,
145 Scale::Two => 0b01,
146 Scale::Four => 0b10,
147 Scale::Eight => 0b11,
148 }
149 }
150
151 fn shift(&self) -> u8 {
157 1 << self.enc()
158 }
159}
160
161#[derive(Clone, Debug)]
163#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
164#[allow(clippy::module_name_repetitions)]
165pub enum GprMem<R: AsReg, M: AsReg> {
166 Gpr(R),
167 Mem(Amode<M>),
168}
169
170impl<R: AsReg, M: AsReg> GprMem<R, M> {
171 pub fn to_string(&self, size: Size) -> String {
173 match self {
174 GprMem::Gpr(gpr) => reg::enc::to_string(gpr.enc(), size).to_owned(),
175 GprMem::Mem(amode) => amode.to_string(),
176 }
177 }
178
179 pub(crate) fn always_emit_if_8bit_needed(&self, rex: &mut RexFlags) {
182 match self {
183 GprMem::Gpr(gpr) => {
184 rex.always_emit_if_8bit_needed(gpr.enc());
185 }
186 GprMem::Mem(_) => {}
187 }
188 }
189}
190
191pub fn emit_modrm_sib_disp<R: AsReg>(
193 sink: &mut impl CodeSink,
194 offsets: &impl KnownOffsetTable,
195 enc_g: u8,
196 mem_e: &Amode<R>,
197 bytes_at_end: u8,
198 evex_scaling: Option<i8>,
199) {
200 match mem_e.clone() {
201 Amode::ImmReg { simm32, base, .. } => {
202 let enc_e = base.enc();
203 let mut imm = Imm::new(simm32.value(offsets), evex_scaling);
204
205 let enc_e_low3 = enc_e & 7;
209 if enc_e_low3 == reg::enc::RSP {
210 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
216 sink.put1(0b00_100_100);
217 imm.emit(sink);
218 } else {
219 if enc_e_low3 == reg::enc::RBP {
223 imm.force_immediate();
224 }
225 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7));
226 imm.emit(sink);
227 }
228 }
229
230 Amode::ImmRegRegShift {
231 simm32,
232 base,
233 index,
234 scale,
235 ..
236 } => {
237 let enc_base = base.enc();
238 let enc_index = index.enc();
239
240 assert!(enc_index != reg::enc::RSP);
245
246 let mut imm = Imm::new(simm32.value(), evex_scaling);
251 if enc_base & 7 == reg::enc::RBP {
252 imm.force_immediate();
253 }
254
255 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100));
258 sink.put1(encode_sib(scale.enc(), enc_index & 7, enc_base & 7));
259 imm.emit(sink);
260 }
261
262 Amode::RipRelative { target } => {
263 sink.put1(encode_modrm(0b00, enc_g & 7, 0b101));
265
266 let offset = sink.current_offset();
267 let target = match target {
268 DeferredTarget::Label(label) => label.clone(),
269 DeferredTarget::Constant(constant) => sink.get_label_for_constant(constant.clone()),
270 };
271 sink.use_label_at_offset(offset, target);
272
273 #[allow(clippy::cast_sign_loss)]
281 sink.put4(-(i32::from(bytes_at_end)) as u32);
282 }
283 }
284}