diff --git a/lib/cretonne/meta/cdsl/registers.py b/lib/cretonne/meta/cdsl/registers.py index 243a72e669..b27c6128ec 100644 --- a/lib/cretonne/meta/cdsl/registers.py +++ b/lib/cretonne/meta/cdsl/registers.py @@ -32,6 +32,15 @@ except ImportError: pass +# The number of 32-bit elements in a register unit mask +MASK_LEN = 3 + +# The maximum total number of register units allowed. +# This limit can be raised by also adjusting the RegUnitMask type in +# src/isa/registers.rs. +MAX_UNITS = MASK_LEN * 32 + + class RegBank(object): """ A register bank belonging to an ISA. @@ -61,6 +70,7 @@ class RegBank(object): self.units = units self.prefix = prefix self.names = names + self.classes = list() # type: List[RegClass] assert len(names) <= units @@ -91,19 +101,16 @@ class RegClass(object): allocated two units at a time. When multiple units are allocated, it is always a contiguous set of unit numbers. - :param name: Name of this register class. :param bank: The register bank we're allocating from. :param count: The maximum number of allocations in this register class. By default, the whole register bank can be allocated. :param width: How many units to allocate at a time. :param start: The first unit to allocate, relative to `bank.first.unit`. - :param stride: How many units to skip to get to the next allocation. - Default is `width`. """ - def __init__(self, name, bank, count=None, width=1, start=0, stride=None): - # type: (str, RegBank, int, int, int, int) -> None - self.name = name + def __init__(self, bank, count=None, width=1, start=0): + # type: (RegBank, int, int, int) -> None + self.name = None # type: str self.bank = bank self.start = start self.width = width @@ -111,21 +118,53 @@ class RegClass(object): assert width > 0 assert start >= 0 and start < bank.units - if stride is None: - stride = width - assert stride > 0 - self.stride = stride - if count is None: - count = bank.units / stride + count = bank.units // width self.count = count - # When the stride is 1, we can wrap around to the beginning of the - # register bank, but with a larger stride, we wouldn't cover all the - # possible allocations with a simple modulo stride. For example, - # attempting to allocate the even registers before the odd ones - # wouldn't work. Only if stride is coprime to bank.units would it work, - # but that is unlikely since the bank size is almost always a power of - # two. - if start + count*stride > bank.units: - assert stride == 1, 'Wrapping with stride not supported' + bank.classes.append(self) + + 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. + """ + 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' + + w = self.width + s = self.start + sliced.start * w + c = sliced.stop - sliced.start + assert c > 1, "Can't have single-register classes" + + return RegClass(self.bank, count=c, width=w, start=s) + + def mask(self): + """ + Compute a bit-mask of the register units allocated by this register + class. + + Return as a list of 32-bit integers. + """ + mask = [0] * MASK_LEN + + start = self.bank.first_unit + self.start + for a in range(self.count): + u = start + a * self.width + mask[u // 32] |= 1 << (u % 32) + + return mask + + @staticmethod + def extract_names(globs): + """ + Given a dict mapping name -> object as returned by `globals()`, find + all the RegClass objects and set their name from the dict key. + This is used to name a bunch of global variables in a module. + """ + for name, obj in globs.items(): + if isinstance(obj, RegClass): + assert obj.name is None + obj.name = name diff --git a/lib/cretonne/meta/gen_registers.py b/lib/cretonne/meta/gen_registers.py index 323aec6173..d3afe3ca4c 100644 --- a/lib/cretonne/meta/gen_registers.py +++ b/lib/cretonne/meta/gen_registers.py @@ -8,7 +8,7 @@ import srcgen try: from typing import Sequence # noqa from cdsl.isa import TargetISA # noqa - from cdsl.registers import RegBank # noqa + from cdsl.registers import RegBank, RegClass # noqa except ImportError: pass @@ -18,8 +18,7 @@ def gen_regbank(regbank, fmt): """ Emit a static data definition for regbank. """ - with fmt.indented( - 'RegBank {{'.format(regbank.name), '},'): + with fmt.indented('RegBank {', '},'): fmt.line('name: "{}",'.format(regbank.name)) fmt.line('first_unit: {},'.format(regbank.first_unit)) fmt.line('units: {},'.format(regbank.units)) @@ -29,6 +28,19 @@ def gen_regbank(regbank, fmt): fmt.line('prefix: "{}",'.format(regbank.prefix)) +def gen_regclass(idx, rc, fmt): + # type: (int, 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('width: {},'.format(rc.width)) + mask = ', '.join('0x{:08x}'.format(x) for x in rc.mask()) + fmt.line('mask: [{}],'.format(mask)) + + def gen_isa(isa, fmt): # type: (TargetISA, srcgen.Formatter) -> None """ @@ -36,11 +48,29 @@ def gen_isa(isa, fmt): """ if not isa.regbanks: print('cargo:warning={} has no register banks'.format(isa.name)) + + rcs = list() # type: List[RegClass] with fmt.indented('pub static INFO: RegInfo = RegInfo {', '};'): # Bank descriptors. with fmt.indented('banks: &[', '],'): for regbank in isa.regbanks: gen_regbank(regbank, fmt) + rcs += regbank.classes + fmt.line('classes: &CLASSES,') + + # Register class descriptors. + with fmt.indented( + 'const CLASSES: [RegClassData; {}] = ['.format(len(rcs)), '];'): + for idx, rc in enumerate(rcs): + gen_regclass(idx, 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)) def generate(isas, out_dir): diff --git a/lib/cretonne/meta/isa/arm32/registers.py b/lib/cretonne/meta/isa/arm32/registers.py index af031e145b..9522057e34 100644 --- a/lib/cretonne/meta/isa/arm32/registers.py +++ b/lib/cretonne/meta/isa/arm32/registers.py @@ -29,7 +29,9 @@ IntRegs = RegBank( 'General purpose registers', units=16, prefix='r') -GPR = RegClass('GPR', IntRegs) -S = RegClass('S', FloatRegs, count=32) -D = RegClass('D', FloatRegs, width=2) -Q = RegClass('Q', FloatRegs, width=4) +GPR = RegClass(IntRegs) +S = RegClass(FloatRegs, count=32) +D = RegClass(FloatRegs, width=2) +Q = RegClass(FloatRegs, width=4) + +RegClass.extract_names(globals()) diff --git a/lib/cretonne/meta/isa/arm64/registers.py b/lib/cretonne/meta/isa/arm64/registers.py index fec3601367..b39bc917a1 100644 --- a/lib/cretonne/meta/isa/arm64/registers.py +++ b/lib/cretonne/meta/isa/arm64/registers.py @@ -18,5 +18,7 @@ FloatRegs = RegBank( 'Floating point registers', units=32, prefix='v') -GPR = RegClass('GPR', IntRegs) -FPR = RegClass('FPR', FloatRegs) +GPR = RegClass(IntRegs) +FPR = RegClass(FloatRegs) + +RegClass.extract_names(globals()) diff --git a/lib/cretonne/meta/isa/intel/registers.py b/lib/cretonne/meta/isa/intel/registers.py index 6a4c59403e..92e76191fa 100644 --- a/lib/cretonne/meta/isa/intel/registers.py +++ b/lib/cretonne/meta/isa/intel/registers.py @@ -38,5 +38,8 @@ FloatRegs = RegBank( 'SSE floating point registers', units=16, prefix='xmm') -GPR = RegClass('GPR', IntRegs) -FPR = RegClass('FPR', FloatRegs) +GPR = RegClass(IntRegs) +ABCD = GPR[0:4] +FPR = RegClass(FloatRegs) + +RegClass.extract_names(globals()) diff --git a/lib/cretonne/meta/isa/riscv/registers.py b/lib/cretonne/meta/isa/riscv/registers.py index f00c9a0f0d..d9d43f0432 100644 --- a/lib/cretonne/meta/isa/riscv/registers.py +++ b/lib/cretonne/meta/isa/riscv/registers.py @@ -17,5 +17,7 @@ FloatRegs = RegBank( 'Floating point registers', units=32, prefix='f') -GPR = RegClass('GPR', IntRegs) -FPR = RegClass('FPR', FloatRegs) +GPR = RegClass(IntRegs) +FPR = RegClass(FloatRegs) + +RegClass.extract_names(globals()) diff --git a/lib/cretonne/src/isa/arm32/registers.rs b/lib/cretonne/src/isa/arm32/registers.rs index 30e7fd58cf..69571976cb 100644 --- a/lib/cretonne/src/isa/arm32/registers.rs +++ b/lib/cretonne/src/isa/arm32/registers.rs @@ -1,6 +1,6 @@ //! ARM32 register descriptions. -use isa::registers::{RegBank, RegInfo}; +use isa::registers::{RegBank, RegClass, RegClassData, RegInfo}; include!(concat!(env!("OUT_DIR"), "/registers-arm32.rs")); diff --git a/lib/cretonne/src/isa/arm64/registers.rs b/lib/cretonne/src/isa/arm64/registers.rs index 2420db6df1..0a0e043a20 100644 --- a/lib/cretonne/src/isa/arm64/registers.rs +++ b/lib/cretonne/src/isa/arm64/registers.rs @@ -1,6 +1,6 @@ //! ARM64 register descriptions. -use isa::registers::{RegBank, RegInfo}; +use isa::registers::{RegBank, RegClass, RegClassData, RegInfo}; include!(concat!(env!("OUT_DIR"), "/registers-arm64.rs")); diff --git a/lib/cretonne/src/isa/intel/registers.rs b/lib/cretonne/src/isa/intel/registers.rs index 1946d0fc57..82c5b9886e 100644 --- a/lib/cretonne/src/isa/intel/registers.rs +++ b/lib/cretonne/src/isa/intel/registers.rs @@ -1,6 +1,6 @@ //! Intel register descriptions. -use isa::registers::{RegBank, RegInfo}; +use isa::registers::{RegBank, RegClass, RegClassData, RegInfo}; include!(concat!(env!("OUT_DIR"), "/registers-intel.rs")); diff --git a/lib/cretonne/src/isa/registers.rs b/lib/cretonne/src/isa/registers.rs index 8736f06e84..26dc65b7d1 100644 --- a/lib/cretonne/src/isa/registers.rs +++ b/lib/cretonne/src/isa/registers.rs @@ -11,6 +11,14 @@ use std::fmt; /// The register allocator will enforce that each register unit only gets used for one thing. pub type RegUnit = u16; +/// A bit mask indexed by register units. +/// +/// The size of this type is determined by the target ISA that has the most register units defined. +/// Currently that is arm32 which has 64+16 units. +/// +/// This type should be coordinated with meta/cdsl/registers.py. +pub type RegUnitMask = [u32; 3]; + /// The register units in a target ISA are divided into disjoint register banks. Each bank covers a /// contiguous range of register units. /// @@ -23,7 +31,7 @@ pub struct RegBank { pub first_unit: RegUnit, /// The total number of register units in this bank. - pub units: u16, + pub units: RegUnit, /// Array of specially named register units. This array can be shorter than the number of units /// in the bank. @@ -79,6 +87,30 @@ impl RegBank { } } +/// A register class reference. +/// +/// All register classes are statically defined in tables generated from the meta descriptions. +pub type RegClass = &'static RegClassData; + +/// Data about a register class. +/// +/// A register class represents a subset of the registers in a bank. It describes the set of +/// permitted registers for a register operand in a given encoding of an instruction. +/// +/// A register class can be a subset of another register class. The top-level register classes are +/// disjoint. +pub struct RegClassData { + /// The index of this class in the ISA's RegInfo description. + pub index: u8, + + /// How many register units to allocate per register. + pub width: u8, + + /// 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, +} + /// Information about the registers in an ISA. /// /// The `RegUnit` data structure collects all relevant static information about the registers in an @@ -87,6 +119,9 @@ pub struct RegInfo { /// All register banks, ordered by their `first_unit`. The register banks are disjoint, but /// there may be holes of unused register unit numbers between banks due to alignment. pub banks: &'static [RegBank], + + /// All register classes ordered topologically so a sub-class always follows its parent. + pub classes: &'static [RegClassData], } impl RegInfo { diff --git a/lib/cretonne/src/isa/riscv/registers.rs b/lib/cretonne/src/isa/riscv/registers.rs index a3fe4739e8..7deef9251a 100644 --- a/lib/cretonne/src/isa/riscv/registers.rs +++ b/lib/cretonne/src/isa/riscv/registers.rs @@ -1,6 +1,6 @@ //! RISC-V register descriptions. -use isa::registers::{RegBank, RegInfo}; +use isa::registers::{RegBank, RegClass, RegClassData, RegInfo}; include!(concat!(env!("OUT_DIR"), "/registers-riscv.rs"));