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:
Jakob Stoklund Olesen
2017-01-25 13:57:43 -08:00
parent 0394f35034
commit 672e4abd7e
6 changed files with 200 additions and 13 deletions

View File

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

View File

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