diff --git a/lib/cretonne/meta/cdsl/isa.py b/lib/cretonne/meta/cdsl/isa.py index 8e1423315c..0065e4e907 100644 --- a/lib/cretonne/meta/cdsl/isa.py +++ b/lib/cretonne/meta/cdsl/isa.py @@ -51,6 +51,7 @@ class TargetISA(object): """ self._collect_encoding_recipes() self._collect_predicates() + self._collect_regclasses() return self def _collect_encoding_recipes(self): @@ -96,6 +97,18 @@ class TargetISA(object): if enc.isap: self.settings.number_predicate(enc.isap) + def _collect_regclasses(self): + """ + Collect and number register classes. + + Every register class needs a unique index, and the classes need to be + topologically ordered. + """ + rc_index = 0 + for bank in self.regbanks: + bank.finish_regclasses(rc_index) + rc_index += len(bank.classes) + class CPUMode(object): """ diff --git a/lib/cretonne/meta/cdsl/registers.py b/lib/cretonne/meta/cdsl/registers.py index 828e6a2704..99dd453aec 100644 --- a/lib/cretonne/meta/cdsl/registers.py +++ b/lib/cretonne/meta/cdsl/registers.py @@ -26,8 +26,11 @@ from . import is_power_of_two, next_power_of_two try: - from typing import Sequence # noqa + from typing import Sequence, Tuple # noqa from .isa import TargetISA # noqa + # A tuple uniquely identifying a register class inside a register bank. + # (count, width, start) + RCTup = Tuple[int, int, int] except ImportError: pass @@ -90,6 +93,63 @@ class RegBank(object): return ('RegBank({}, units={}, first_unit={})' .format(self.name, self.units, self.first_unit)) + def finish_regclasses(self, first_index): + # type: (int) -> None + """ + Assign indexes to the register classes in this bank, starting from + `first_index`. + + Verify that the set of register classes satisfies: + + 1. Closed under intersection: The intersection of any two register + classes in the set is either empty or identical to a member of the + set. + 2. There are no identical classes under different names. + 3. Classes are sorted topologically such that all subclasses have a + higher index that the superclass. + + We could reorder classes topologically here instead of just enforcing + the order, but the ordering tends to fall out naturally anyway. + """ + cmap = dict() # type: Dict[RCTup, RegClass] + + for idx, rc in enumerate(self.classes): + # All register classes must be given a name. + assert rc.name, "Anonymous register class found" + + # Assign a unique index. + assert rc.index is None + rc.index = idx + first_index + + # Check for duplicates. + tup = rc.rctup() + if tup in cmap: + raise AssertionError( + '{} and {} are identical register classes' + .format(rc, cmap[tup])) + cmap[tup] = rc + + # Check intersections and topological order. + for idx, rc1 in enumerate(self.classes): + for rc2 in self.classes[0:idx]: + itup = rc1.intersect(rc2) + if itup is None: + continue + if itup not in cmap: + raise AssertionError( + 'intersection of {} and {} missing' + .format(rc1, rc2)) + irc = cmap[itup] + # rc1 > rc2, so rc2 can't be the sub-class. + if irc is rc2: + raise AssertionError( + 'Bad topological order: {}/{}' + .format(rc1, rc2)) + if irc is rc1: + # The intersection of rc1 and rc2 is rc1, so it must be a + # sub-class. + rc2.subclasses.append(rc1) + class RegClass(object): """ @@ -111,10 +171,14 @@ class RegClass(object): def __init__(self, bank, count=None, width=1, start=0): # type: (RegBank, int, int, int) -> None self.name = None # type: str + self.index = None # type: int self.bank = bank self.start = start self.width = width + # This is computed later in `finish_regclasses()`. + self.subclasses = list() # type: List[RegClass] + assert width > 0 assert start >= 0 and start < bank.units @@ -127,13 +191,43 @@ class RegClass(object): def __str__(self): return self.name + def rctup(self): + # type: () -> RCTup + """ + Get a tuple that uniquely identifies the registers in this class. + + The tuple can be used as a dictionary key to ensure that there are no + duplicate register classes. + """ + return (self.count, self.width, self.start) + + def intersect(self, other): + # type: (RegClass) -> RCTup + """ + Get a tuple representing the intersction of two register classes. + + Returns `None` if the two classes are disjoint. + """ + if self.width != other.width: + return None + s_end = self.start + self.count * self.width + o_end = other.start + other.count * other.width + if self.start >= o_end or other.start >= s_end: + return None + + # We have an overlap. + start = max(self.start, other.start) + end = min(s_end, o_end) + count = (end - start) // self.width + assert count > 0 + return (count, self.width, start) + def __getitem__(self, sliced): """ Create a sub-class of a register class using slice notation. The slice indexes refer to allocations in the parent register class, not register units. """ - print(repr(sliced)) assert isinstance(sliced, slice), "RegClass slicing can't be 1 reg" # We could add strided sub-classes if needed. assert sliced.step is None, 'Subclass striding not supported' @@ -167,6 +261,16 @@ class RegClass(object): return mask + def subclass_mask(self): + # type: () -> int + """ + Compute a bit-mask of subclasses, including self. + """ + m = 1 << self.index + for rc in self.subclasses: + m |= 1 << rc.index + return m + @staticmethod def extract_names(globs): """ diff --git a/lib/cretonne/meta/gen_registers.py b/lib/cretonne/meta/gen_registers.py index d3afe3ca4c..c535f44c22 100644 --- a/lib/cretonne/meta/gen_registers.py +++ b/lib/cretonne/meta/gen_registers.py @@ -28,15 +28,16 @@ def gen_regbank(regbank, fmt): fmt.line('prefix: "{}",'.format(regbank.prefix)) -def gen_regclass(idx, rc, fmt): - # type: (int, RegClass, srcgen.Formatter) -> None +def gen_regclass(rc, fmt): + # type: (RegClass, srcgen.Formatter) -> None """ Emit a static data definition for a register class. """ fmt.comment(rc.name) with fmt.indented('RegClassData {', '},'): - fmt.line('index: {},'.format(idx)) + fmt.line('index: {},'.format(rc.index)) fmt.line('width: {},'.format(rc.width)) + fmt.line('subclasses: 0x{:x},'.format(rc.subclass_mask())) mask = ', '.join('0x{:08x}'.format(x) for x in rc.mask()) fmt.line('mask: [{}],'.format(mask)) @@ -62,15 +63,15 @@ def gen_isa(isa, fmt): with fmt.indented( 'const CLASSES: [RegClassData; {}] = ['.format(len(rcs)), '];'): for idx, rc in enumerate(rcs): - gen_regclass(idx, rc, fmt) + assert idx == rc.index + gen_regclass(rc, fmt) # Emit constants referencing the register classes. - for idx, rc in enumerate(rcs): - if rc.name: - fmt.line('#[allow(dead_code)]') - fmt.line( - 'pub const {}: RegClass = &CLASSES[{}];' - .format(rc.name, idx)) + for rc in rcs: + fmt.line('#[allow(dead_code)]') + fmt.line( + 'pub const {}: RegClass = &CLASSES[{}];' + .format(rc.name, rc.index)) def generate(isas, out_dir): diff --git a/lib/cretonne/src/isa/intel/registers.rs b/lib/cretonne/src/isa/intel/registers.rs index 82c5b9886e..14b4050d94 100644 --- a/lib/cretonne/src/isa/intel/registers.rs +++ b/lib/cretonne/src/isa/intel/registers.rs @@ -6,7 +6,7 @@ include!(concat!(env!("OUT_DIR"), "/registers-intel.rs")); #[cfg(test)] mod tests { - use super::INFO; + use super::*; use isa::RegUnit; #[test] @@ -46,4 +46,17 @@ mod tests { assert_eq!(uname(16), "%xmm0"); assert_eq!(uname(31), "%xmm15"); } + + #[test] + fn regclasses() { + assert_eq!(GPR.intersect(GPR), Some(GPR.into())); + assert_eq!(GPR.intersect(ABCD), Some(ABCD.into())); + assert_eq!(GPR.intersect(FPR), None); + assert_eq!(ABCD.intersect(GPR), Some(ABCD.into())); + assert_eq!(ABCD.intersect(ABCD), Some(ABCD.into())); + assert_eq!(ABCD.intersect(FPR), None); + assert_eq!(FPR.intersect(FPR), Some(FPR.into())); + assert_eq!(FPR.intersect(GPR), None); + assert_eq!(FPR.intersect(ABCD), None); + } } diff --git a/lib/cretonne/src/isa/registers.rs b/lib/cretonne/src/isa/registers.rs index 48173c50bd..b223a3b5b0 100644 --- a/lib/cretonne/src/isa/registers.rs +++ b/lib/cretonne/src/isa/registers.rs @@ -1,5 +1,6 @@ //! Data structures describing the registers in an ISA. +use entity_map::EntityRef; use std::fmt; /// Register units are the smallest units of register allocation. @@ -106,11 +107,59 @@ pub struct RegClassData { /// How many register units to allocate per register. pub width: u8, + /// Bit-mask of sub-classes of this register class, including itself. + /// + /// Bits correspond to RC indexes. + pub subclasses: u32, + /// Mask of register units in the class. If `width > 1`, the mask only has a bit set for the /// first register unit in each allocatable register. pub mask: RegUnitMask, } +impl RegClassData { + /// Get the register class corresponding to the intersection of `self` and `other`. + /// + /// This register class is guaranteed to exist if the register classes overlap. If the register + /// classes don't overlap, returns `None`. + pub fn intersect(&self, other: RegClass) -> Option { + // Compute the set of common subclasses. + let mask = self.subclasses & other.subclasses; + + if mask == 0 { + // No overlap. + None + } else { + // Register class indexes are topologically ordered, so the largest common subclass has + // the smallest index. + Some(RegClassIndex(mask.trailing_zeros() as u8)) + } + } +} + +/// A small reference to a register class. +/// +/// Use this when storing register classes in compact data structures. The `RegInfo::rc()` method +/// can be used to get the real register class reference back. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct RegClassIndex(u8); + +impl EntityRef for RegClassIndex { + fn new(idx: usize) -> Self { + RegClassIndex(idx as u8) + } + + fn index(self) -> usize { + self.0 as usize + } +} + +impl From for RegClassIndex { + fn from(rc: RegClass) -> Self { + RegClassIndex(rc.index) + } +} + /// Information about the registers in an ISA. /// /// The `RegUnit` data structure collects all relevant static information about the registers in an @@ -143,6 +192,11 @@ impl RegInfo { reginfo: self, } } + + /// Get the register class corresponding to `idx`. + pub fn rc(&self, idx: RegClassIndex) -> RegClass { + &self.classes[idx.index()] + } } /// Temporary object that holds enough information to print a register unit. diff --git a/lib/cretonne/src/regalloc/allocatable_set.rs b/lib/cretonne/src/regalloc/allocatable_set.rs index 34145cd01a..f2ef0102d4 100644 --- a/lib/cretonne/src/regalloc/allocatable_set.rs +++ b/lib/cretonne/src/regalloc/allocatable_set.rs @@ -120,11 +120,13 @@ mod tests { const GPR: RegClass = &RegClassData { index: 0, width: 1, + subclasses: 0, mask: [0xf0000000, 0x0000000f, 0], }; const DPR: RegClass = &RegClassData { index: 0, width: 2, + subclasses: 0, mask: [0x50000000, 0x0000000a, 0], };