cranelift_codegen/machinst/
reg.rs1use alloc::{string::String, vec::Vec};
6use core::{fmt::Debug, hash::Hash};
7use regalloc2::{Allocation, Operand, OperandConstraint, PReg, PRegSet, VReg};
8
9#[cfg(feature = "enable-serde")]
10use serde::{Deserialize, Serialize};
11
12const PINNED_VREGS: usize = 128;
21
22pub fn pinned_vreg_to_preg(vreg: VReg) -> Option<PReg> {
24 if vreg.vreg() < PINNED_VREGS {
25 Some(PReg::from_index(vreg.vreg()))
26 } else {
27 None
28 }
29}
30
31pub fn first_user_vreg_index() -> usize {
34 PINNED_VREGS
39}
40
41#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
47#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
48pub struct Reg(VReg);
49
50impl Reg {
51 pub fn to_real_reg(self) -> Option<RealReg> {
54 if pinned_vreg_to_preg(self.0).is_some() {
55 Some(RealReg(self.0))
56 } else {
57 None
58 }
59 }
60
61 pub fn to_virtual_reg(self) -> Option<VirtualReg> {
64 if pinned_vreg_to_preg(self.0).is_none() {
65 Some(VirtualReg(self.0))
66 } else {
67 None
68 }
69 }
70
71 pub fn class(self) -> RegClass {
73 self.0.class()
74 }
75
76 pub fn is_real(self) -> bool {
78 self.to_real_reg().is_some()
79 }
80
81 pub fn is_virtual(self) -> bool {
83 self.to_virtual_reg().is_some()
84 }
85}
86
87impl std::fmt::Debug for Reg {
88 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
89 if let Some(rreg) = self.to_real_reg() {
90 let preg: PReg = rreg.into();
91 write!(f, "{}", preg)
92 } else if let Some(vreg) = self.to_virtual_reg() {
93 let vreg: VReg = vreg.into();
94 write!(f, "{}", vreg)
95 } else {
96 unreachable!()
97 }
98 }
99}
100
101#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
104#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
105pub struct RealReg(VReg);
106
107impl RealReg {
108 pub fn class(self) -> RegClass {
110 self.0.class()
111 }
112
113 pub fn hw_enc(self) -> u8 {
114 PReg::from(self).hw_enc() as u8
115 }
116}
117
118impl std::fmt::Debug for RealReg {
119 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
120 Reg::from(*self).fmt(f)
121 }
122}
123
124#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
130#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
131pub struct VirtualReg(VReg);
132
133impl VirtualReg {
134 pub fn class(self) -> RegClass {
136 self.0.class()
137 }
138
139 pub fn index(self) -> usize {
140 self.0.vreg()
141 }
142}
143
144impl std::fmt::Debug for VirtualReg {
145 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
146 Reg::from(*self).fmt(f)
147 }
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
160#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
161pub struct Writable<T: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash> {
162 reg: T,
163}
164
165impl<T: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash> Writable<T> {
166 pub fn from_reg(reg: T) -> Writable<T> {
171 Writable { reg }
172 }
173
174 pub fn to_reg(self) -> T {
176 self.reg
177 }
178
179 pub fn map<U, F>(self, f: F) -> Writable<U>
181 where
182 U: Clone + Copy + Debug + PartialEq + Eq + PartialOrd + Ord + Hash,
183 F: Fn(T) -> U,
184 {
185 Writable { reg: f(self.reg) }
186 }
187}
188
189impl std::convert::From<regalloc2::VReg> for Reg {
193 fn from(vreg: regalloc2::VReg) -> Reg {
194 Reg(vreg)
195 }
196}
197
198impl std::convert::From<regalloc2::VReg> for VirtualReg {
199 fn from(vreg: regalloc2::VReg) -> VirtualReg {
200 debug_assert!(pinned_vreg_to_preg(vreg).is_none());
201 VirtualReg(vreg)
202 }
203}
204
205impl std::convert::From<regalloc2::VReg> for RealReg {
206 fn from(vreg: regalloc2::VReg) -> RealReg {
207 debug_assert!(pinned_vreg_to_preg(vreg).is_some());
208 RealReg(vreg)
209 }
210}
211
212impl std::convert::From<Reg> for regalloc2::VReg {
213 fn from(reg: Reg) -> regalloc2::VReg {
217 reg.0
218 }
219}
220
221impl std::convert::From<VirtualReg> for regalloc2::VReg {
222 fn from(reg: VirtualReg) -> regalloc2::VReg {
223 reg.0
224 }
225}
226
227impl std::convert::From<RealReg> for regalloc2::VReg {
228 fn from(reg: RealReg) -> regalloc2::VReg {
229 reg.0
230 }
231}
232
233impl std::convert::From<RealReg> for regalloc2::PReg {
234 fn from(reg: RealReg) -> regalloc2::PReg {
235 PReg::from_index(reg.0.vreg())
236 }
237}
238
239impl std::convert::From<regalloc2::PReg> for RealReg {
240 fn from(preg: regalloc2::PReg) -> RealReg {
241 RealReg(VReg::new(preg.index(), preg.class()))
242 }
243}
244
245impl std::convert::From<regalloc2::PReg> for Reg {
246 fn from(preg: regalloc2::PReg) -> Reg {
247 Reg(VReg::new(preg.index(), preg.class()))
248 }
249}
250
251impl std::convert::From<RealReg> for Reg {
252 fn from(reg: RealReg) -> Reg {
253 Reg(reg.0)
254 }
255}
256
257impl std::convert::From<VirtualReg> for Reg {
258 fn from(reg: VirtualReg) -> Reg {
259 Reg(reg.0)
260 }
261}
262
263pub type SpillSlot = regalloc2::SpillSlot;
265
266pub type RegClass = regalloc2::RegClass;
282
283#[derive(Debug)]
288pub struct OperandCollector<'a, F: Fn(VReg) -> VReg> {
289 operands: &'a mut Vec<Operand>,
290 operands_start: usize,
291 clobbers: PRegSet,
292
293 allocatable: PRegSet,
295
296 renamer: F,
297}
298
299impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
300 pub fn new(operands: &'a mut Vec<Operand>, allocatable: PRegSet, renamer: F) -> Self {
302 let operands_start = operands.len();
303 Self {
304 operands,
305 operands_start,
306 clobbers: PRegSet::default(),
307 allocatable,
308 renamer,
309 }
310 }
311
312 pub fn no_reuse_def(&self) -> bool {
314 !self.operands[self.operands_start..]
315 .iter()
316 .any(|operand| match operand.constraint() {
317 OperandConstraint::Reuse(_) => true,
318 _ => false,
319 })
320 }
321
322 fn is_allocatable_preg(&self, reg: PReg) -> bool {
323 self.allocatable.contains(reg)
324 }
325
326 fn add_operand(&mut self, operand: Operand) {
328 let vreg = (self.renamer)(operand.vreg());
329 let operand = Operand::new(vreg, operand.constraint(), operand.kind(), operand.pos());
330 self.operands.push(operand);
331 }
332
333 pub fn finish(self) -> ((u32, u32), PRegSet) {
337 let start = self.operands_start as u32;
338 let end = self.operands.len() as u32;
339 ((start, end), self.clobbers)
340 }
341
342 pub fn reg_fixed_nonallocatable(&mut self, preg: PReg) {
344 debug_assert!(!self.is_allocatable_preg(preg));
345 self.add_operand(Operand::fixed_nonallocatable(preg))
346 }
347
348 pub fn reg_use(&mut self, reg: Reg) {
351 if let Some(rreg) = reg.to_real_reg() {
352 self.reg_fixed_nonallocatable(rreg.into());
353 } else {
354 debug_assert!(reg.is_virtual());
355 self.add_operand(Operand::reg_use(reg.into()));
356 }
357 }
358
359 pub fn reg_late_use(&mut self, reg: Reg) {
361 if let Some(rreg) = reg.to_real_reg() {
362 self.reg_fixed_nonallocatable(rreg.into());
363 } else {
364 debug_assert!(reg.is_virtual());
365 self.add_operand(Operand::reg_use_at_end(reg.into()));
366 }
367 }
368
369 pub fn reg_uses(&mut self, regs: &[Reg]) {
371 for ® in regs {
372 self.reg_use(reg);
373 }
374 }
375
376 pub fn reg_def(&mut self, reg: Writable<Reg>) {
380 if let Some(rreg) = reg.to_reg().to_real_reg() {
381 self.reg_fixed_nonallocatable(rreg.into());
382 } else {
383 debug_assert!(reg.to_reg().is_virtual());
384 self.add_operand(Operand::reg_def(reg.to_reg().into()));
385 }
386 }
387
388 pub fn reg_defs(&mut self, regs: &[Writable<Reg>]) {
390 for ® in regs {
391 self.reg_def(reg);
392 }
393 }
394
395 pub fn reg_early_def(&mut self, reg: Writable<Reg>) {
400 if let Some(rreg) = reg.to_reg().to_real_reg() {
401 self.reg_fixed_nonallocatable(rreg.into());
402 } else {
403 debug_assert!(reg.to_reg().is_virtual());
404 self.add_operand(Operand::reg_def_at_start(reg.to_reg().into()));
405 }
406 }
407
408 pub fn reg_fixed_use(&mut self, reg: Reg, rreg: Reg) {
411 debug_assert!(reg.is_virtual());
412 let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
413 debug_assert!(self.is_allocatable_preg(rreg.into()));
414 self.add_operand(Operand::reg_fixed_use(reg.into(), rreg.into()));
415 }
416
417 pub fn reg_fixed_def(&mut self, reg: Writable<Reg>, rreg: Reg) {
420 debug_assert!(reg.to_reg().is_virtual());
421 let rreg = rreg.to_real_reg().expect("fixed reg is not a RealReg");
422 debug_assert!(self.is_allocatable_preg(rreg.into()));
423 self.add_operand(Operand::reg_fixed_def(reg.to_reg().into(), rreg.into()));
424 }
425
426 pub fn reg_reuse_def(&mut self, reg: Writable<Reg>, idx: usize) {
430 if let Some(rreg) = reg.to_reg().to_real_reg() {
431 self.reg_fixed_nonallocatable(rreg.into());
436 } else {
437 self.add_operand(Operand::reg_reuse_def(reg.to_reg().into(), idx));
441 }
442 }
443
444 pub fn reg_clobbers(&mut self, regs: PRegSet) {
448 self.clobbers.union_from(regs);
449 }
450}
451
452pub trait PrettyPrint {
458 fn pretty_print(&self, size_bytes: u8, allocs: &mut AllocationConsumer<'_>) -> String;
459
460 fn pretty_print_default(&self) -> String {
461 self.pretty_print(0, &mut AllocationConsumer::new(&[]))
462 }
463}
464
465#[derive(Clone)]
478pub struct AllocationConsumer<'a> {
479 allocs: std::slice::Iter<'a, Allocation>,
480}
481
482impl<'a> AllocationConsumer<'a> {
483 pub fn new(allocs: &'a [Allocation]) -> Self {
484 Self {
485 allocs: allocs.iter(),
486 }
487 }
488
489 pub fn next_fixed_nonallocatable(&mut self, preg: PReg) {
490 let alloc = self.allocs.next();
491 let alloc = alloc.map(|alloc| {
492 Reg::from(
493 alloc
494 .as_reg()
495 .expect("Should not have gotten a stack allocation"),
496 )
497 });
498
499 assert_eq!(preg, alloc.unwrap().to_real_reg().unwrap().into());
500 }
501
502 pub fn next(&mut self, pre_regalloc_reg: Reg) -> Reg {
503 let alloc = self.allocs.next();
504 let alloc = alloc.map(|alloc| {
505 Reg::from(
506 alloc
507 .as_reg()
508 .expect("Should not have gotten a stack allocation"),
509 )
510 });
511
512 match (pre_regalloc_reg.to_real_reg(), alloc) {
513 (Some(rreg), None) => rreg.into(),
514 (Some(rreg), Some(alloc)) => {
515 debug_assert_eq!(Reg::from(rreg), alloc);
516 alloc
517 }
518 (None, Some(alloc)) => alloc,
519 _ => pre_regalloc_reg,
520 }
521 }
522
523 pub fn next_writable(&mut self, pre_regalloc_reg: Writable<Reg>) -> Writable<Reg> {
524 Writable::from_reg(self.next(pre_regalloc_reg.to_reg()))
525 }
526}
527
528impl<'a> std::default::Default for AllocationConsumer<'a> {
529 fn default() -> Self {
530 Self { allocs: [].iter() }
531 }
532}