""" Register set definitions ------------------------ Each ISA defines a separate register set that is used by the register allocator and the final binary encoding of machine code. The CPU registers are first divided into disjoint register banks, represented by a `RegBank` instance. Registers in different register banks never interfere with each other. A typical CPU will have a general purpose and a floating point register bank. A register bank consists of a number of *register units* which are the smallest indivisible units of allocation and interference. A register unit doesn't necessarily correspond to a particular number of bits in a register, it is more like a placeholder that can be used to determine of a register is taken or not. The register allocator works with *register classes* which can allocate one or more register units at a time. A register class allocates more than one register unit at a time when its registers are composed of smaller allocatable units. For example, the ARM double precision floating point registers are composed of two single precision registers. """ from __future__ import absolute_import from . import is_power_of_two, next_power_of_two try: from typing import Sequence, Tuple, List, Dict, Any, TYPE_CHECKING # noqa if TYPE_CHECKING: 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 # 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. A register bank controls a set of *register units* disjoint from all the other register banks in the ISA. The register units are numbered uniquely within the target ISA, and the units in a register bank form a contiguous sequence starting from a sufficiently aligned point that their low bits can be used directly when encoding machine code instructions. Register units can be given generated names like `r0`, `r1`, ..., or a tuple of special register unit names can be provided. :param name: Name of this register bank. :param doc: Documentation string. :param units: Number of register units. :param prefix: Prefix for generated unit names. :param names: Special names for the first units. May be shorter than `units`, the remaining units are named using `prefix`. """ def __init__(self, name, isa, doc, units, prefix='r', names=()): # type: (str, TargetISA, str, int, str, Sequence[str]) -> None self.name = name self.isa = isa self.first_unit = 0 self.units = units self.prefix = prefix self.names = names self.classes = list() # type: List[RegClass] self.toprcs = list() # type: List[RegClass] self.first_toprc_index = None # type: int assert len(names) <= units if isa.regbanks: # Get the next free unit number. last = isa.regbanks[-1] u = last.first_unit + last.units align = units if not is_power_of_two(align): align = next_power_of_two(align) self.first_unit = (u + align - 1) & -align self.index = len(isa.regbanks) isa.regbanks.append(self) def __repr__(self): # type: () -> str return ('RegBank({}, units={}, first_unit={})' .format(self.name, self.units, self.first_unit)) def finish_regclasses(self): # type: () -> None """ Compute subclasses and the top-level register class. 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 rc in self.classes: # All register classes must be given a name. assert rc.name, "Anonymous register class found" # 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): rc1.toprc = rc1 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) rc1.toprc = rc2.toprc if rc1.is_toprc(): self.toprcs.append(rc1) def unit_by_name(self, name): # type: (str) -> int """ Get a register unit in this bank by name. """ if name in self.names: r = self.names.index(name) elif name.startswith(self.prefix): r = int(name[len(self.prefix):]) assert r < self.units, 'Invalid register name: ' + name return self.first_unit + r class RegClass(object): """ A register class is a subset of register units in a RegBank along with a strategy for allocating registers. The *width* parameter determines how many register units are allocated at a time. Usually it that is one, but for example the ARM D registers are allocated two units at a time. When multiple units are allocated, it is always a contiguous set of unit numbers. :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`. """ 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] self.toprc = None # type: RegClass assert width > 0 assert start >= 0 and start < bank.units if count is None: count = bank.units // width self.count = count bank.classes.append(self) def __str__(self): # type: () -> str return self.name def is_toprc(self): # type: () -> bool """ Is this a top-level register class? A top-level register class has no sub-classes. This can only be answered aster running `finish_regclasses()`. """ return self.toprc is self 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): # type: (slice) -> RegClass """ 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 __getattr__(self, attr): # type: (str) -> Register """ Get a specific register in the class by name. For example: `GPR.r5`. """ return Register(self, self.bank.unit_by_name(attr)) def mask(self): # type: () -> List[int] """ 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 b = u % 32 # We need fancier masking code if a register can straddle mask # words. This will only happen with widths that are not powers of # two. assert b + self.width <= 32, 'Register straddles words' mask[u // 32] |= 1 << b 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): # type: (Dict[str, Any]) -> None """ 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 class Register(object): """ A specific register in a register class. A register is identified by the top-level register class it belongs to and its first register unit. Specific registers are used to describe constraints on instructions where some operands must use a fixed register. Register instances can be created with the constructor, or accessed as attributes on the register class: `GPR.rcx`. """ def __init__(self, rc, unit): # type: (RegClass, int) -> None self.regclass = rc self.unit = unit class Stack(object): """ An operand that must be in a stack slot. A `Stack` object can be used to indicate an operand constraint for a value operand that must live in a stack slot. """ def __init__(self, rc): # type: (RegClass) -> None self.regclass = rc