cranelift_codegen/machinst/
reg.rs1use 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 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 Reg(vreg.bits() as u32)
58 }
59
60 pub const fn from_real_reg(preg: regalloc2::PReg) -> Reg {
62 Reg(preg_to_pinned_vreg(preg).bits() as u32)
63 }
64
65 pub fn to_real_reg(self) -> Option<RealReg> {
68 pinned_vreg_to_preg(self.0.into()).map(RealReg)
69 }
70
71 pub fn to_virtual_reg(self) -> Option<VirtualReg> {
74 if self.to_spillslot().is_some() {
75 None
76 } else if pinned_vreg_to_preg(self.0.into()).is_none() {
77 Some(VirtualReg(self.0.into()))
78 } else {
79 None
80 }
81 }
82
83 pub fn to_spillslot(self) -> Option<SpillSlot> {
85 if (self.0 & REG_SPILLSLOT_BIT) != 0 {
86 Some(SpillSlot::new((self.0 & REG_SPILLSLOT_MASK) as usize))
87 } else {
88 None
89 }
90 }
91
92 pub fn class(self) -> RegClass {
94 assert!(!self.to_spillslot().is_some());
95 VReg::from(self.0).class()
96 }
97
98 pub fn is_real(self) -> bool {
100 self.to_real_reg().is_some()
101 }
102
103 pub fn is_virtual(self) -> bool {
105 self.to_virtual_reg().is_some()
106 }
107
108 pub fn is_spillslot(self) -> bool {
110 self.to_spillslot().is_some()
111 }
112}
113
114impl std::fmt::Debug for Reg {
115 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116 if VReg::from(self.0) == VReg::invalid() {
117 write!(f, "<invalid>")
118 } else if let Some(spillslot) = self.to_spillslot() {
119 write!(f, "{spillslot}")
120 } else if let Some(rreg) = self.to_real_reg() {
121 let preg: PReg = rreg.into();
122 write!(f, "{preg}")
123 } else if let Some(vreg) = self.to_virtual_reg() {
124 let vreg: VReg = vreg.into();
125 write!(f, "{vreg}")
126 } else {
127 unreachable!()
128 }
129 }
130}
131
132impl AsMut<Reg> for Reg {
133 fn as_mut(&mut self) -> &mut Reg {
134 self
135 }
136}
137
138#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
141#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
142pub struct RealReg(PReg);
143
144impl RealReg {
145 pub fn class(self) -> RegClass {
147 self.0.class()
148 }
149
150 pub fn hw_enc(self) -> u8 {
152 self.0.hw_enc() as u8
153 }
154}
155
156impl std::fmt::Debug for RealReg {
157 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
158 Reg::from(*self).fmt(f)
159 }
160}
161
162#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
168#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
169pub struct VirtualReg(VReg);
170
171impl VirtualReg {
172 pub fn class(self) -> RegClass {
174 self.0.class()
175 }
176
177 pub fn index(self) -> usize {
178 self.0.vreg()
179 }
180}
181
182impl std::fmt::Debug for VirtualReg {
183 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
184 Reg::from(*self).fmt(f)
185 }
186}
187
188#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
198#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
199pub struct Writable<T> {
200 reg: T,
201}
202
203impl<T> Writable<T> {
204 pub fn from_reg(reg: T) -> Writable<T> {
209 Writable { reg }
210 }
211
212 pub fn to_reg(self) -> T {
214 self.reg
215 }
216
217 pub fn reg_mut(&mut self) -> &mut T {
219 &mut self.reg
220 }
221
222 pub fn map<U>(self, f: impl Fn(T) -> U) -> Writable<U> {
224 Writable { reg: f(self.reg) }
225 }
226}
227
228impl<R: cranelift_assembler_x64::AsReg> cranelift_assembler_x64::AsReg for Writable<R> {
230 fn enc(&self) -> u8 {
231 self.reg.enc()
232 }
233
234 fn to_string(&self, size: Option<cranelift_assembler_x64::gpr::Size>) -> String {
235 self.reg.to_string(size)
236 }
237
238 fn new(_: u8) -> Self {
239 panic!("disallow creation of new assembler registers")
240 }
241}
242
243impl std::convert::From<regalloc2::VReg> for Reg {
247 fn from(vreg: regalloc2::VReg) -> Reg {
248 Reg(vreg.bits() as u32)
249 }
250}
251
252impl std::convert::From<regalloc2::VReg> for VirtualReg {
253 fn from(vreg: regalloc2::VReg) -> VirtualReg {
254 debug_assert!(pinned_vreg_to_preg(vreg).is_none());
255 VirtualReg(vreg)
256 }
257}
258
259impl std::convert::From<Reg> for regalloc2::VReg {
260 fn from(reg: Reg) -> regalloc2::VReg {
264 reg.0.into()
265 }
266}
267impl std::convert::From<&Reg> for regalloc2::VReg {
268 fn from(reg: &Reg) -> regalloc2::VReg {
269 reg.0.into()
270 }
271}
272
273impl std::convert::From<VirtualReg> for regalloc2::VReg {
274 fn from(reg: VirtualReg) -> regalloc2::VReg {
275 reg.0
276 }
277}
278
279impl std::convert::From<RealReg> for regalloc2::VReg {
280 fn from(reg: RealReg) -> regalloc2::VReg {
281 VReg::new(reg.0.index(), reg.0.class())
284 }
285}
286
287impl std::convert::From<RealReg> for regalloc2::PReg {
288 fn from(reg: RealReg) -> regalloc2::PReg {
289 reg.0
290 }
291}
292
293impl std::convert::From<regalloc2::PReg> for RealReg {
294 fn from(preg: regalloc2::PReg) -> RealReg {
295 RealReg(preg)
296 }
297}
298
299impl std::convert::From<regalloc2::PReg> for Reg {
300 fn from(preg: regalloc2::PReg) -> Reg {
301 RealReg(preg).into()
302 }
303}
304
305impl std::convert::From<RealReg> for Reg {
306 fn from(reg: RealReg) -> Reg {
307 Reg(VReg::from(reg).bits() as u32)
308 }
309}
310
311impl std::convert::From<VirtualReg> for Reg {
312 fn from(reg: VirtualReg) -> Reg {
313 Reg(reg.0.bits() as u32)
314 }
315}
316
317pub type SpillSlot = regalloc2::SpillSlot;
319
320impl std::convert::From<regalloc2::SpillSlot> for Reg {
321 fn from(spillslot: regalloc2::SpillSlot) -> Reg {
322 Reg(REG_SPILLSLOT_BIT | spillslot.index() as u32)
323 }
324}
325
326pub type RegClass = regalloc2::RegClass;
342
343#[derive(Debug)]
348pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {
349 operands: &'a mut Vec<Operand>,
350 clobbers: PRegSet,
351
352 allocatable: PRegSet,
354
355 renamer: F,
356}
357
358impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
359 pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self {
361 Self {
362 operands,
363 clobbers: PRegSet::default(),
364 allocatable,
365 renamer,
366 }
367 }
368
369 pub fn finish(self) -> (usize, PRegSet) {
373 let end = self.operands.len();
374 (end, self.clobbers)
375 }
376}
377
378pub trait OperandVisitor {
379 fn add_operand(
380 &mut self,
381 reg: &mut Reg,
382 constraint: OperandConstraint,
383 kind: OperandKind,
384 pos: OperandPos,
385 );
386
387 fn debug_assert_is_allocatable_preg(&self, _reg: PReg, _expected: bool) {}
388
389 fn reg_clobbers(&mut self, _regs: PRegSet) {}
393}
394
395pub trait OperandVisitorImpl: OperandVisitor {
396 fn reg_fixed_nonallocatable(&mut self, preg: PReg) {
398 self.debug_assert_is_allocatable_preg(preg, false);
399 }
402
403 fn reg_use(&mut self, reg: &mut impl AsMut<Reg>) {
406 self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Early);
407 }
408
409 fn reg_late_use(&mut self, reg: &mut impl AsMut<Reg>) {
411 self.reg_maybe_fixed(reg.as_mut(), OperandKind::Use, OperandPos::Late);
412 }
413
414 fn reg_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
418 self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Late);
419 }
420
421 fn reg_early_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
426 self.reg_maybe_fixed(reg.reg.as_mut(), OperandKind::Def, OperandPos::Early);
427 }
428
429 fn reg_fixed_late_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
432 self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Late);
433 }
434
435 fn reg_fixed_use(&mut self, reg: &mut impl AsMut<Reg>, rreg: Reg) {
438 self.reg_fixed(reg.as_mut(), rreg, OperandKind::Use, OperandPos::Early);
439 }
440
441 fn reg_fixed_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, rreg: Reg) {
444 self.reg_fixed(reg.reg.as_mut(), rreg, OperandKind::Def, OperandPos::Late);
445 }
446
447 fn reg_fixed(&mut self, reg: &mut Reg, rreg: Reg, kind: OperandKind, pos: OperandPos) {
449 debug_assert!(reg.is_virtual());
450 let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
451 self.debug_assert_is_allocatable_preg(rreg.into(), true);
452 let constraint = OperandConstraint::FixedReg(rreg.into());
453 self.add_operand(reg, constraint, kind, pos);
454 }
455
456 fn reg_maybe_fixed(&mut self, reg: &mut Reg, kind: OperandKind, pos: OperandPos) {
458 if let Some(rreg) = reg.to_real_reg() {
459 self.reg_fixed_nonallocatable(rreg.into());
460 } else {
461 debug_assert!(reg.is_virtual());
462 self.add_operand(reg, OperandConstraint::Reg, kind, pos);
463 }
464 }
465
466 fn reg_reuse_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>, idx: usize) {
470 let reg = reg.reg.as_mut();
471 if let Some(rreg) = reg.to_real_reg() {
472 self.reg_fixed_nonallocatable(rreg.into());
477 } else {
478 debug_assert!(reg.is_virtual());
479 let constraint = OperandConstraint::Reuse(idx);
483 self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);
484 }
485 }
486
487 fn any_def(&mut self, reg: &mut Writable<impl AsMut<Reg>>) {
492 self.add_operand(
493 reg.reg.as_mut(),
494 OperandConstraint::Any,
495 OperandKind::Def,
496 OperandPos::Late,
497 );
498 }
499}
500
501impl<T: OperandVisitor> OperandVisitorImpl for T {}
502
503impl<'a, F: Fn(VReg) -> VReg> OperandVisitor for OperandCollector<'a, F> {
504 fn add_operand(
505 &mut self,
506 reg: &mut Reg,
507 constraint: OperandConstraint,
508 kind: OperandKind,
509 pos: OperandPos,
510 ) {
511 debug_assert!(!reg.is_spillslot());
512 reg.0 = (self.renamer)(VReg::from(reg.0)).bits() as u32;
513 self.operands
514 .push(Operand::new(VReg::from(reg.0), constraint, kind, pos));
515 }
516
517 fn debug_assert_is_allocatable_preg(&self, reg: PReg, expected: bool) {
518 debug_assert_eq!(
519 self.allocatable.contains(reg),
520 expected,
521 "{reg:?} should{} be allocatable",
522 if expected { "" } else { " not" }
523 );
524 }
525
526 fn reg_clobbers(&mut self, regs: PRegSet) {
527 self.clobbers.union_from(regs);
528 }
529}
530
531impl<T: FnMut(&mut Reg, OperandConstraint, OperandKind, OperandPos)> OperandVisitor for T {
532 fn add_operand(
533 &mut self,
534 reg: &mut Reg,
535 constraint: OperandConstraint,
536 kind: OperandKind,
537 pos: OperandPos,
538 ) {
539 self(reg, constraint, kind, pos)
540 }
541}
542
543pub trait PrettyPrint {
549 fn pretty_print(&self, size_bytes: u8) -> String;
550
551 fn pretty_print_default(&self) -> String {
552 self.pretty_print(0)
553 }
554}