Skip to main content

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}