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