xref: /wasmtime-44.0.1/winch/codegen/src/regset.rs (revision d36d4708)
1 use crate::isa::reg::{Reg, RegClass};
2 
3 /// A bit set to track register availability.
4 pub(crate) struct RegSet {
5     /// Bitset to track general purpose register availability.
6     gpr: RegBitSet,
7     /// Bitset to track floating-point register availability.
8     fpr: RegBitSet,
9 }
10 
11 use std::ops::{Index, IndexMut};
12 
13 impl Index<RegClass> for RegSet {
14     type Output = RegBitSet;
15 
index(&self, class: RegClass) -> &Self::Output16     fn index(&self, class: RegClass) -> &Self::Output {
17         match class {
18             RegClass::Int => &self.gpr,
19             RegClass::Float => &self.fpr,
20             c => unreachable!("Unexpected register class {:?}", c),
21         }
22     }
23 }
24 
25 impl IndexMut<RegClass> for RegSet {
index_mut(&mut self, class: RegClass) -> &mut Self::Output26     fn index_mut(&mut self, class: RegClass) -> &mut Self::Output {
27         match class {
28             RegClass::Int => &mut self.gpr,
29             RegClass::Float => &mut self.fpr,
30             c => unreachable!("Unexpected register class {:?}", c),
31         }
32     }
33 }
34 
35 /// Bitset for a particular register class.
36 pub struct RegBitSet {
37     /// The register class.
38     class: RegClass,
39     /// The set of allocatable
40     allocatable: u64,
41     /// The set of non-alloctable registers.
42     non_allocatable: u64,
43     /// The max number of registers.
44     /// Invariant:
45     /// When allocating or freeing a register the encoding (index) of the
46     /// register must be less than the max property.
47     max: usize,
48 }
49 
50 impl RegBitSet {
51     /// Creates an integer register class bitset.
int(allocatable: u64, non_allocatable: u64, max: usize) -> Self52     pub fn int(allocatable: u64, non_allocatable: u64, max: usize) -> Self {
53         // Assert that one set is the complement of the other.
54         debug_assert!(allocatable & non_allocatable == 0);
55         Self {
56             class: RegClass::Int,
57             allocatable,
58             non_allocatable,
59             max,
60         }
61     }
62 
63     /// Creates a float register class bitset.
float(allocatable: u64, non_allocatable: u64, max: usize) -> Self64     pub fn float(allocatable: u64, non_allocatable: u64, max: usize) -> Self {
65         // Assert that one set is the complement of the other.
66         debug_assert!(allocatable & non_allocatable == 0);
67         Self {
68             class: RegClass::Float,
69             allocatable,
70             non_allocatable,
71             max,
72         }
73     }
74 }
75 
76 impl RegSet {
77     /// Create a new register set.
new(gpr: RegBitSet, fpr: RegBitSet) -> Self78     pub fn new(gpr: RegBitSet, fpr: RegBitSet) -> Self {
79         debug_assert!(gpr.class == RegClass::Int);
80         debug_assert!(fpr.class == RegClass::Float);
81 
82         Self { gpr, fpr }
83     }
84 
85     /// Allocate the next available register of the given class,
86     /// returning `None` if there are no more registers available.
reg_for_class(&mut self, class: RegClass) -> Option<Reg>87     pub fn reg_for_class(&mut self, class: RegClass) -> Option<Reg> {
88         self.available(class).then(|| {
89             let bitset = &self[class];
90             let index = bitset.allocatable.trailing_zeros();
91             self.allocate(class, index.into());
92             Reg::from(class, index as usize)
93         })
94     }
95 
96     /// Request a specific register.
reg(&mut self, reg: Reg) -> Option<Reg>97     pub fn reg(&mut self, reg: Reg) -> Option<Reg> {
98         let index = reg.hw_enc();
99         self.named_reg_available(reg).then(|| {
100             self.allocate(reg.class(), index.try_into().unwrap());
101             reg
102         })
103     }
104 
105     /// Marks the specified register as available, utilizing the
106     /// register class to determine the bitset that requires updating.
free(&mut self, reg: Reg)107     pub fn free(&mut self, reg: Reg) {
108         let bitset = &self[reg.class()];
109         let index = reg.hw_enc();
110         assert!(index < bitset.max);
111         let index = u64::try_from(index).unwrap();
112         if !self.is_non_allocatable(reg.class(), index) {
113             self[reg.class()].allocatable |= 1 << index;
114         }
115     }
116 
117     /// Returns true if the specified register is allocatable.
named_reg_available(&self, reg: Reg) -> bool118     pub fn named_reg_available(&self, reg: Reg) -> bool {
119         let bitset = &self[reg.class()];
120         assert!(reg.hw_enc() < bitset.max);
121         let index = 1 << reg.hw_enc();
122 
123         (!bitset.allocatable & index) == 0
124             || self.is_non_allocatable(reg.class(), reg.hw_enc().try_into().unwrap())
125     }
126 
available(&self, class: RegClass) -> bool127     fn available(&self, class: RegClass) -> bool {
128         let bitset = &self[class];
129         bitset.allocatable != 0
130     }
131 
allocate(&mut self, class: RegClass, index: u64)132     fn allocate(&mut self, class: RegClass, index: u64) {
133         if !self.is_non_allocatable(class, index) {
134             self[class].allocatable &= !(1 << index);
135         }
136     }
137 
is_non_allocatable(&self, class: RegClass, index: u64) -> bool138     fn is_non_allocatable(&self, class: RegClass, index: u64) -> bool {
139         let bitset = &self[class];
140         let non_allocatable = bitset.non_allocatable;
141         non_allocatable != 0 && !non_allocatable & (1 << index) == 0
142     }
143 }
144 
145 #[cfg(test)]
146 mod tests {
147     use super::{Reg, RegBitSet, RegClass, RegSet};
148 
149     const UNIVERSE: u64 = (1 << 16) - 1;
150     const MAX: usize = 16;
151 
152     #[test]
test_any_gpr()153     fn test_any_gpr() {
154         let bitset = RegBitSet::int(UNIVERSE, !UNIVERSE, MAX);
155         let zero = RegBitSet::float(0, 0, MAX);
156         let mut set = RegSet::new(bitset, zero);
157         for _ in 0..16 {
158             let gpr = set.reg_for_class(RegClass::Int);
159             assert!(gpr.is_some())
160         }
161 
162         assert!(!set.available(RegClass::Int));
163         assert!(set.reg_for_class(RegClass::Int).is_none())
164     }
165 
166     #[test]
test_gpr()167     fn test_gpr() {
168         let non_allocatable: u64 = 1 << 5;
169         let all = UNIVERSE & !non_allocatable;
170         let non_alloc = Reg::int(5);
171         let alloc = Reg::int(2);
172         let bitset = RegBitSet::int(all, non_allocatable, MAX);
173         let zero = RegBitSet::float(0, 0, MAX);
174         let mut set = RegSet::new(bitset, zero);
175         // Requesting a non alloctable register returns the register
176         // and doesn't allocate it.
177         assert!(set.reg(non_alloc).is_some());
178         assert!(set.reg(non_alloc).is_some());
179         // Requesting an allocatable register twice returns none the
180         // second time.
181         assert!(set.reg(alloc).is_some());
182         assert!(set.reg(alloc).is_none());
183     }
184 
185     #[test]
test_free_reg()186     fn test_free_reg() {
187         let set = RegBitSet::int(UNIVERSE, !UNIVERSE, MAX);
188         let zero = RegBitSet::float(0, 0, MAX);
189         let mut set = RegSet::new(set, zero);
190         let gpr = set.reg_for_class(RegClass::Int).unwrap();
191         set.free(gpr);
192         assert!(set.reg(gpr).is_some());
193     }
194 }
195