cranelift_codegen/isa/registers.rs
1//! Data structures describing the registers in an ISA.
2
3use crate::entity::EntityRef;
4use core::fmt;
5
6/// Register units are the smallest units of register allocation.
7///
8/// Normally there is a 1-1 correspondence between registers and register units, but when an ISA
9/// has aliasing registers, the aliasing can be modeled with registers that cover multiple
10/// register units.
11///
12/// The register allocator will enforce that each register unit only gets used for one thing.
13pub type RegUnit = u16;
14
15/// A bit mask indexed by register classes.
16///
17/// The size of this type is determined by the ISA with the most register classes.
18pub type RegClassMask = u32;
19
20/// A bit mask indexed by register units.
21///
22/// The size of this type is determined by the target ISA that has the most register units defined.
23/// Currently that is arm32 which has 64+16 units.
24pub type RegUnitMask = [RegClassMask; 3];
25
26/// The register units in a target ISA are divided into disjoint register banks. Each bank covers a
27/// contiguous range of register units.
28///
29/// The `RegBank` struct provides a static description of a register bank.
30pub struct RegBank {
31 /// The name of this register bank as defined in the ISA's DSL definition.
32 pub name: &'static str,
33
34 /// The first register unit in this bank.
35 pub first_unit: RegUnit,
36
37 /// The total number of register units in this bank.
38 pub units: RegUnit,
39
40 /// Array of specially named register units. This array can be shorter than the number of units
41 /// in the bank.
42 pub names: &'static [&'static str],
43
44 /// Name prefix to use for those register units in the bank not covered by the `names` array.
45 /// The remaining register units will be named this prefix followed by their decimal offset in
46 /// the bank. So with a prefix `r`, registers will be named `r8`, `r9`, ...
47 pub prefix: &'static str,
48
49 /// Index of the first top-level register class in this bank.
50 pub first_toprc: usize,
51
52 /// Number of top-level register classes in this bank.
53 ///
54 /// The top-level register classes in a bank are guaranteed to be numbered sequentially from
55 /// `first_toprc`, and all top-level register classes across banks come before any sub-classes.
56 pub num_toprcs: usize,
57
58 /// Is register pressure tracking enabled for this bank?
59 pub pressure_tracking: bool,
60}
61
62impl RegBank {
63 /// Does this bank contain `regunit`?
64 fn contains(&self, regunit: RegUnit) -> bool {
65 regunit >= self.first_unit && regunit - self.first_unit < self.units
66 }
67
68 /// Try to parse a regunit name. The name is not expected to begin with `%`.
69 fn parse_regunit(&self, name: &str) -> Option<RegUnit> {
70 match self.names.iter().position(|&x| x == name) {
71 Some(offset) => {
72 // This is one of the special-cased names.
73 Some(offset as RegUnit)
74 }
75 None => {
76 // Try a regular prefixed name.
77 if name.starts_with(self.prefix) {
78 name[self.prefix.len()..].parse().ok()
79 } else {
80 None
81 }
82 }
83 }
84 .and_then(|offset| {
85 if offset < self.units {
86 Some(offset + self.first_unit)
87 } else {
88 None
89 }
90 })
91 }
92
93 /// Write `regunit` to `w`, assuming that it belongs to this bank.
94 /// All regunits are written with a `%` prefix.
95 fn write_regunit(&self, f: &mut fmt::Formatter, regunit: RegUnit) -> fmt::Result {
96 let offset = regunit - self.first_unit;
97 assert!(offset < self.units);
98 if (offset as usize) < self.names.len() {
99 write!(f, "%{}", self.names[offset as usize])
100 } else {
101 write!(f, "%{}{}", self.prefix, offset)
102 }
103 }
104}
105
106/// A register class reference.
107///
108/// All register classes are statically defined in tables generated from the meta descriptions.
109pub type RegClass = &'static RegClassData;
110
111/// Data about a register class.
112///
113/// A register class represents a subset of the registers in a bank. It describes the set of
114/// permitted registers for a register operand in a given encoding of an instruction.
115///
116/// A register class can be a subset of another register class. The top-level register classes are
117/// disjoint.
118pub struct RegClassData {
119 /// The name of the register class.
120 pub name: &'static str,
121
122 /// The index of this class in the ISA's RegInfo description.
123 pub index: u8,
124
125 /// How many register units to allocate per register.
126 pub width: u8,
127
128 /// Index of the register bank this class belongs to.
129 pub bank: u8,
130
131 /// Index of the top-level register class contains this one.
132 pub toprc: u8,
133
134 /// The first register unit in this class.
135 pub first: RegUnit,
136
137 /// Bit-mask of sub-classes of this register class, including itself.
138 ///
139 /// Bits correspond to RC indexes.
140 pub subclasses: RegClassMask,
141
142 /// Mask of register units in the class. If `width > 1`, the mask only has a bit set for the
143 /// first register unit in each allocatable register.
144 pub mask: RegUnitMask,
145
146 /// The global `RegInfo` instance containing this register class.
147 pub info: &'static RegInfo,
148
149 /// The "pinned" register of the associated register bank.
150 ///
151 /// This register must be non-volatile (callee-preserved) and must not be the fixed
152 /// output register of any instruction.
153 pub pinned_reg: Option<RegUnit>,
154}
155
156impl RegClassData {
157 /// Get the register class index corresponding to the intersection of `self` and `other`.
158 ///
159 /// This register class is guaranteed to exist if the register classes overlap. If the register
160 /// classes don't overlap, returns `None`.
161 pub fn intersect_index(&self, other: RegClass) -> Option<RegClassIndex> {
162 // Compute the set of common subclasses.
163 let mask = self.subclasses & other.subclasses;
164
165 if mask == 0 {
166 // No overlap.
167 None
168 } else {
169 // Register class indexes are topologically ordered, so the largest common subclass has
170 // the smallest index.
171 Some(RegClassIndex(mask.trailing_zeros() as u8))
172 }
173 }
174
175 /// Get the intersection of `self` and `other`.
176 pub fn intersect(&self, other: RegClass) -> Option<RegClass> {
177 self.intersect_index(other).map(|rci| self.info.rc(rci))
178 }
179
180 /// Returns true if `other` is a subclass of this register class.
181 /// A register class is considered to be a subclass of itself.
182 pub fn has_subclass<RCI: Into<RegClassIndex>>(&self, other: RCI) -> bool {
183 self.subclasses & (1 << other.into().0) != 0
184 }
185
186 /// Get the top-level register class containing this class.
187 pub fn toprc(&self) -> RegClass {
188 self.info.rc(RegClassIndex(self.toprc))
189 }
190
191 /// Get a specific register unit in this class.
192 pub fn unit(&self, offset: usize) -> RegUnit {
193 let uoffset = offset * usize::from(self.width);
194 self.first + uoffset as RegUnit
195 }
196
197 /// Does this register class contain `regunit`?
198 pub fn contains(&self, regunit: RegUnit) -> bool {
199 self.mask[(regunit / 32) as usize] & (1u32 << (regunit % 32)) != 0
200 }
201
202 /// If the pinned register is used, is the given regunit the pinned register of this class?
203 #[inline]
204 pub fn is_pinned_reg(&self, enabled: bool, regunit: RegUnit) -> bool {
205 enabled
206 && self
207 .pinned_reg
208 .map_or(false, |pinned_reg| pinned_reg == regunit)
209 }
210}
211
212impl fmt::Display for RegClassData {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 f.write_str(self.name)
215 }
216}
217
218impl fmt::Debug for RegClassData {
219 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
220 f.write_str(self.name)
221 }
222}
223
224/// Within an ISA, register classes are uniquely identified by their index.
225impl PartialEq for RegClassData {
226 fn eq(&self, other: &Self) -> bool {
227 self.index == other.index
228 }
229}
230
231/// A small reference to a register class.
232///
233/// Use this when storing register classes in compact data structures. The `RegInfo::rc()` method
234/// can be used to get the real register class reference back.
235#[derive(Clone, Copy, Debug, PartialEq, Eq)]
236pub struct RegClassIndex(u8);
237
238impl EntityRef for RegClassIndex {
239 fn new(idx: usize) -> Self {
240 Self(idx as u8)
241 }
242
243 fn index(self) -> usize {
244 usize::from(self.0)
245 }
246}
247
248impl From<RegClass> for RegClassIndex {
249 fn from(rc: RegClass) -> Self {
250 Self(rc.index)
251 }
252}
253
254impl fmt::Display for RegClassIndex {
255 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
256 write!(f, "rci{}", self.0)
257 }
258}
259
260/// Test of two registers overlap.
261///
262/// A register is identified as a `(RegClass, RegUnit)` pair. The register class is needed to
263/// determine the width (in regunits) of the register.
264pub fn regs_overlap(rc1: RegClass, reg1: RegUnit, rc2: RegClass, reg2: RegUnit) -> bool {
265 let end1 = reg1 + RegUnit::from(rc1.width);
266 let end2 = reg2 + RegUnit::from(rc2.width);
267 !(end1 <= reg2 || end2 <= reg1)
268}
269
270/// Information about the registers in an ISA.
271///
272/// The `RegUnit` data structure collects all relevant static information about the registers in an
273/// ISA.
274#[derive(Clone)]
275pub struct RegInfo {
276 /// All register banks, ordered by their `first_unit`. The register banks are disjoint, but
277 /// there may be holes of unused register unit numbers between banks due to alignment.
278 pub banks: &'static [RegBank],
279
280 /// All register classes ordered topologically so a sub-class always follows its parent.
281 pub classes: &'static [RegClass],
282}
283
284impl RegInfo {
285 /// Get the register bank holding `regunit`.
286 pub fn bank_containing_regunit(&self, regunit: RegUnit) -> Option<&RegBank> {
287 // We could do a binary search, but most ISAs have only two register banks...
288 self.banks.iter().find(|b| b.contains(regunit))
289 }
290
291 /// Try to parse a regunit name. The name is not expected to begin with `%`.
292 pub fn parse_regunit(&self, name: &str) -> Option<RegUnit> {
293 self.banks
294 .iter()
295 .filter_map(|b| b.parse_regunit(name))
296 .next()
297 }
298
299 /// Make a temporary object that can display a register unit.
300 pub fn display_regunit(&self, regunit: RegUnit) -> DisplayRegUnit {
301 DisplayRegUnit {
302 regunit,
303 reginfo: self,
304 }
305 }
306
307 /// Get the register class corresponding to `idx`.
308 pub fn rc(&self, idx: RegClassIndex) -> RegClass {
309 self.classes[idx.index()]
310 }
311
312 /// Get the top-level register class containing the `idx` class.
313 pub fn toprc(&self, idx: RegClassIndex) -> RegClass {
314 self.classes[self.rc(idx).toprc as usize]
315 }
316}
317
318/// Temporary object that holds enough information to print a register unit.
319pub struct DisplayRegUnit<'a> {
320 regunit: RegUnit,
321 reginfo: &'a RegInfo,
322}
323
324impl<'a> fmt::Display for DisplayRegUnit<'a> {
325 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
326 match self.reginfo.bank_containing_regunit(self.regunit) {
327 Some(b) => b.write_regunit(f, self.regunit),
328 None => write!(f, "%INVALID{}", self.regunit),
329 }
330 }
331}
332
333#[test]
334fn assert_sizes() {
335 use cranelift_codegen_shared::constants;
336 use std::mem::size_of;
337
338 // In these tests, size_of returns number of bytes: we actually want the number of bits, so
339 // multiply these by 8.
340 assert!(
341 (size_of::<RegClassMask>() * 8) <= constants::MAX_NUM_REG_CLASSES,
342 "need to bump MAX_NUM_REG_CLASSES or change RegClassMask type"
343 );
344
345 assert!(
346 constants::MAX_NUM_REG_CLASSES < (1 << (size_of::<RegClassIndex>() * 8)),
347 "need to change RegClassIndex's type to a wider type"
348 );
349}