diff --git a/lib/cretonne/meta/cdsl/isa.py b/lib/cretonne/meta/cdsl/isa.py index 7f59a971db..6c324603a5 100644 --- a/lib/cretonne/meta/cdsl/isa.py +++ b/lib/cretonne/meta/cdsl/isa.py @@ -10,6 +10,7 @@ try: from .predicates import Predicate, FieldPredicate # noqa from .settings import SettingGroup # noqa from .types import ValueType # noqa + from .registers import RegBank # noqa AnyPredicate = Union[Predicate, FieldPredicate] except ImportError: pass @@ -32,6 +33,7 @@ class TargetISA(object): self.settings = None # type: SettingGroup self.instruction_groups = instruction_groups self.cpumodes = list() # type: List[CPUMode] + self.regbanks = list() # type: List[RegBank] def finish(self): # type: () -> TargetISA diff --git a/lib/cretonne/meta/cdsl/registers.py b/lib/cretonne/meta/cdsl/registers.py new file mode 100644 index 0000000000..6c1d379dac --- /dev/null +++ b/lib/cretonne/meta/cdsl/registers.py @@ -0,0 +1,80 @@ +""" +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 +necesarily 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 alocatable +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 # noqa + from .isa import TargetISA # noqa +except ImportError: + pass + + +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='p', 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 + + 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 + + isa.regbanks.append(self) + + def __repr__(self): + # type: () -> str + return ('RegBank({}, units={}, first_unit={})' + .format(self.name, self.units, self.first_unit)) diff --git a/lib/cretonne/meta/isa/arm32/registers.py b/lib/cretonne/meta/isa/arm32/registers.py new file mode 100644 index 0000000000..8d2b0480ae --- /dev/null +++ b/lib/cretonne/meta/isa/arm32/registers.py @@ -0,0 +1,29 @@ +""" +ARM32 register banks. +""" +from __future__ import absolute_import +from cdsl.registers import RegBank +from .defs import ISA + + +# Special register units: +# - r15 is the program counter. +# - r14 is the link register. +# - r13 is usually the stack pointer. +IntRegs = RegBank( + 'IntRegs', ISA, + 'General purpose registers', + units=16, prefix='r') + +FloatRegs = RegBank( + 'FloatRegs', ISA, r""" + Floating point registers. + + The floating point register units correspond to the S-registers, but + extended as if there were 64 registers. + + - S registers are one unit each. + - D registers are two units each, even D16 and above. + - Q registers are 4 units each. + """, + units=64, prefix='s') diff --git a/lib/cretonne/meta/isa/arm64/registers.py b/lib/cretonne/meta/isa/arm64/registers.py new file mode 100644 index 0000000000..fa1c87120b --- /dev/null +++ b/lib/cretonne/meta/isa/arm64/registers.py @@ -0,0 +1,19 @@ +""" +Aarch64 register banks. +""" +from __future__ import absolute_import +from cdsl.registers import RegBank +from .defs import ISA + + +# The `x31` regunit serves as the stack pointer / zero register depending on +# context. We reserve it and don't model the difference. +IntRegs = RegBank( + 'IntRegs', ISA, + 'General purpose registers', + units=32, prefix='x') + +FloatRegs = RegBank( + 'FloatRegs', ISA, + 'Floating point registers', + units=32, prefix='v') diff --git a/lib/cretonne/meta/isa/intel/registers.py b/lib/cretonne/meta/isa/intel/registers.py new file mode 100644 index 0000000000..abd6353eb1 --- /dev/null +++ b/lib/cretonne/meta/isa/intel/registers.py @@ -0,0 +1,39 @@ +""" +Intel register banks. + +While the floating-point registers are straight-forward, the general purpose +register bank has a few quirks on Intel architectures. We have these encodings +of the 8-bit registers: + + I32 I64 | 16b 32b 64b + 000 AL AL | AX EAX RAX + 001 CL CL | CX ECX RCX + 010 DL DL | DX EDX RDX + 011 BL BL | BX EBX RBX + 100 AH SPL | SP ESP RSP + 101 CH BPL | BP EBP RBP + 110 DH SIL | SI ESI RSI + 111 BH DIL | DI EDI RDI + +Here, the I64 column refers to the registers you get with a REX prefix. Without +the REX prefix, you get the I32 registers. + +The 8-bit registers are not that useful since WebAssembly only has i32 and i64 +data types, and the H-registers even less so. Rather than trying to model the +H-registers accurately, we'll avoid using them in both I32 and I64 modes. +""" +from __future__ import absolute_import +from cdsl.registers import RegBank +from .defs import ISA + + +IntRegs = RegBank( + 'IntRegs', ISA, + 'General purpose registers', + units=16, prefix='r', + names='rax rcx rdx rbx rsp rbp rsi rdi'.split()) + +FloatRegs = RegBank( + 'FloatRegs', ISA, + 'SSE floating point registers', + units=16, prefix='xmm') diff --git a/lib/cretonne/meta/isa/riscv/registers.py b/lib/cretonne/meta/isa/riscv/registers.py new file mode 100644 index 0000000000..9d1d0eaadb --- /dev/null +++ b/lib/cretonne/meta/isa/riscv/registers.py @@ -0,0 +1,18 @@ +""" +RISC-V register banks. +""" +from __future__ import absolute_import +from cdsl.registers import RegBank +from .defs import ISA + + +# We include `x0`, a.k.a `zero` in the register bank. It will be reserved. +IntRegs = RegBank( + 'IntRegs', ISA, + 'General purpose registers', + units=32, prefix='x') + +FloatRegs = RegBank( + 'FloatRegs', ISA, + 'Floating point registers', + units=32, prefix='f')