cranelift_codegen/isa/x64/inst/
external.rs1use 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 ReadXmm = Xmm;
19 type ReadWriteXmm = PairedXmm;
20}
21
22#[derive(Clone, Copy, Debug)]
29#[expect(missing_docs, reason = "self-describing variants")]
30pub struct PairedGpr {
31 pub read: Gpr,
32 pub write: WritableGpr,
33}
34
35impl From<WritableGpr> for PairedGpr {
36 fn from(wgpr: WritableGpr) -> Self {
37 let read = wgpr.to_reg();
38 let write = wgpr;
39 Self { read, write }
40 }
41}
42
43impl From<WritableGpr> for asm::Gpr<PairedGpr> {
45 fn from(wgpr: WritableGpr) -> Self {
46 asm::Gpr::new(wgpr.into())
47 }
48}
49
50impl From<Writable<Reg>> for asm::GprMem<PairedGpr, Gpr> {
52 fn from(wgpr: Writable<Reg>) -> Self {
53 assert!(wgpr.to_reg().class() == RegClass::Int);
54 let wgpr = WritableGpr::from_writable_reg(wgpr).unwrap();
55 Self::Gpr(wgpr.into())
56 }
57}
58
59impl From<Gpr> for asm::GprMem<Gpr, Gpr> {
61 fn from(gpr: Gpr) -> Self {
62 Self::Gpr(gpr)
63 }
64}
65
66impl From<Reg> for asm::GprMem<Gpr, Gpr> {
68 fn from(gpr: Reg) -> Self {
69 assert!(gpr.class() == RegClass::Int);
70 let gpr = Gpr::unwrap_new(gpr);
71 Self::Gpr(gpr)
72 }
73}
74
75impl From<Writable<Reg>> for asm::GprMem<Gpr, Gpr> {
77 fn from(wgpr: Writable<Reg>) -> Self {
78 wgpr.to_reg().into()
79 }
80}
81
82impl From<Writable<Reg>> for asm::Gpr<PairedGpr> {
84 fn from(wgpr: Writable<Reg>) -> Self {
85 assert!(wgpr.to_reg().class() == RegClass::Int);
86 let wgpr = WritableGpr::from_writable_reg(wgpr).unwrap();
87 Self::new(wgpr.into())
88 }
89}
90
91impl From<PairedGpr> for asm::Gpr<PairedGpr> {
93 fn from(pair: PairedGpr) -> Self {
94 Self::new(pair)
95 }
96}
97
98impl From<PairedGpr> for asm::GprMem<PairedGpr, Gpr> {
100 fn from(pair: PairedGpr) -> Self {
101 Self::Gpr(pair)
102 }
103}
104
105impl asm::AsReg for PairedGpr {
106 fn enc(&self) -> u8 {
107 let PairedGpr { read, write } = self;
108 let read = enc_gpr(read);
109 let write = enc_gpr(&write.to_reg());
110 assert_eq!(read, write);
111 write
112 }
113
114 fn to_string(&self, size: Option<asm::Size>) -> String {
115 if self.read.is_real() {
116 asm::gpr::enc::to_string(self.enc(), size.unwrap()).into()
117 } else {
118 let read = self.read.to_reg();
119 let write = self.write.to_reg().to_reg();
120 format!("(%{write:?} <- %{read:?})")
121 }
122 }
123
124 fn new(_: u8) -> Self {
125 panic!("disallow creation of new assembler registers")
126 }
127}
128
129#[derive(Clone, Copy, Debug)]
131#[expect(missing_docs, reason = "self-describing variants")]
132pub struct PairedXmm {
133 pub read: Xmm,
134 pub write: WritableXmm,
135}
136
137impl From<WritableXmm> for PairedXmm {
138 fn from(wxmm: WritableXmm) -> Self {
139 let read = wxmm.to_reg();
140 let write = wxmm;
141 Self { read, write }
142 }
143}
144
145impl From<WritableXmm> for asm::Xmm<PairedXmm> {
147 fn from(wgpr: WritableXmm) -> Self {
148 asm::Xmm::new(wgpr.into())
149 }
150}
151
152impl From<Writable<Reg>> for asm::Xmm<PairedXmm> {
154 fn from(wxmm: Writable<Reg>) -> Self {
155 assert!(wxmm.to_reg().class() == RegClass::Float);
156 let wxmm = WritableXmm::from_writable_reg(wxmm).unwrap();
157 Self::new(wxmm.into())
158 }
159}
160
161impl From<Reg> for asm::Xmm<Xmm> {
163 fn from(xmm: Reg) -> Self {
164 assert!(xmm.class() == RegClass::Float);
165 let xmm = Xmm::unwrap_new(xmm);
166 Self::new(xmm)
167 }
168}
169
170impl From<Reg> for asm::XmmMem<Xmm, Gpr> {
172 fn from(xmm: Reg) -> Self {
173 assert!(xmm.class() == RegClass::Float);
174 let xmm = Xmm::unwrap_new(xmm);
175 Self::Xmm(xmm)
176 }
177}
178
179impl From<PairedXmm> for asm::Xmm<PairedXmm> {
181 fn from(pair: PairedXmm) -> Self {
182 Self::new(pair)
183 }
184}
185
186impl asm::AsReg for PairedXmm {
187 fn enc(&self) -> u8 {
188 let PairedXmm { read, write } = self;
189 let read = enc_xmm(read);
190 let write = enc_xmm(&write.to_reg());
191 assert_eq!(read, write);
192 write
193 }
194
195 fn to_string(&self, size: Option<asm::Size>) -> String {
196 assert!(size.is_none(), "XMM registers do not have size variants");
197 if self.read.is_real() {
198 asm::xmm::enc::to_string(self.enc()).into()
199 } else {
200 let read = self.read.to_reg();
201 let write = self.write.to_reg().to_reg();
202 format!("(%{write:?} <- %{read:?})")
203 }
204 }
205
206 fn new(_: u8) -> Self {
207 panic!("disallow creation of new assembler registers")
208 }
209}
210
211impl asm::AsReg for Gpr {
213 fn enc(&self) -> u8 {
214 enc_gpr(self)
215 }
216
217 fn to_string(&self, size: Option<asm::Size>) -> String {
218 if self.is_real() {
219 asm::gpr::enc::to_string(self.enc(), size.unwrap()).into()
220 } else {
221 format!("%{:?}", self.to_reg())
222 }
223 }
224
225 fn new(_: u8) -> Self {
226 panic!("disallow creation of new assembler registers")
227 }
228}
229
230impl asm::AsReg for Xmm {
232 fn enc(&self) -> u8 {
233 enc_xmm(self)
234 }
235
236 fn to_string(&self, size: Option<asm::Size>) -> String {
237 assert!(size.is_none(), "XMM registers do not have size variants");
238 if self.is_real() {
239 asm::xmm::enc::to_string(self.enc()).into()
240 } else {
241 format!("%{:?}", self.to_reg())
242 }
243 }
244
245 fn new(_: u8) -> Self {
246 panic!("disallow creation of new assembler registers")
247 }
248}
249
250#[inline]
252fn enc_gpr(gpr: &Gpr) -> u8 {
253 if let Some(real) = gpr.to_reg().to_real_reg() {
254 real.hw_enc()
255 } else {
256 unreachable!()
257 }
258}
259
260#[inline]
262fn enc_xmm(xmm: &Xmm) -> u8 {
263 if let Some(real) = xmm.to_reg().to_real_reg() {
264 real.hw_enc()
265 } else {
266 unreachable!()
267 }
268}
269
270pub(crate) struct RegallocVisitor<'a, T>
274where
275 T: OperandVisitorImpl,
276{
277 pub collector: &'a mut T,
278}
279
280impl<'a, T: OperandVisitor> asm::RegisterVisitor<CraneliftRegisters> for RegallocVisitor<'a, T> {
281 fn read_gpr(&mut self, reg: &mut Gpr) {
282 self.collector.reg_use(reg);
283 }
284
285 fn read_write_gpr(&mut self, reg: &mut PairedGpr) {
286 let PairedGpr { read, write } = reg;
287 self.collector.reg_use(read);
288 self.collector.reg_reuse_def(write, 0);
289 }
290
291 fn fixed_read_gpr(&mut self, reg: &mut Gpr, enc: u8) {
292 self.collector
293 .reg_fixed_use(reg, fixed_reg(enc, RegClass::Int));
294 }
295
296 fn fixed_read_write_gpr(&mut self, reg: &mut PairedGpr, enc: u8) {
297 let PairedGpr { read, write } = reg;
298 self.collector
299 .reg_fixed_use(read, fixed_reg(enc, RegClass::Int));
300 self.collector
301 .reg_fixed_def(write, fixed_reg(enc, RegClass::Int));
302 }
303
304 fn read_xmm(&mut self, reg: &mut Xmm) {
305 self.collector.reg_use(reg);
306 }
307
308 fn read_write_xmm(&mut self, reg: &mut PairedXmm) {
309 let PairedXmm { read, write } = reg;
310 self.collector.reg_use(read);
311 self.collector.reg_reuse_def(write, 0);
312 }
313
314 fn fixed_read_xmm(&mut self, reg: &mut Xmm, enc: u8) {
315 self.collector
316 .reg_fixed_use(reg, fixed_reg(enc, RegClass::Float));
317 }
318
319 fn fixed_read_write_xmm(&mut self, reg: &mut PairedXmm, enc: u8) {
320 let PairedXmm { read, write } = reg;
321 self.collector
322 .reg_fixed_use(read, fixed_reg(enc, RegClass::Float));
323 self.collector
324 .reg_fixed_def(write, 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 Into<asm::Amode<Gpr>> for SyntheticAmode {
335 fn into(self) -> asm::Amode<Gpr> {
336 match self {
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::rbp()),
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 Into<asm::Amode<Gpr>> for Amode {
362 fn into(self) -> asm::Amode<Gpr> {
363 match self {
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
396#[expect(missing_docs, reason = "self-describing keys")]
399pub mod offsets {
400 pub const KEY_INCOMING_ARG: usize = 0;
401 pub const KEY_SLOT_OFFSET: usize = 1;
402}
403
404impl asm::CodeSink for MachBuffer<Inst> {
405 fn put1(&mut self, value: u8) {
406 self.put1(value)
407 }
408
409 fn put2(&mut self, value: u16) {
410 self.put2(value)
411 }
412
413 fn put4(&mut self, value: u32) {
414 self.put4(value)
415 }
416
417 fn put8(&mut self, value: u64) {
418 self.put8(value)
419 }
420
421 fn current_offset(&self) -> u32 {
422 self.cur_offset()
423 }
424
425 fn use_label_at_offset(&mut self, offset: u32, label: asm::Label) {
426 self.use_label_at_offset(offset, label.into(), LabelUse::JmpRel32);
427 }
428
429 fn add_trap(&mut self, code: asm::TrapCode) {
430 self.add_trap(code.into());
431 }
432
433 fn get_label_for_constant(&mut self, c: asm::Constant) -> asm::Label {
434 self.get_label_for_constant(c.into()).into()
435 }
436}
437
438impl From<asm::TrapCode> for TrapCode {
439 fn from(value: asm::TrapCode) -> Self {
440 Self::from_raw(value.0)
441 }
442}
443
444impl From<TrapCode> for asm::TrapCode {
445 fn from(value: TrapCode) -> Self {
446 Self(value.as_raw())
447 }
448}
449
450impl From<asm::Label> for MachLabel {
451 fn from(value: asm::Label) -> Self {
452 Self::from_u32(value.0)
453 }
454}
455
456impl From<MachLabel> for asm::Label {
457 fn from(value: MachLabel) -> Self {
458 Self(value.as_u32())
459 }
460}
461
462impl From<asm::Constant> for VCodeConstant {
463 fn from(value: asm::Constant) -> Self {
464 Self::from_u32(value.0)
465 }
466}
467
468include!(concat!(env!("OUT_DIR"), "/assembler-isle-macro.rs"));
472pub(crate) use isle_assembler_methods;
473
474#[cfg(test)]
475mod tests {
476 use super::asm::{AsReg, Size};
477 use super::PairedGpr;
478 use crate::isa::x64::args::{FromWritableReg, Gpr, WritableGpr, WritableXmm, Xmm};
479 use crate::isa::x64::inst::external::PairedXmm;
480 use crate::{Reg, Writable};
481 use regalloc2::{RegClass, VReg};
482
483 #[test]
484 fn pretty_print_registers() {
485 let v200: Reg = VReg::new(200, RegClass::Int).into();
491 let gpr200 = Gpr::new(v200).unwrap();
492 assert_eq!(gpr200.to_string(Some(Size::Quadword)), "%v200");
493
494 let v300: Reg = VReg::new(300, RegClass::Int).into();
495 let wgpr300 = WritableGpr::from_writable_reg(Writable::from_reg(v300).into()).unwrap();
496 let pair = PairedGpr {
497 read: gpr200,
498 write: wgpr300,
499 };
500 assert_eq!(pair.to_string(Some(Size::Quadword)), "(%v300 <- %v200)");
501
502 let v400: Reg = VReg::new(400, RegClass::Float).into();
503 let xmm400 = Xmm::new(v400).unwrap();
504 assert_eq!(xmm400.to_string(None), "%v400");
505
506 let v500: Reg = VReg::new(500, RegClass::Float).into();
507 let wxmm500 = WritableXmm::from_writable_reg(Writable::from_reg(v500).into()).unwrap();
508 let pair = PairedXmm {
509 read: xmm400,
510 write: wxmm500,
511 };
512 assert_eq!(pair.to_string(None), "(%v500 <- %v400)");
513 }
514}