winch_codegen/
regset.rs

1use crate::isa::reg::{Reg, RegClass};
2
3/// A bit set to track register availability.
4pub(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
11use std::ops::{Index, IndexMut};
12
13impl Index<RegClass> for RegSet {
14    type Output = RegBitSet;
15
16    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
25impl IndexMut<RegClass> for RegSet {
26    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.
36pub 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
50impl RegBitSet {
51    /// Creates an integer register class bitset.
52    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.
64    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
76impl RegSet {
77    /// Create a new register set.
78    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.
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.
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.
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.
118    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
127    fn available(&self, class: RegClass) -> bool {
128        let bitset = &self[class];
129        bitset.allocatable != 0
130    }
131
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
138    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)]
146mod 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}