1use alloc::{string::String, vec::Vec};
6use core::{fmt::Debug, hash::Hash};
7use regalloc2::{Operand, OperandConstraint, OperandKind, OperandPos, PReg, PRegSet, VReg};
8
9#[cfg(feature = "enable-serde")]
10use serde_derive::{Deserialize, Serialize};
11
12const PINNED_VREGS: usize = 192;
17
18pub const fn pinned_vreg_to_preg(vreg: VReg) -> Option<PReg> {
20 if vreg.vreg() < PINNED_VREGS {
21 Some(PReg::from_index(vreg.vreg()))
22 } else {
23 None
24 }
25}
26
27pub const fn preg_to_pinned_vreg(preg: PReg) -> VReg {
29 VReg::new(preg.index(), preg.class())
30}
31
32pub fn first_user_vreg_index() -> usize {
35 PINNED_VREGS
40}
41
42#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
49pub struct Reg(u32);
50
51const REG_SPILLSLOT_BIT: u32 = 0x8000_0000;
52const REG_SPILLSLOT_MASK: u32 = !REG_SPILLSLOT_BIT;
53
54impl Reg {
55 pub const fn from_virtual_reg(vreg: regalloc2::VReg) -> Reg {
57 let bits = vreg.bits() as u32;
58 debug_assert!(bits <= REG_SPILLSLOT_MASK);
59 Reg(bits)
60 }
61
62 pub const fn from_real_reg(preg: regalloc2::PReg) -> Reg {
64 let vreg = preg_to_pinned_vreg(preg);
65 let bits = vreg.bits() as u32;
66 debug_assert!(bits <= REG_SPILLSLOT_MASK);
67 Reg(bits)
68 }
69
70 pub fn from_virtual_reg_checked(vreg: regalloc2::VReg) -> Option<Reg> {
73 let bits = vreg.bits() as u32;
74 if bits <= REG_SPILLSLOT_MASK {
75 Some(Reg(bits))
76 } else {
77 None
78 }
79 }
80
81 pub const fn to_real_reg(self) -> Option<RealReg> {
84 match pinned_vreg_to_preg(VReg::from_bits(self.0)) {
86 Some(preg) => Some(RealReg(preg)),
87 None => None,
88 }
89 }
90
91 pub fn to_virtual_reg(self) -> Option<VirtualReg> {
94 if self.to_spillslot().is_some() {
95 None
96 } else if pinned_vreg_to_preg(self.0.into()).is_none() {
97 Some(VirtualReg(self.0.into()))
98 } else {
99 None
100 }
101 }
102
103 pub fn to_spillslot(self) -> Option<SpillSlot> {
105 if (self.0 & REG_SPILLSLOT_BIT) != 0 {
106 Some(SpillSlot::new((self.0 & REG_SPILLSLOT_MASK) as usize))
107 } else {
108 None
109 }
110 }
111
112 pub fn class(self) -> RegClass {
114 assert!(!self.to_spillslot().is_some());
115 VReg::from(self.0).class()
116 }
117
118 pub fn is_real(self) -> bool {
120 self.to_real_reg().is_some()
121 }
122
123 pub fn is_virtual(self) -> bool {
125 self.to_virtual_reg().is_some()
126 }
127
128 pub fn is_spillslot(self) -> bool {
130 self.to_spillslot().is_some()
131 }
132}
133
134impl core::fmt::Debug for Reg {
135 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
136 if VReg::from(self.0) == VReg::invalid() {
137 write!(f, "<invalid>")
138 } else if let Some(spillslot) = self.to_spillslot() {
139 write!(f, "{spillslot}")
140 } else if let Some(rreg) = self.to_real_reg() {
141 let preg: PReg = rreg.into();
142 write!(f, "{preg}")
143 } else if let Some(vreg) = self.to_virtual_reg() {
144 let vreg: VReg = vreg.into();
145 write!(f, "{vreg}")
146 } else {
147 unreachable!()
148 }
149 }
150}
151
152impl AsMut<Reg> for Reg {
153 fn as_mut(&mut self) -> &mut Reg {
154 self
155 }
156}
157
158#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
161#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
162pub struct RealReg(PReg);
163
164impl RealReg {
165 pub fn class(self) -> RegClass {
167 self.0.class()
168 }
169
170 pub fn hw_enc(self) -> u8 {
172 self.0.hw_enc() as u8
173 }
174
175 pub const fn preg(self) -> PReg {
177 self.0
178 }
179}
180
181impl core::fmt::Debug for RealReg {
182 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
183 Reg::from(*self).fmt(f)
184 }
185}
186
187#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
193#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
194pub struct VirtualReg(VReg);
195
196impl VirtualReg {
197 pub fn class(self) -> RegClass {
199 self.0.class()
200 }
201
202 pub fn index(self) -> usize {
203 self.0.vreg()
204 }
205}
206
207impl core::fmt::Debug for VirtualReg {
208 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
209 Reg::from(*self).fmt(f)
210 }
211}
212
213#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
223#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
224pub struct Writable<T> {
225 reg: T,
226}
227
228impl<T> Writable<T> {
229 pub const fn from_reg(reg: T) -> Writable<T> {
234 Writable { reg }
235 }
236
237 pub fn to_reg(self) -> T {
239 self.reg
240 }
241
242 pub fn reg_mut(&mut self) -> &mut T {
244 &mut self.reg
245 }
246
247 pub fn map<U>(self, f: impl Fn(T) -> U) -> Writable<U> {
249 Writable { reg: f(self.reg) }
250 }
251}
252
253impl<R: cranelift_assembler_x64::AsReg> cranelift_assembler_x64::AsReg for Writable<R> {
255 fn enc(&self) -> u8 {
256 self.reg.enc()
257 }
258
259 fn to_string(&self, size: Option<cranelift_assembler_x64::gpr::Size>) -> String {
260 self.reg.to_string(size)
261 }
262
263 fn new(_: u8) -> Self {
264 panic!("disallow creation of new assembler registers")
265 }
266}
267
268impl core::convert::From<regalloc2::VReg> for Reg {
272 fn from(vreg: regalloc2::VReg) -> Reg {
273 Reg(vreg.bits() as u32)
274 }
275}
276
277impl core::convert::From<regalloc2::VReg> for VirtualReg {
278 fn from(vreg: regalloc2::VReg) -> VirtualReg {
279 debug_assert!(pinned_vreg_to_preg(vreg).is_none());
280 VirtualReg(vreg)
281 }
282}
283
284impl core::convert::From<Reg> for regalloc2::VReg {
285 fn from(reg: Reg) -> regalloc2::VReg {
289 reg.0.into()
290 }
291}
292impl core::convert::From<&Reg> for regalloc2::VReg {
293 fn from(reg: &Reg) -> regalloc2::VReg {
294 reg.0.into()
295 }
296}
297
298impl core::convert::From<VirtualReg> for regalloc2::VReg {
299 fn from(reg: VirtualReg) -> regalloc2::VReg {
300 reg.0
301 }
302}
303
304impl core::convert::From<RealReg> for regalloc2::VReg {
305 fn from(reg: RealReg) -> regalloc2::VReg {
306 VReg::new(reg.0.index(), reg.0.class())
309 }
310}
311
312impl core::convert::From<RealReg> for regalloc2::PReg {
313 fn from(reg: RealReg) -> regalloc2::PReg {
314 reg.0
315 }
316}
317
318impl core::convert::From<regalloc2::PReg> for RealReg {
319 fn from(preg: regalloc2::PReg) -> RealReg {
320 RealReg(preg)
321 }
322}
323
324impl core::convert::From<regalloc2::PReg> for Reg {
325 fn from(preg: regalloc2::PReg) -> Reg {
326 RealReg(preg).into()
327 }
328}
329
330impl core::convert::From<RealReg> for Reg {
331 fn from(reg: RealReg) -> Reg {
332 Reg(VReg::from(reg).bits() as u32)
333 }
334}
335
336impl core::convert::From<VirtualReg> for Reg {
337 fn from(reg: VirtualReg) -> Reg {
338 Reg(reg.0.bits() as u32)
339 }
340}
341
342pub type SpillSlot = regalloc2::SpillSlot;
344
345impl core::convert::From<regalloc2::SpillSlot> for Reg {
346 fn from(spillslot: regalloc2::SpillSlot) -> Reg {
347 Reg(REG_SPILLSLOT_BIT | spillslot.index() as u32)
348 }
349}
350
351pub type RegClass = regalloc2::RegClass;
367
368#[derive(Debug)]
373pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {
374 operands: &'a mut Vec<Operand>,
375 clobbers: PRegSet,
376
377 allocatable: PRegSet,
379
380 renamer: F,
381}
382
383impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
384 pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self {
386 Self {
387 operands,
388 clobbers: PRegSet::default(),
389 allocatable,
390 renamer,
391 }
392 }
393
394 pub fn finish(self) -> (usize, PRegSet) {
398 let end = self.operands.len();
399 (end, self.clobbers)
400 }
401}
402
403pub trait OperandVisitor {
404 fn add_operand(
405 &mut self,
406 reg: &mut Reg,
407 constraint: OperandConstraint,
408 kind: OperandKind,
409 pos: OperandPos,
410 );
411
412 fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {}
413
414 fn reg_clobbers(&mut self, _regs: PRegSet) {}
418}
419
420pub trait OperandVisitorImpl: OperandVisitor {
421 fn reg_fixed_nonallocatable(&mut self, preg: PReg) {
423 self.debug_assert_is_allocatable_preg(preg, false);
424 }
427
428 fn reg_use(&mut self, reg: &mut impl AsMut<Reg>) {
431 self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early);
432 }
433
434 fn reg_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
436 self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late);
437 }
438
439 fn reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
443 self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Late);
444 }
445
446 fn reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
451 self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early);
452 }
453
454 fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
457 self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late);
458 }
459
460 fn reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
463 self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early);
464 }
465
466 fn reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, rreg: Reg) {
469 self.reg_fixed(reg.reg.as_mut(), rreg, OperandKind::Def, OperandPos::Late);
470 }
471
472 fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) {
474 debug_assert!(reg.is_virtual());
475 let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
476 self.debug_assert_is_allocatable_preg(rreg.into(), true);
477 let constraint = OperandConstraint::FixedReg(rreg.into());
478 self.add_operand(reg, constraint, kind, pos);
479 }
480
481 fn reg_maybe_fixed(&mut self, reg: &mut Reg, kind: OperandKind, pos: OperandPos) {
483 if let Some(rreg) = reg.to_real_reg() {
484 self.reg_fixed_nonallocatable(rreg.into());
485 } else {
486 debug_assert!(reg.is_virtual());
487 self.add_operand(reg, OperandConstraint::Reg, kind, pos);
488 }
489 }
490
491 fn reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, idx: usize) {
495 let reg = reg.reg.as_mut();
496 if let Some(rreg) = reg.to_real_reg() {
497 self.reg_fixed_nonallocatable(rreg.into());
502 } else {
503 debug_assert!(reg.is_virtual());
504 let constraint = OperandConstraint::Reuse(idx);
508 self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);
509 }
510 }
511
512 fn any_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
517 self.add_operand(
518 reg.reg.as_mut(),
519 OperandConstraint::Any,
520 OperandKind::Def,
521 OperandPos::Late,
522 );
523 }
524
525 fn any_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
528 self.add_operand(
529 reg.as_mut(),
530 OperandConstraint::Any,
531 OperandKind::Use,
532 OperandPos::Late,
533 );
534 }
535}
536
537impl<T: OperandVisitor> OperandVisitorImpl for T {}
538
539impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> {
540 fn add_operand(
541 &mut self,
542 reg: &mut Reg,
543 constraint: OperandConstraint,
544 kind: OperandKind,
545 pos: OperandPos,
546 ) {
547 debug_assert!(!reg.is_spillslot());
548 reg.0 = (self.renamer)(VReg::from(reg.0)).bits() as u32;
549 self.operands
550 .push(Operand::new(VReg::from(reg.0), constraint, kind, pos));
551 }
552
553 fn debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool) {
554 debug_assert_eq!(
555 self.allocatable.contains(reg),
556 expected,
557 "{reg:?} should{} be allocatable",
558 if expected { "" } else { " not" }
559 );
560 }
561
562 fn reg_clobbers(&mut self, regs: PRegSet) {
563 self.clobbers.union_from(regs);
564 }
565}
566
567impl<T: FnMut(&mut Reg, OperandConstraint, OperandKind, OperandPos)> OperandVisitor for T {
568 fn add_operand(
569 &mut self,
570 reg: &mut Reg,
571 constraint: OperandConstraint,
572 kind: OperandKind,
573 pos: OperandPos,
574 ) {
575 self(reg, constraint, kind, pos)
576 }
577}
578
579pub trait PrettyPrint {
585 fn pretty_print(&self, size_bytes: u8) -> String;
586
587 fn pretty_print_default(&self) -> String {
588 self.pretty_print(0)
589 }
590}