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