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