Generate register class descriptors.
Add a mechanism for defining sub-classes of register classes.
This commit is contained in:
@@ -32,6 +32,15 @@ except ImportError:
|
|||||||
pass
|
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):
|
class RegBank(object):
|
||||||
"""
|
"""
|
||||||
A register bank belonging to an ISA.
|
A register bank belonging to an ISA.
|
||||||
@@ -61,6 +70,7 @@ class RegBank(object):
|
|||||||
self.units = units
|
self.units = units
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
self.names = names
|
self.names = names
|
||||||
|
self.classes = list() # type: List[RegClass]
|
||||||
|
|
||||||
assert len(names) <= units
|
assert len(names) <= units
|
||||||
|
|
||||||
@@ -91,19 +101,16 @@ class RegClass(object):
|
|||||||
allocated two units at a time. When multiple units are allocated, it is
|
allocated two units at a time. When multiple units are allocated, it is
|
||||||
always a contiguous set of unit numbers.
|
always a contiguous set of unit numbers.
|
||||||
|
|
||||||
:param name: Name of this register class.
|
|
||||||
:param bank: The register bank we're allocating from.
|
:param bank: The register bank we're allocating from.
|
||||||
:param count: The maximum number of allocations in this register class. By
|
:param count: The maximum number of allocations in this register class. By
|
||||||
default, the whole register bank can be allocated.
|
default, the whole register bank can be allocated.
|
||||||
:param width: How many units to allocate at a time.
|
:param width: How many units to allocate at a time.
|
||||||
:param start: The first unit to allocate, relative to `bank.first.unit`.
|
: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):
|
def __init__(self, bank, count=None, width=1, start=0):
|
||||||
# type: (str, RegBank, int, int, int, int) -> None
|
# type: (RegBank, int, int, int) -> None
|
||||||
self.name = name
|
self.name = None # type: str
|
||||||
self.bank = bank
|
self.bank = bank
|
||||||
self.start = start
|
self.start = start
|
||||||
self.width = width
|
self.width = width
|
||||||
@@ -111,21 +118,53 @@ class RegClass(object):
|
|||||||
assert width > 0
|
assert width > 0
|
||||||
assert start >= 0 and start < bank.units
|
assert start >= 0 and start < bank.units
|
||||||
|
|
||||||
if stride is None:
|
|
||||||
stride = width
|
|
||||||
assert stride > 0
|
|
||||||
self.stride = stride
|
|
||||||
|
|
||||||
if count is None:
|
if count is None:
|
||||||
count = bank.units / stride
|
count = bank.units // width
|
||||||
self.count = count
|
self.count = count
|
||||||
|
|
||||||
# When the stride is 1, we can wrap around to the beginning of the
|
bank.classes.append(self)
|
||||||
# register bank, but with a larger stride, we wouldn't cover all the
|
|
||||||
# possible allocations with a simple modulo stride. For example,
|
def __getitem__(self, sliced):
|
||||||
# attempting to allocate the even registers before the odd ones
|
"""
|
||||||
# wouldn't work. Only if stride is coprime to bank.units would it work,
|
Create a sub-class of a register class using slice notation. The slice
|
||||||
# but that is unlikely since the bank size is almost always a power of
|
indexes refer to allocations in the parent register class, not register
|
||||||
# two.
|
units.
|
||||||
if start + count*stride > bank.units:
|
"""
|
||||||
assert stride == 1, 'Wrapping with stride not supported'
|
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
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import srcgen
|
|||||||
try:
|
try:
|
||||||
from typing import Sequence # noqa
|
from typing import Sequence # noqa
|
||||||
from cdsl.isa import TargetISA # noqa
|
from cdsl.isa import TargetISA # noqa
|
||||||
from cdsl.registers import RegBank # noqa
|
from cdsl.registers import RegBank, RegClass # noqa
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -18,8 +18,7 @@ def gen_regbank(regbank, fmt):
|
|||||||
"""
|
"""
|
||||||
Emit a static data definition for regbank.
|
Emit a static data definition for regbank.
|
||||||
"""
|
"""
|
||||||
with fmt.indented(
|
with fmt.indented('RegBank {', '},'):
|
||||||
'RegBank {{'.format(regbank.name), '},'):
|
|
||||||
fmt.line('name: "{}",'.format(regbank.name))
|
fmt.line('name: "{}",'.format(regbank.name))
|
||||||
fmt.line('first_unit: {},'.format(regbank.first_unit))
|
fmt.line('first_unit: {},'.format(regbank.first_unit))
|
||||||
fmt.line('units: {},'.format(regbank.units))
|
fmt.line('units: {},'.format(regbank.units))
|
||||||
@@ -29,6 +28,19 @@ def gen_regbank(regbank, fmt):
|
|||||||
fmt.line('prefix: "{}",'.format(regbank.prefix))
|
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):
|
def gen_isa(isa, fmt):
|
||||||
# type: (TargetISA, srcgen.Formatter) -> None
|
# type: (TargetISA, srcgen.Formatter) -> None
|
||||||
"""
|
"""
|
||||||
@@ -36,11 +48,29 @@ def gen_isa(isa, fmt):
|
|||||||
"""
|
"""
|
||||||
if not isa.regbanks:
|
if not isa.regbanks:
|
||||||
print('cargo:warning={} has no register banks'.format(isa.name))
|
print('cargo:warning={} has no register banks'.format(isa.name))
|
||||||
|
|
||||||
|
rcs = list() # type: List[RegClass]
|
||||||
with fmt.indented('pub static INFO: RegInfo = RegInfo {', '};'):
|
with fmt.indented('pub static INFO: RegInfo = RegInfo {', '};'):
|
||||||
# Bank descriptors.
|
# Bank descriptors.
|
||||||
with fmt.indented('banks: &[', '],'):
|
with fmt.indented('banks: &[', '],'):
|
||||||
for regbank in isa.regbanks:
|
for regbank in isa.regbanks:
|
||||||
gen_regbank(regbank, fmt)
|
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):
|
def generate(isas, out_dir):
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ IntRegs = RegBank(
|
|||||||
'General purpose registers',
|
'General purpose registers',
|
||||||
units=16, prefix='r')
|
units=16, prefix='r')
|
||||||
|
|
||||||
GPR = RegClass('GPR', IntRegs)
|
GPR = RegClass(IntRegs)
|
||||||
S = RegClass('S', FloatRegs, count=32)
|
S = RegClass(FloatRegs, count=32)
|
||||||
D = RegClass('D', FloatRegs, width=2)
|
D = RegClass(FloatRegs, width=2)
|
||||||
Q = RegClass('Q', FloatRegs, width=4)
|
Q = RegClass(FloatRegs, width=4)
|
||||||
|
|
||||||
|
RegClass.extract_names(globals())
|
||||||
|
|||||||
@@ -18,5 +18,7 @@ FloatRegs = RegBank(
|
|||||||
'Floating point registers',
|
'Floating point registers',
|
||||||
units=32, prefix='v')
|
units=32, prefix='v')
|
||||||
|
|
||||||
GPR = RegClass('GPR', IntRegs)
|
GPR = RegClass(IntRegs)
|
||||||
FPR = RegClass('FPR', FloatRegs)
|
FPR = RegClass(FloatRegs)
|
||||||
|
|
||||||
|
RegClass.extract_names(globals())
|
||||||
|
|||||||
@@ -38,5 +38,8 @@ FloatRegs = RegBank(
|
|||||||
'SSE floating point registers',
|
'SSE floating point registers',
|
||||||
units=16, prefix='xmm')
|
units=16, prefix='xmm')
|
||||||
|
|
||||||
GPR = RegClass('GPR', IntRegs)
|
GPR = RegClass(IntRegs)
|
||||||
FPR = RegClass('FPR', FloatRegs)
|
ABCD = GPR[0:4]
|
||||||
|
FPR = RegClass(FloatRegs)
|
||||||
|
|
||||||
|
RegClass.extract_names(globals())
|
||||||
|
|||||||
@@ -17,5 +17,7 @@ FloatRegs = RegBank(
|
|||||||
'Floating point registers',
|
'Floating point registers',
|
||||||
units=32, prefix='f')
|
units=32, prefix='f')
|
||||||
|
|
||||||
GPR = RegClass('GPR', IntRegs)
|
GPR = RegClass(IntRegs)
|
||||||
FPR = RegClass('FPR', FloatRegs)
|
FPR = RegClass(FloatRegs)
|
||||||
|
|
||||||
|
RegClass.extract_names(globals())
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! ARM32 register descriptions.
|
//! ARM32 register descriptions.
|
||||||
|
|
||||||
use isa::registers::{RegBank, RegInfo};
|
use isa::registers::{RegBank, RegClass, RegClassData, RegInfo};
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/registers-arm32.rs"));
|
include!(concat!(env!("OUT_DIR"), "/registers-arm32.rs"));
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! ARM64 register descriptions.
|
//! ARM64 register descriptions.
|
||||||
|
|
||||||
use isa::registers::{RegBank, RegInfo};
|
use isa::registers::{RegBank, RegClass, RegClassData, RegInfo};
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/registers-arm64.rs"));
|
include!(concat!(env!("OUT_DIR"), "/registers-arm64.rs"));
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Intel register descriptions.
|
//! Intel register descriptions.
|
||||||
|
|
||||||
use isa::registers::{RegBank, RegInfo};
|
use isa::registers::{RegBank, RegClass, RegClassData, RegInfo};
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/registers-intel.rs"));
|
include!(concat!(env!("OUT_DIR"), "/registers-intel.rs"));
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,14 @@ use std::fmt;
|
|||||||
/// The register allocator will enforce that each register unit only gets used for one thing.
|
/// The register allocator will enforce that each register unit only gets used for one thing.
|
||||||
pub type RegUnit = u16;
|
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
|
/// The register units in a target ISA are divided into disjoint register banks. Each bank covers a
|
||||||
/// contiguous range of register units.
|
/// contiguous range of register units.
|
||||||
///
|
///
|
||||||
@@ -23,7 +31,7 @@ pub struct RegBank {
|
|||||||
pub first_unit: RegUnit,
|
pub first_unit: RegUnit,
|
||||||
|
|
||||||
/// The total number of register units in this bank.
|
/// 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
|
/// Array of specially named register units. This array can be shorter than the number of units
|
||||||
/// in the bank.
|
/// 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.
|
/// Information about the registers in an ISA.
|
||||||
///
|
///
|
||||||
/// The `RegUnit` data structure collects all relevant static information about the registers in an
|
/// 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
|
/// 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.
|
/// there may be holes of unused register unit numbers between banks due to alignment.
|
||||||
pub banks: &'static [RegBank],
|
pub banks: &'static [RegBank],
|
||||||
|
|
||||||
|
/// All register classes ordered topologically so a sub-class always follows its parent.
|
||||||
|
pub classes: &'static [RegClassData],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegInfo {
|
impl RegInfo {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! RISC-V register descriptions.
|
//! RISC-V register descriptions.
|
||||||
|
|
||||||
use isa::registers::{RegBank, RegInfo};
|
use isa::registers::{RegBank, RegClass, RegClassData, RegInfo};
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/registers-riscv.rs"));
|
include!(concat!(env!("OUT_DIR"), "/registers-riscv.rs"));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user