Browse Source

Compute register class intersections.

Ensure that the set of register classes is closed under intersection.

Provide a RegClass::intersect() method which finds the register class
representing the intersection of two classes.

Generate a bit-mask of subclasses for each register class to be used by
the intersect() method.

Ensure that register classes are sorted topologically. This is also used
by the intersect() method.
pull/3/head
Jakob Stoklund Olesen 8 years ago
parent
commit
672e4abd7e
  1. 13
      lib/cretonne/meta/cdsl/isa.py
  2. 108
      lib/cretonne/meta/cdsl/registers.py
  3. 21
      lib/cretonne/meta/gen_registers.py
  4. 15
      lib/cretonne/src/isa/intel/registers.rs
  5. 54
      lib/cretonne/src/isa/registers.rs
  6. 2
      lib/cretonne/src/regalloc/allocatable_set.rs

13
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):
"""

108
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):
"""

21
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):

15
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);
}
}

54
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<RegClassIndex> {
// 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<RegClass> 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.

2
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],
};

Loading…
Cancel
Save