From 3b83496edbd2918f5eb05e0c741c830e1d209d96 Mon Sep 17 00:00:00 2001 From: Jakob Stoklund Olesen Date: Fri, 20 Jan 2017 14:41:06 -0800 Subject: [PATCH] Add an AllocatableSet for registers. This set of available register units also manages register aliasing in an efficient way. Detect if the units in a register straddles mask words. The algorithm for allocating multi-unit registers expect the whole register to be inside a single mask word. We could handle this if necessary, but so far no ISAs need it. --- lib/cretonne/meta/cdsl/registers.py | 7 +- lib/cretonne/src/isa/mod.rs | 4 +- lib/cretonne/src/isa/registers.rs | 4 +- lib/cretonne/src/regalloc/allocatable_set.rs | 176 +++++++++++++++++++ lib/cretonne/src/regalloc/mod.rs | 1 + 5 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 lib/cretonne/src/regalloc/allocatable_set.rs diff --git a/lib/cretonne/meta/cdsl/registers.py b/lib/cretonne/meta/cdsl/registers.py index b27c6128ec..dc6885c296 100644 --- a/lib/cretonne/meta/cdsl/registers.py +++ b/lib/cretonne/meta/cdsl/registers.py @@ -153,7 +153,12 @@ class RegClass(object): start = self.bank.first_unit + self.start for a in range(self.count): u = start + a * self.width - mask[u // 32] |= 1 << (u % 32) + b = u % 32 + # We need fancier masking code if a register can straddle mask + # words. This will only happen with widths that are not powers of + # two. + assert b + self.width <= 32, 'Register straddles words' + mask[u // 32] |= 1 << b return mask diff --git a/lib/cretonne/src/isa/mod.rs b/lib/cretonne/src/isa/mod.rs index dc66355904..6d22d5dd71 100644 --- a/lib/cretonne/src/isa/mod.rs +++ b/lib/cretonne/src/isa/mod.rs @@ -41,7 +41,7 @@ //! concurrent function compilations. pub use isa::encoding::Encoding; -pub use isa::registers::{RegUnit, RegBank, RegInfo}; +pub use isa::registers::{RegInfo, RegUnit, RegClass}; use settings; use ir::{InstructionData, DataFlowGraph}; @@ -49,9 +49,9 @@ pub mod riscv; pub mod intel; pub mod arm32; pub mod arm64; +pub mod registers; mod encoding; mod enc_tables; -mod registers; /// Look for a supported ISA with the given `name`. /// Return a builder that can create a corresponding `TargetIsa`. diff --git a/lib/cretonne/src/isa/registers.rs b/lib/cretonne/src/isa/registers.rs index 26dc65b7d1..48173c50bd 100644 --- a/lib/cretonne/src/isa/registers.rs +++ b/lib/cretonne/src/isa/registers.rs @@ -147,8 +147,8 @@ impl RegInfo { /// Temporary object that holds enough information to print a register unit. pub struct DisplayRegUnit<'a> { - pub regunit: RegUnit, - pub reginfo: &'a RegInfo, + regunit: RegUnit, + reginfo: &'a RegInfo, } impl<'a> fmt::Display for DisplayRegUnit<'a> { diff --git a/lib/cretonne/src/regalloc/allocatable_set.rs b/lib/cretonne/src/regalloc/allocatable_set.rs new file mode 100644 index 0000000000..34145cd01a --- /dev/null +++ b/lib/cretonne/src/regalloc/allocatable_set.rs @@ -0,0 +1,176 @@ +//! Set of allocatable registers as a bit vector of register units. +//! +//! While allocating registers, we need to keep track of which registers are available and which +//! registers are in use. Since registers can alias in different ways, we track this via the +//! "register unit" abstraction. Every register contains one or more register units. Registers that +//! share a register unit can't be in use at the same time. + +use std::mem::size_of_val; +use isa::registers::{RegUnit, RegUnitMask, RegClass}; + +/// Set of registers available for allocation. +#[derive(Clone)] +pub struct AllocatableSet { + avail: RegUnitMask, +} + +// Given a register class and a register unit in the class, compute a word index and a bit mask of +// register units representing that register. +// +// Note that a register is not allowed to straddle words. +fn bitmask(rc: RegClass, reg: RegUnit) -> (usize, u32) { + // Bit mask representing the register. It is `rc.width` consecutive units. + let width_bits = (1 << rc.width) - 1; + // Index into avail[] of the word containing `reg`. + let word_index = (reg / 32) as usize; + // The actual bits in the word that cover `reg`. + let reg_bits = width_bits << (reg % 32); + + (word_index, reg_bits) +} + +impl AllocatableSet { + /// Create a new register set with all registers available. + /// + /// Note that this includes *all* registers. Query the `TargetIsa` object to get a set of + /// allocatable registers where reserved registers have been filtered out. + pub fn new() -> AllocatableSet { + AllocatableSet { avail: [!0; 3] } + } + + /// Returns `true` if the spoecified register is available. + pub fn is_avail(&self, rc: RegClass, reg: RegUnit) -> bool { + let (idx, bits) = bitmask(rc, reg); + (self.avail[idx] & bits) == bits + } + + /// Allocate `reg` from `rc` so it is no longer available. + /// + /// It is an error to take a register that doesn't have all of its register units available. + pub fn take(&mut self, rc: RegClass, reg: RegUnit) { + let (idx, bits) = bitmask(rc, reg); + debug_assert!((self.avail[idx] & bits) == bits, "Not available"); + self.avail[idx] &= !bits; + } + + /// Make `reg` available for allocation again. + pub fn free(&mut self, rc: RegClass, reg: RegUnit) { + let (idx, bits) = bitmask(rc, reg); + debug_assert!((self.avail[idx] & bits) == 0, "Not allocated"); + self.avail[idx] |= bits; + } + + /// Return an iterator over all available registers belonging to the register class `rc`. + /// + /// This doesn't allocate anything from the set; use `take()` for that. + pub fn iter(&self, rc: RegClass) -> RegSetIter { + // Start by copying the RC mask. It is a single set bit for each register in the class. + let mut rsi = RegSetIter { regs: rc.mask }; + + // Mask out the unavailable units. + for idx in 0..self.avail.len() { + // If a single unit in a register is unavailable, the whole register can't be used. + // If a register straddles a word boundary, it will be marked as unavailable. + // There's an assertion in cdsl/registers.py to check for that. + for i in 0..rc.width { + rsi.regs[idx] &= self.avail[idx] >> i; + } + } + rsi + } +} + +/// Iterator over available registers in a register class. +pub struct RegSetIter { + regs: RegUnitMask, +} + +impl Iterator for RegSetIter { + type Item = RegUnit; + + fn next(&mut self) -> Option { + let mut unit_offset = 0; + + // Find the first set bit in `self.regs`. + for word in &mut self.regs { + if *word != 0 { + // Compute the register unit number from the lowest set bit in the word. + let unit = unit_offset + word.trailing_zeros() as RegUnit; + + // Clear that lowest bit so we won't find it again. + *word = *word & (*word - 1); + + return Some(unit); + } + // How many register units was there in the word? This is a constant 32 for u32 etc. + unit_offset += 8 * size_of_val(word) as RegUnit; + } + + // All of `self.regs` is 0. + None + } +} + +#[cfg(test)] +mod tests { + use super::*; + use isa::registers::{RegClass, RegClassData}; + + // Register classes for testing. + const GPR: RegClass = &RegClassData { + index: 0, + width: 1, + mask: [0xf0000000, 0x0000000f, 0], + }; + const DPR: RegClass = &RegClassData { + index: 0, + width: 2, + mask: [0x50000000, 0x0000000a, 0], + }; + + #[test] + fn put_and_take() { + let mut regs = AllocatableSet::new(); + + // GPR has units 28-36. + assert_eq!(regs.iter(GPR).count(), 8); + assert_eq!(regs.iter(DPR).collect::>(), [28, 30, 33, 35]); + + assert!(regs.is_avail(GPR, 29)); + regs.take(&GPR, 29); + assert!(!regs.is_avail(GPR, 29)); + + assert_eq!(regs.iter(GPR).count(), 7); + assert_eq!(regs.iter(DPR).collect::>(), [30, 33, 35]); + + assert!(regs.is_avail(GPR, 30)); + regs.take(&GPR, 30); + assert!(!regs.is_avail(GPR, 30)); + + assert_eq!(regs.iter(GPR).count(), 6); + assert_eq!(regs.iter(DPR).collect::>(), [33, 35]); + + assert!(regs.is_avail(GPR, 32)); + regs.take(&GPR, 32); + assert!(!regs.is_avail(GPR, 32)); + + assert_eq!(regs.iter(GPR).count(), 5); + assert_eq!(regs.iter(DPR).collect::>(), [33, 35]); + + regs.free(&GPR, 30); + assert!(regs.is_avail(GPR, 30)); + assert!(!regs.is_avail(GPR, 29)); + assert!(!regs.is_avail(GPR, 32)); + + assert_eq!(regs.iter(GPR).count(), 6); + assert_eq!(regs.iter(DPR).collect::>(), [30, 33, 35]); + + regs.free(&GPR, 32); + assert!(regs.is_avail(GPR, 31)); + assert!(!regs.is_avail(GPR, 29)); + assert!(regs.is_avail(GPR, 32)); + + assert_eq!(regs.iter(GPR).count(), 7); + assert_eq!(regs.iter(DPR).collect::>(), [30, 33, 35]); + } +} diff --git a/lib/cretonne/src/regalloc/mod.rs b/lib/cretonne/src/regalloc/mod.rs index dff29bc5bc..9e0f7b820d 100644 --- a/lib/cretonne/src/regalloc/mod.rs +++ b/lib/cretonne/src/regalloc/mod.rs @@ -4,3 +4,4 @@ pub mod liverange; pub mod liveness; +pub mod allocatable_set;