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.
This commit is contained in:
@@ -51,6 +51,7 @@ class TargetISA(object):
|
|||||||
"""
|
"""
|
||||||
self._collect_encoding_recipes()
|
self._collect_encoding_recipes()
|
||||||
self._collect_predicates()
|
self._collect_predicates()
|
||||||
|
self._collect_regclasses()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _collect_encoding_recipes(self):
|
def _collect_encoding_recipes(self):
|
||||||
@@ -96,6 +97,18 @@ class TargetISA(object):
|
|||||||
if enc.isap:
|
if enc.isap:
|
||||||
self.settings.number_predicate(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):
|
class CPUMode(object):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ from . import is_power_of_two, next_power_of_two
|
|||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from typing import Sequence # noqa
|
from typing import Sequence, Tuple # noqa
|
||||||
from .isa import TargetISA # 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:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -90,6 +93,63 @@ class RegBank(object):
|
|||||||
return ('RegBank({}, units={}, first_unit={})'
|
return ('RegBank({}, units={}, first_unit={})'
|
||||||
.format(self.name, self.units, self.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):
|
class RegClass(object):
|
||||||
"""
|
"""
|
||||||
@@ -111,10 +171,14 @@ class RegClass(object):
|
|||||||
def __init__(self, bank, count=None, width=1, start=0):
|
def __init__(self, bank, count=None, width=1, start=0):
|
||||||
# type: (RegBank, int, int, int) -> None
|
# type: (RegBank, int, int, int) -> None
|
||||||
self.name = None # type: str
|
self.name = None # type: str
|
||||||
|
self.index = None # type: int
|
||||||
self.bank = bank
|
self.bank = bank
|
||||||
self.start = start
|
self.start = start
|
||||||
self.width = width
|
self.width = width
|
||||||
|
|
||||||
|
# This is computed later in `finish_regclasses()`.
|
||||||
|
self.subclasses = list() # type: List[RegClass]
|
||||||
|
|
||||||
assert width > 0
|
assert width > 0
|
||||||
assert start >= 0 and start < bank.units
|
assert start >= 0 and start < bank.units
|
||||||
|
|
||||||
@@ -127,13 +191,43 @@ class RegClass(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
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):
|
def __getitem__(self, sliced):
|
||||||
"""
|
"""
|
||||||
Create a sub-class of a register class using slice notation. The slice
|
Create a sub-class of a register class using slice notation. The slice
|
||||||
indexes refer to allocations in the parent register class, not register
|
indexes refer to allocations in the parent register class, not register
|
||||||
units.
|
units.
|
||||||
"""
|
"""
|
||||||
print(repr(sliced))
|
|
||||||
assert isinstance(sliced, slice), "RegClass slicing can't be 1 reg"
|
assert isinstance(sliced, slice), "RegClass slicing can't be 1 reg"
|
||||||
# We could add strided sub-classes if needed.
|
# We could add strided sub-classes if needed.
|
||||||
assert sliced.step is None, 'Subclass striding not supported'
|
assert sliced.step is None, 'Subclass striding not supported'
|
||||||
@@ -167,6 +261,16 @@ class RegClass(object):
|
|||||||
|
|
||||||
return mask
|
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
|
@staticmethod
|
||||||
def extract_names(globs):
|
def extract_names(globs):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -28,15 +28,16 @@ def gen_regbank(regbank, fmt):
|
|||||||
fmt.line('prefix: "{}",'.format(regbank.prefix))
|
fmt.line('prefix: "{}",'.format(regbank.prefix))
|
||||||
|
|
||||||
|
|
||||||
def gen_regclass(idx, rc, fmt):
|
def gen_regclass(rc, fmt):
|
||||||
# type: (int, RegClass, srcgen.Formatter) -> None
|
# type: (RegClass, srcgen.Formatter) -> None
|
||||||
"""
|
"""
|
||||||
Emit a static data definition for a register class.
|
Emit a static data definition for a register class.
|
||||||
"""
|
"""
|
||||||
fmt.comment(rc.name)
|
fmt.comment(rc.name)
|
||||||
with fmt.indented('RegClassData {', '},'):
|
with fmt.indented('RegClassData {', '},'):
|
||||||
fmt.line('index: {},'.format(idx))
|
fmt.line('index: {},'.format(rc.index))
|
||||||
fmt.line('width: {},'.format(rc.width))
|
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())
|
mask = ', '.join('0x{:08x}'.format(x) for x in rc.mask())
|
||||||
fmt.line('mask: [{}],'.format(mask))
|
fmt.line('mask: [{}],'.format(mask))
|
||||||
|
|
||||||
@@ -62,15 +63,15 @@ def gen_isa(isa, fmt):
|
|||||||
with fmt.indented(
|
with fmt.indented(
|
||||||
'const CLASSES: [RegClassData; {}] = ['.format(len(rcs)), '];'):
|
'const CLASSES: [RegClassData; {}] = ['.format(len(rcs)), '];'):
|
||||||
for idx, rc in enumerate(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.
|
# Emit constants referencing the register classes.
|
||||||
for idx, rc in enumerate(rcs):
|
for rc in rcs:
|
||||||
if rc.name:
|
|
||||||
fmt.line('#[allow(dead_code)]')
|
fmt.line('#[allow(dead_code)]')
|
||||||
fmt.line(
|
fmt.line(
|
||||||
'pub const {}: RegClass = &CLASSES[{}];'
|
'pub const {}: RegClass = &CLASSES[{}];'
|
||||||
.format(rc.name, idx))
|
.format(rc.name, rc.index))
|
||||||
|
|
||||||
|
|
||||||
def generate(isas, out_dir):
|
def generate(isas, out_dir):
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ include!(concat!(env!("OUT_DIR"), "/registers-intel.rs"));
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::INFO;
|
use super::*;
|
||||||
use isa::RegUnit;
|
use isa::RegUnit;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -46,4 +46,17 @@ mod tests {
|
|||||||
assert_eq!(uname(16), "%xmm0");
|
assert_eq!(uname(16), "%xmm0");
|
||||||
assert_eq!(uname(31), "%xmm15");
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//! Data structures describing the registers in an ISA.
|
//! Data structures describing the registers in an ISA.
|
||||||
|
|
||||||
|
use entity_map::EntityRef;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// Register units are the smallest units of register allocation.
|
/// Register units are the smallest units of register allocation.
|
||||||
@@ -106,11 +107,59 @@ pub struct RegClassData {
|
|||||||
/// How many register units to allocate per register.
|
/// How many register units to allocate per register.
|
||||||
pub width: u8,
|
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
|
/// 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.
|
/// first register unit in each allocatable register.
|
||||||
pub mask: RegUnitMask,
|
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.
|
/// 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
|
||||||
@@ -143,6 +192,11 @@ impl RegInfo {
|
|||||||
reginfo: self,
|
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.
|
/// Temporary object that holds enough information to print a register unit.
|
||||||
|
|||||||
@@ -120,11 +120,13 @@ mod tests {
|
|||||||
const GPR: RegClass = &RegClassData {
|
const GPR: RegClass = &RegClassData {
|
||||||
index: 0,
|
index: 0,
|
||||||
width: 1,
|
width: 1,
|
||||||
|
subclasses: 0,
|
||||||
mask: [0xf0000000, 0x0000000f, 0],
|
mask: [0xf0000000, 0x0000000f, 0],
|
||||||
};
|
};
|
||||||
const DPR: RegClass = &RegClassData {
|
const DPR: RegClass = &RegClassData {
|
||||||
index: 0,
|
index: 0,
|
||||||
width: 2,
|
width: 2,
|
||||||
|
subclasses: 0,
|
||||||
mask: [0x50000000, 0x0000000a, 0],
|
mask: [0x50000000, 0x0000000a, 0],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user