/src/wasmtime/winch/codegen/src/regset.rs
Line | Count | Source |
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 | | |
16 | 14.1M | fn index(&self, class: RegClass) -> &Self::Output { |
17 | 14.1M | match class { |
18 | 10.6M | RegClass::Int => &self.gpr, |
19 | 3.47M | RegClass::Float => &self.fpr, |
20 | 0 | c => unreachable!("Unexpected register class {:?}", c), |
21 | | } |
22 | 14.1M | } |
23 | | } |
24 | | |
25 | | impl IndexMut<RegClass> for RegSet { |
26 | 5.42M | fn index_mut(&mut self, class: RegClass) -> &mut Self::Output { |
27 | 5.42M | match class { |
28 | 4.02M | RegClass::Int => &mut self.gpr, |
29 | 1.40M | RegClass::Float => &mut self.fpr, |
30 | 0 | c => unreachable!("Unexpected register class {:?}", c), |
31 | | } |
32 | 5.42M | } |
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. |
52 | 77.3k | pub fn int(allocatable: u64, non_allocatable: u64, max: usize) -> Self { |
53 | | // Assert that one set is the complement of the other. |
54 | 77.3k | debug_assert!(allocatable & non_allocatable == 0); |
55 | 77.3k | Self { |
56 | 77.3k | class: RegClass::Int, |
57 | 77.3k | allocatable, |
58 | 77.3k | non_allocatable, |
59 | 77.3k | max, |
60 | 77.3k | } |
61 | 77.3k | } |
62 | | |
63 | | /// Creates a float register class bitset. |
64 | 77.3k | pub fn float(allocatable: u64, non_allocatable: u64, max: usize) -> Self { |
65 | | // Assert that one set is the complement of the other. |
66 | 77.3k | debug_assert!(allocatable & non_allocatable == 0); |
67 | 77.3k | Self { |
68 | 77.3k | class: RegClass::Float, |
69 | 77.3k | allocatable, |
70 | 77.3k | non_allocatable, |
71 | 77.3k | max, |
72 | 77.3k | } |
73 | 77.3k | } |
74 | | } |
75 | | |
76 | | impl RegSet { |
77 | | /// Create a new register set. |
78 | 77.3k | pub fn new(gpr: RegBitSet, fpr: RegBitSet) -> Self { |
79 | 77.3k | debug_assert!(gpr.class == RegClass::Int); |
80 | 77.3k | debug_assert!(fpr.class == RegClass::Float); |
81 | | |
82 | 77.3k | Self { gpr, fpr } |
83 | 77.3k | } |
84 | | |
85 | | /// Allocate the next available register of the given class, |
86 | | /// returning `None` if there are no more registers available. |
87 | 2.36M | pub fn reg_for_class(&mut self, class: RegClass) -> Option<Reg> { |
88 | 2.36M | self.available(class).then(|| { |
89 | 2.36M | let bitset = &self[class]; |
90 | 2.36M | let index = bitset.allocatable.trailing_zeros(); |
91 | 2.36M | self.allocate(class, index.into()); |
92 | 2.36M | Reg::from(class, index as usize) |
93 | 2.36M | }) |
94 | 2.36M | } |
95 | | |
96 | | /// Request a specific register. |
97 | 355k | pub fn reg(&mut self, reg: Reg) -> Option<Reg> { |
98 | 355k | let index = reg.hw_enc(); |
99 | 355k | self.named_reg_available(reg).then(|| { |
100 | 349k | self.allocate(reg.class(), index.try_into().unwrap()); |
101 | 349k | reg |
102 | 349k | }) |
103 | 355k | } |
104 | | |
105 | | /// Marks the specified register as available, utilizing the |
106 | | /// register class to determine the bitset that requires updating. |
107 | 3.08M | pub fn free(&mut self, reg: Reg) { |
108 | 3.08M | let bitset = &self[reg.class()]; |
109 | 3.08M | let index = reg.hw_enc(); |
110 | 3.08M | assert!(index < bitset.max); |
111 | 3.08M | let index = u64::try_from(index).unwrap(); |
112 | 3.08M | if !self.is_non_allocatable(reg.class(), index) { |
113 | 2.71M | self[reg.class()].allocatable |= 1 << index; |
114 | 2.71M | } |
115 | 3.08M | } |
116 | | |
117 | | /// Returns true if the specified register is allocatable. |
118 | 498k | pub fn named_reg_available(&self, reg: Reg) -> bool { |
119 | 498k | let bitset = &self[reg.class()]; |
120 | 498k | assert!(reg.hw_enc() < bitset.max); |
121 | 498k | let index = 1 << reg.hw_enc(); |
122 | | |
123 | 498k | (!bitset.allocatable & index) == 0 |
124 | 5.91k | || self.is_non_allocatable(reg.class(), reg.hw_enc().try_into().unwrap()) |
125 | 498k | } |
126 | | |
127 | 2.36M | fn available(&self, class: RegClass) -> bool { |
128 | 2.36M | let bitset = &self[class]; |
129 | 2.36M | bitset.allocatable != 0 |
130 | 2.36M | } |
131 | | |
132 | 2.71M | fn allocate(&mut self, class: RegClass, index: u64) { |
133 | 2.71M | if !self.is_non_allocatable(class, index) { |
134 | 2.71M | self[class].allocatable &= !(1 << index); |
135 | 2.71M | } |
136 | 2.71M | } |
137 | | |
138 | 5.79M | fn is_non_allocatable(&self, class: RegClass, index: u64) -> bool { |
139 | 5.79M | let bitset = &self[class]; |
140 | 5.79M | let non_allocatable = bitset.non_allocatable; |
141 | 5.79M | non_allocatable != 0 && !non_allocatable & (1 << index) == 0 |
142 | 5.79M | } |
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] |
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] |
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] |
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 | | } |