Add operand register constraints.
Every encoding recipe must specify register constraints on input and output values. Generate recipe constraint tables along with the other encoding tables.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""Defining instruction set architectures."""
|
||||
from __future__ import absolute_import
|
||||
from .predicates import And
|
||||
from .registers import RegClass, Register
|
||||
|
||||
# The typing module is only required by mypy, and we don't use these imports
|
||||
# outside type comments.
|
||||
@@ -12,6 +13,8 @@ try:
|
||||
from .types import ValueType # noqa
|
||||
from .registers import RegBank # noqa
|
||||
AnyPredicate = Union[Predicate, FieldPredicate]
|
||||
OperandConstraint = Union[RegClass, Register, int]
|
||||
ConstraintSeq = Union[OperandConstraint, Tuple[OperandConstraint, ...]]
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
@@ -133,13 +136,24 @@ class EncRecipe(object):
|
||||
Many different instructions can be encoded by the same recipe, but they
|
||||
must all have the same instruction format.
|
||||
|
||||
The `ins` and `outs` arguments are tuples specifying the register
|
||||
allocation constraints for the value operands and results respectively. The
|
||||
possible constraints for an operand are:
|
||||
|
||||
- A `RegClass` specifying the set of allowed registers.
|
||||
- A `Register` specifying a fixed-register operand.
|
||||
- An integer indicating that this result is tied to a value operand, so
|
||||
they must use the same register.
|
||||
|
||||
:param name: Short mnemonic name for this recipe.
|
||||
:param format: All encoded instructions must have this
|
||||
:py:class:`InstructionFormat`.
|
||||
:param: ins Tuple of register constraints for value operands.
|
||||
:param: outs Tuple of register constraints for results.
|
||||
"""
|
||||
|
||||
def __init__(self, name, format, instp=None, isap=None):
|
||||
# type: (str, InstructionFormat, AnyPredicate, AnyPredicate) -> None
|
||||
def __init__(self, name, format, ins, outs, instp=None, isap=None):
|
||||
# type: (str, InstructionFormat, ConstraintSeq, ConstraintSeq, AnyPredicate, AnyPredicate) -> None # noqa
|
||||
self.name = name
|
||||
self.format = format
|
||||
self.instp = instp
|
||||
@@ -148,10 +162,29 @@ class EncRecipe(object):
|
||||
assert instp.predicate_context() == format
|
||||
self.number = None # type: int
|
||||
|
||||
self.ins = self._verify_constraints(ins)
|
||||
assert len(self.ins) == len(format.value_operands)
|
||||
self.outs = self._verify_constraints(outs)
|
||||
if len(self.outs) > 1:
|
||||
assert format.multiple_results
|
||||
|
||||
def __str__(self):
|
||||
# type: () -> str
|
||||
return self.name
|
||||
|
||||
def _verify_constraints(self, seq):
|
||||
# (ConstraintSeq) -> Sequence[OperandConstraint]
|
||||
if not isinstance(seq, tuple):
|
||||
seq = (seq,)
|
||||
for c in seq:
|
||||
if isinstance(c, int):
|
||||
# An integer constraint is bound to a value operand.
|
||||
# Check that it is in range.
|
||||
assert c >= 0 and c < len(self.format.value_operands)
|
||||
else:
|
||||
assert isinstance(c, RegClass) or isinstance(c, Register)
|
||||
return seq
|
||||
|
||||
|
||||
class Encoding(object):
|
||||
"""
|
||||
|
||||
@@ -62,7 +62,7 @@ class RegBank(object):
|
||||
`units`, the remaining units are named using `prefix`.
|
||||
"""
|
||||
|
||||
def __init__(self, name, isa, doc, units, prefix='p', names=()):
|
||||
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
|
||||
@@ -124,12 +124,16 @@ class RegClass(object):
|
||||
|
||||
bank.classes.append(self)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
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'
|
||||
@@ -142,6 +146,7 @@ class RegClass(object):
|
||||
return RegClass(self.bank, count=c, width=w, start=s)
|
||||
|
||||
def mask(self):
|
||||
# type: () -> List[int]
|
||||
"""
|
||||
Compute a bit-mask of the register units allocated by this register
|
||||
class.
|
||||
@@ -173,3 +178,22 @@ class RegClass(object):
|
||||
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 objects should be created using the indexing syntax on the
|
||||
register class.
|
||||
"""
|
||||
def __init__(self, rc, unit):
|
||||
# type: (RegClass, int) -> None
|
||||
self.regclass = rc
|
||||
self.unit = unit
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Generate sources for instruction encoding.
|
||||
|
||||
The tables and functions generated here support the `TargetIsa::encode()`
|
||||
The tables and functions generated here support the `TargetISA::encode()`
|
||||
function which determines if a given instruction is legal, and if so, it's
|
||||
`Encoding` data which consists of a *recipe* and some *encoding* bits.
|
||||
|
||||
@@ -56,6 +56,13 @@ from unique_table import UniqueSeqTable
|
||||
from collections import OrderedDict, defaultdict
|
||||
import math
|
||||
import itertools
|
||||
from cdsl.registers import RegClass, Register
|
||||
|
||||
try:
|
||||
from typing import Sequence # noqa
|
||||
from cdsl.isa import TargetISA, OperandConstraint # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def emit_instp(instp, fmt):
|
||||
@@ -399,6 +406,7 @@ def offset_type(length):
|
||||
|
||||
|
||||
def emit_recipe_names(isa, fmt):
|
||||
# type: (TargetISA, srcgen.Formatter) -> None
|
||||
"""
|
||||
Emit a table of encoding recipe names keyed by recipe number.
|
||||
|
||||
@@ -411,6 +419,48 @@ def emit_recipe_names(isa, fmt):
|
||||
fmt.line('"{}",'.format(r.name))
|
||||
|
||||
|
||||
def emit_recipe_constraints(isa, fmt):
|
||||
# type: (TargetISA, srcgen.Formatter) -> None
|
||||
"""
|
||||
Emit a table of encoding recipe operand constraints keyed by recipe number.
|
||||
|
||||
These are used by the register allocator to pick registers that can be
|
||||
properly encoded.
|
||||
"""
|
||||
with fmt.indented(
|
||||
'pub static RECIPE_CONSTRAINTS: [RecipeConstraints; {}] = ['
|
||||
.format(len(isa.all_recipes)), '];'):
|
||||
for r in isa.all_recipes:
|
||||
fmt.comment(r.name)
|
||||
with fmt.indented('RecipeConstraints {', '},'):
|
||||
emit_operand_constraints(r.ins, 'ins', fmt)
|
||||
emit_operand_constraints(r.outs, 'outs', fmt)
|
||||
|
||||
|
||||
def emit_operand_constraints(seq, field, fmt):
|
||||
# type: (Sequence[OperandConstraint], str, srcgen.Formatter) -> None
|
||||
"""
|
||||
Emit a struct field initializer for an array of operand constraints.
|
||||
"""
|
||||
if len(seq) == 0:
|
||||
fmt.line('{}: &[],'.format(field))
|
||||
return
|
||||
with fmt.indented('{}: &['.format(field), '],'):
|
||||
for cons in seq:
|
||||
with fmt.indented('OperandConstraint {', '},'):
|
||||
if isinstance(cons, RegClass):
|
||||
fmt.line('kind: ConstraintKind::Reg,')
|
||||
fmt.line('regclass: {},'.format(cons))
|
||||
elif isinstance(cons, Register):
|
||||
fmt.line(
|
||||
'kind: ConstraintKind::FixedReg({}),'
|
||||
.format(cons.unit))
|
||||
fmt.line('regclass: {},'.format(cons.regclass))
|
||||
else:
|
||||
raise AssertionError(
|
||||
'Unsupported constraint {}'.format(cons))
|
||||
|
||||
|
||||
def gen_isa(isa, fmt):
|
||||
# First assign numbers to relevant instruction predicates and generate the
|
||||
# check_instp() function..
|
||||
@@ -446,6 +496,7 @@ def gen_isa(isa, fmt):
|
||||
cpumode, level1_tables[cpumode], level1_offt, fmt)
|
||||
|
||||
emit_recipe_names(isa, fmt)
|
||||
emit_recipe_constraints(isa, fmt)
|
||||
|
||||
|
||||
def generate(isas, out_dir):
|
||||
|
||||
@@ -12,6 +12,7 @@ from __future__ import absolute_import
|
||||
from cdsl.isa import EncRecipe
|
||||
from cdsl.predicates import IsSignedInt
|
||||
from base.formats import Binary, BinaryImm
|
||||
from .registers import GPR
|
||||
|
||||
# The low 7 bits of a RISC-V instruction is the base opcode. All 32-bit
|
||||
# instructions have 11 as the two low bits, with bits 6:2 determining the base
|
||||
@@ -67,9 +68,11 @@ def OP32(funct3, funct7):
|
||||
|
||||
# R-type 32-bit instructions: These are mostly binary arithmetic instructions.
|
||||
# The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8)
|
||||
R = EncRecipe('R', Binary)
|
||||
R = EncRecipe('R', Binary, ins=(GPR, GPR), outs=GPR)
|
||||
|
||||
# R-type with an immediate shift amount instead of rs2.
|
||||
Rshamt = EncRecipe('Rshamt', BinaryImm)
|
||||
Rshamt = EncRecipe('Rshamt', BinaryImm, ins=GPR, outs=GPR)
|
||||
|
||||
I = EncRecipe('I', BinaryImm, instp=IsSignedInt(BinaryImm.imm, 12))
|
||||
I = EncRecipe(
|
||||
'I', BinaryImm, ins=GPR, outs=GPR,
|
||||
instp=IsSignedInt(BinaryImm.imm, 12))
|
||||
|
||||
Reference in New Issue
Block a user