1use super::{
4 Amode, Gpr, Inst, LabelUse, MachBuffer, MachLabel, OperandVisitor, OperandVisitorImpl,
5 SyntheticAmode, VCodeConstant, WritableGpr, WritableXmm, Xmm, args::FromWritableReg, regs,
6};
7use crate::{Reg, Writable, ir::TrapCode};
8use cranelift_assembler_x64 as asm;
9use regalloc2::{PReg, RegClass};
10use std::string::String;
11
12#[derive(Clone, Debug)]
14pub struct CraneliftRegisters;
15impl asm::Registers for CraneliftRegisters {
16 type ReadGpr = Gpr;
17 type ReadWriteGpr = PairedGpr;
18 type WriteGpr = WritableGpr;
19 type ReadXmm = Xmm;
20 type ReadWriteXmm = PairedXmm;
21 type WriteXmm = WritableXmm;
22}
23
24#[derive(Clone, Copy, Debug, PartialEq)]
31#[expect(missing_docs, reason = "self-describing variants")]
32pub struct PairedGpr {
33 pub read: Gpr,
34 pub write: WritableGpr,
35}
36
37impl From<WritableGpr> for PairedGpr {
38 fn from(wgpr: WritableGpr) -> Self {
39 let read = wgpr.to_reg();
40 let write = wgpr;
41 Self { read, write }
42 }
43}
44
45impl From<WritableGpr> for asm::Gpr<PairedGpr> {
47 fn from(wgpr: WritableGpr) -> Self {
48 asm::Gpr::new(wgpr.into())
49 }
50}
51
52impl From<Writable<Reg>> for asm::GprMem<PairedGpr, Gpr> {
54 fn from(wgpr: Writable<Reg>) -> Self {
55 assert!(wgpr.to_reg().class() == RegClass::Int);
56 let wgpr = WritableGpr::from_writable_reg(wgpr).unwrap();
57 Self::Gpr(wgpr.into())
58 }
59}
60
61impl From<Reg> for asm::GprMem<Gpr, Gpr> {
63 fn from(gpr: Reg) -> Self {
64 assert!(gpr.class() == RegClass::Int);
65 let gpr = Gpr::unwrap_new(gpr);
66 Self::Gpr(gpr)
67 }
68}
69
70impl From<Writable<Reg>> for asm::GprMem<Gpr, Gpr> {
72 fn from(wgpr: Writable<Reg>) -> Self {
73 wgpr.to_reg().into()
74 }
75}
76
77impl From<Writable<Reg>> for asm::Gpr<PairedGpr> {
79 fn from(wgpr: Writable<Reg>) -> Self {
80 assert!(wgpr.to_reg().class() == RegClass::Int);
81 let wgpr = WritableGpr::from_writable_reg(wgpr).unwrap();
82 Self::new(wgpr.into())
83 }
84}
85
86impl From<Writable<Reg>> for asm::Gpr<WritableGpr> {
87 fn from(wgpr: Writable<Reg>) -> Self {
88 assert!(wgpr.to_reg().class() == RegClass::Int);
89 let wgpr = WritableGpr::from_writable_reg(wgpr).unwrap();
90 Self::new(wgpr)
91 }
92}
93
94impl asm::AsReg for PairedGpr {
95 fn enc(&self) -> u8 {
96 let PairedGpr { read, write } = self;
97 let read = enc_gpr(read);
98 let write = enc_gpr(&write.to_reg());
99 assert_eq!(read, write);
100 write
101 }
102
103 fn to_string(&self, size: Option<asm::Size>) -> String {
104 if self.read.is_real() {
105 asm::gpr::enc::to_string(self.enc(), size.unwrap()).into()
106 } else {
107 let read = self.read.to_reg();
108 let write = self.write.to_reg().to_reg();
109 format!("(%{write:?} <- %{read:?})")
110 }
111 }
112
113 fn new(_: u8) -> Self {
114 panic!("disallow creation of new assembler registers")
115 }
116}
117
118#[derive(Clone, Copy, Debug, PartialEq)]
120#[expect(missing_docs, reason = "self-describing variants")]
121pub struct PairedXmm {
122 pub read: Xmm,
123 pub write: WritableXmm,
124}
125
126impl From<WritableXmm> for PairedXmm {
127 fn from(wxmm: WritableXmm) -> Self {
128 let read = wxmm.to_reg();
129 let write = wxmm;
130 Self { read, write }
131 }
132}
133
134impl From<WritableXmm> for asm::Xmm<PairedXmm> {
136 fn from(wgpr: WritableXmm) -> Self {
137 asm::Xmm::new(wgpr.into())
138 }
139}
140
141impl From<Writable<Reg>> for asm::Xmm<PairedXmm> {
143 fn from(wxmm: Writable<Reg>) -> Self {
144 assert!(wxmm.to_reg().class() == RegClass::Float);
145 let wxmm = WritableXmm::from_writable_reg(wxmm).unwrap();
146 Self::new(wxmm.into())
147 }
148}
149
150impl From<Reg> for asm::Xmm<Xmm> {
152 fn from(xmm: Reg) -> Self {
153 assert!(xmm.class() == RegClass::Float);
154 let xmm = Xmm::unwrap_new(xmm);
155 Self::new(xmm)
156 }
157}
158
159impl From<Reg> for asm::XmmMem<Xmm, Gpr> {
161 fn from(xmm: Reg) -> Self {
162 assert!(xmm.class() == RegClass::Float);
163 let xmm = Xmm::unwrap_new(xmm);
164 Self::Xmm(xmm)
165 }
166}
167
168impl asm::AsReg for PairedXmm {
169 fn enc(&self) -> u8 {
170 let PairedXmm { read, write } = self;
171 let read = enc_xmm(read);
172 let write = enc_xmm(&write.to_reg());
173 assert_eq!(read, write);
174 write
175 }
176
177 fn to_string(&self, size: Option<asm::Size>) -> String {
178 assert!(size.is_none(), "XMM registers do not have size variants");
179 if self.read.is_real() {
180 asm::xmm::enc::to_string(self.enc()).into()
181 } else {
182 let read = self.read.to_reg();
183 let write = self.write.to_reg().to_reg();
184 format!("(%{write:?} <- %{read:?})")
185 }
186 }
187
188 fn new(_: u8) -> Self {
189 panic!("disallow creation of new assembler registers")
190 }
191}
192
193impl asm::AsReg for Gpr {
195 fn enc(&self) -> u8 {
196 enc_gpr(self)
197 }
198
199 fn to_string(&self, size: Option<asm::Size>) -> String {
200 if self.is_real() {
201 asm::gpr::enc::to_string(self.enc(), size.unwrap()).into()
202 } else {
203 format!("%{:?}", self.to_reg())
204 }
205 }
206
207 fn new(_: u8) -> Self {
208 panic!("disallow creation of new assembler registers")
209 }
210}
211
212impl asm::AsReg for Xmm {
214 fn enc(&self) -> u8 {
215 enc_xmm(self)
216 }
217
218 fn to_string(&self, size: Option<asm::Size>) -> String {
219 assert!(size.is_none(), "XMM registers do not have size variants");
220 if self.is_real() {
221 asm::xmm::enc::to_string(self.enc()).into()
222 } else {
223 format!("%{:?}", self.to_reg())
224 }
225 }
226
227 fn new(_: u8) -> Self {
228 panic!("disallow creation of new assembler registers")
229 }
230}
231
232#[inline]
234fn enc_gpr(gpr: &Gpr) -> u8 {
235 if let Some(real) = gpr.to_reg().to_real_reg() {
236 real.hw_enc()
237 } else {
238 unreachable!()
239 }
240}
241
242#[inline]
244fn enc_xmm(xmm: &Xmm) -> u8 {
245 if let Some(real) = xmm.to_reg().to_real_reg() {
246 real.hw_enc()
247 } else {
248 unreachable!()
249 }
250}
251
252pub(crate) struct RegallocVisitor<'a, T>
256where
257 T: OperandVisitorImpl,
258{
259 pub collector: &'a mut T,
260}
261
262impl<'a, T: OperandVisitor> asm::RegisterVisitor<CraneliftRegisters> for RegallocVisitor<'a, T> {
263 fn read_gpr(&mut self, reg: &mut Gpr) {
264 self.collector.reg_use(reg);
265 }
266
267 fn read_write_gpr(&mut self, reg: &mut PairedGpr) {
268 let PairedGpr { read, write } = reg;
269 self.collector.reg_use(read);
270 self.collector.reg_reuse_def(write, 0);
271 }
272
273 fn write_gpr(&mut self, reg: &mut WritableGpr) {
274 self.collector.reg_def(reg);
275 }
276
277 fn fixed_read_gpr(&mut self, reg: &mut Gpr, enc: u8) {
278 self.collector
279 .reg_fixed_use(reg, fixed_reg(enc, RegClass::Int));
280 }
281
282 fn fixed_read_write_gpr(&mut self, reg: &mut PairedGpr, enc: u8) {
283 let PairedGpr { read, write } = reg;
284 self.collector
285 .reg_fixed_use(read, fixed_reg(enc, RegClass::Int));
286 self.collector
287 .reg_fixed_def(write, fixed_reg(enc, RegClass::Int));
288 }
289
290 fn fixed_write_gpr(&mut self, reg: &mut WritableGpr, enc: u8) {
291 self.collector
292 .reg_fixed_def(reg, fixed_reg(enc, RegClass::Int));
293 }
294
295 fn read_xmm(&mut self, reg: &mut Xmm) {
296 self.collector.reg_use(reg);
297 }
298
299 fn read_write_xmm(&mut self, reg: &mut PairedXmm) {
300 let PairedXmm { read, write } = reg;
301 self.collector.reg_use(read);
302 self.collector.reg_reuse_def(write, 0);
303 }
304
305 fn write_xmm(&mut self, reg: &mut WritableXmm) {
306 self.collector.reg_def(reg);
307 }
308
309 fn fixed_read_xmm(&mut self, reg: &mut Xmm, enc: u8) {
310 self.collector
311 .reg_fixed_use(reg, fixed_reg(enc, RegClass::Float));
312 }
313
314 fn fixed_read_write_xmm(&mut self, reg: &mut PairedXmm, enc: u8) {
315 let PairedXmm { read, write } = reg;
316 self.collector
317 .reg_fixed_use(read, fixed_reg(enc, RegClass::Float));
318 self.collector
319 .reg_fixed_def(write, fixed_reg(enc, RegClass::Float));
320 }
321
322 fn fixed_write_xmm(&mut self, reg: &mut WritableXmm, enc: u8) {
323 self.collector
324 .reg_fixed_def(reg, fixed_reg(enc, RegClass::Float));
325 }
326}
327
328fn fixed_reg(enc: u8, class: RegClass) -> Reg {
330 let preg = PReg::new(usize::from(enc), class);
331 Reg::from_real_reg(preg)
332}
333
334impl From<SyntheticAmode> for asm::Amode<Gpr> {
335 fn from(amode: SyntheticAmode) -> asm::Amode<Gpr> {
336 match amode {
337 SyntheticAmode::Real(amode) => amode.into(),
338 SyntheticAmode::IncomingArg { offset } => asm::Amode::ImmReg {
339 base: Gpr::unwrap_new(regs::rbp()),
340 simm32: asm::AmodeOffsetPlusKnownOffset {
341 simm32: (-i32::try_from(offset).unwrap()).into(),
342 offset: Some(offsets::KEY_INCOMING_ARG),
343 },
344 trap: None,
345 },
346 SyntheticAmode::SlotOffset { simm32 } => asm::Amode::ImmReg {
347 base: Gpr::unwrap_new(regs::rsp()),
348 simm32: asm::AmodeOffsetPlusKnownOffset {
349 simm32: simm32.into(),
350 offset: Some(offsets::KEY_SLOT_OFFSET),
351 },
352 trap: None,
353 },
354 SyntheticAmode::ConstantOffset(vcode_constant) => asm::Amode::RipRelative {
355 target: asm::DeferredTarget::Constant(asm::Constant(vcode_constant.as_u32())),
356 },
357 }
358 }
359}
360
361impl From<Amode> for asm::Amode<Gpr> {
362 fn from(amode: Amode) -> asm::Amode<Gpr> {
363 match amode {
364 Amode::ImmReg {
365 simm32,
366 base,
367 flags,
368 } => asm::Amode::ImmReg {
369 simm32: asm::AmodeOffsetPlusKnownOffset {
370 simm32: simm32.into(),
371 offset: None,
372 },
373 base: Gpr::unwrap_new(base),
374 trap: flags.trap_code().map(Into::into),
375 },
376 Amode::ImmRegRegShift {
377 simm32,
378 base,
379 index,
380 shift,
381 flags,
382 } => asm::Amode::ImmRegRegShift {
383 base,
384 index: asm::NonRspGpr::new(index),
385 scale: asm::Scale::new(shift),
386 simm32: simm32.into(),
387 trap: flags.trap_code().map(Into::into),
388 },
389 Amode::RipRelative { target } => asm::Amode::RipRelative {
390 target: asm::DeferredTarget::Label(asm::Label(target.as_u32())),
391 },
392 }
393 }
394}
395
396impl<R: asm::AsReg> From<SyntheticAmode> for asm::XmmMem<R, Gpr> {
397 fn from(amode: SyntheticAmode) -> Self {
398 asm::XmmMem::Mem(amode.into())
399 }
400}
401
402impl<R: asm::AsReg> From<SyntheticAmode> for asm::GprMem<R, Gpr> {
403 fn from(amode: SyntheticAmode) -> Self {
404 asm::GprMem::Mem(amode.into())
405 }
406}
407
408impl<R: asm::AsReg> From<Amode> for asm::XmmMem<R, Gpr> {
409 fn from(amode: Amode) -> Self {
410 asm::XmmMem::Mem(amode.into())
411 }
412}
413
414impl<R: asm::AsReg> From<Amode> for asm::GprMem<R, Gpr> {
415 fn from(amode: Amode) -> Self {
416 asm::GprMem::Mem(amode.into())
417 }
418}
419
420#[expect(missing_docs, reason = "self-describing keys")]
423pub mod offsets {
424 pub const KEY_INCOMING_ARG: u8 = 0;
425 pub const KEY_SLOT_OFFSET: u8 = 1;
426}
427
428impl asm::CodeSink for MachBuffer<Inst> {
429 fn put1(&mut self, value: u8) {
430 self.put1(value)
431 }
432
433 fn put2(&mut self, value: u16) {
434 self.put2(value)
435 }
436
437 fn put4(&mut self, value: u32) {
438 self.put4(value)
439 }
440
441 fn put8(&mut self, value: u64) {
442 self.put8(value)
443 }
444
445 fn current_offset(&self) -> u32 {
446 self.cur_offset()
447 }
448
449 fn use_label_at_offset(&mut self, offset: u32, label: asm::Label) {
450 self.use_label_at_offset(offset, label.into(), LabelUse::JmpRel32);
451 }
452
453 fn add_trap(&mut self, code: asm::TrapCode) {
454 self.add_trap(code.into());
455 }
456
457 fn get_label_for_constant(&mut self, c: asm::Constant) -> asm::Label {
458 self.get_label_for_constant(c.into()).into()
459 }
460}
461
462impl From<asm::TrapCode> for TrapCode {
463 fn from(value: asm::TrapCode) -> Self {
464 Self::from_raw(value.0)
465 }
466}
467
468impl From<TrapCode> for asm::TrapCode {
469 fn from(value: TrapCode) -> Self {
470 Self(value.as_raw())
471 }
472}
473
474impl From<asm::Label> for MachLabel {
475 fn from(value: asm::Label) -> Self {
476 Self::from_u32(value.0)
477 }
478}
479
480impl From<MachLabel> for asm::Label {
481 fn from(value: MachLabel) -> Self {
482 Self(value.as_u32())
483 }
484}
485
486impl From<asm::Constant> for VCodeConstant {
487 fn from(value: asm::Constant) -> Self {
488 Self::from_u32(value.0)
489 }
490}
491
492include!(concat!(env!("OUT_DIR"), "/assembler-isle-macro.rs"));
496pub(crate) use isle_assembler_methods;
497
498#[cfg(test)]
499mod tests {
500 use super::PairedGpr;
501 use super::asm::{AsReg, Size};
502 use crate::isa::x64::args::{FromWritableReg, Gpr, WritableGpr, WritableXmm, Xmm};
503 use crate::isa::x64::inst::external::PairedXmm;
504 use crate::{Reg, Writable};
505 use regalloc2::{RegClass, VReg};
506
507 #[test]
508 fn pretty_print_registers() {
509 let v200: Reg = VReg::new(200, RegClass::Int).into();
515 let gpr200 = Gpr::new(v200).unwrap();
516 assert_eq!(gpr200.to_string(Some(Size::Quadword)), "%v200");
517
518 let v300: Reg = VReg::new(300, RegClass::Int).into();
519 let wgpr300 = WritableGpr::from_writable_reg(Writable::from_reg(v300)).unwrap();
520 let pair = PairedGpr {
521 read: gpr200,
522 write: wgpr300,
523 };
524 assert_eq!(pair.to_string(Some(Size::Quadword)), "(%v300 <- %v200)");
525
526 let v400: Reg = VReg::new(400, RegClass::Float).into();
527 let xmm400 = Xmm::new(v400).unwrap();
528 assert_eq!(xmm400.to_string(None), "%v400");
529
530 let v500: Reg = VReg::new(500, RegClass::Float).into();
531 let wxmm500 = WritableXmm::from_writable_reg(Writable::from_reg(v500)).unwrap();
532 let pair = PairedXmm {
533 read: xmm400,
534 write: wxmm500,
535 };
536 assert_eq!(pair.to_string(None), "(%v500 <- %v400)");
537 }
538}