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:
@@ -331,8 +331,6 @@ encoded:
|
|||||||
- The CPU mode that must be active.
|
- The CPU mode that must be active.
|
||||||
- A :term:`sub-target predicate` that must be satisfied by the currently active
|
- A :term:`sub-target predicate` that must be satisfied by the currently active
|
||||||
sub-target.
|
sub-target.
|
||||||
- :term:`Register constraint`\s that must be satisfied by the instruction's value
|
|
||||||
operands and results.
|
|
||||||
|
|
||||||
An encoding specifies an *encoding recipe* along with some *encoding bits* that
|
An encoding specifies an *encoding recipe* along with some *encoding bits* that
|
||||||
the recipe can use for native opcode fields etc. The encoding recipe has
|
the recipe can use for native opcode fields etc. The encoding recipe has
|
||||||
@@ -349,6 +347,83 @@ encodings only need the recipe predicates.
|
|||||||
|
|
||||||
.. autoclass:: EncRecipe
|
.. autoclass:: EncRecipe
|
||||||
|
|
||||||
|
Register constraints
|
||||||
|
====================
|
||||||
|
|
||||||
|
After an encoding recipe has been chosen for an instruction, it is the register
|
||||||
|
allocator's job to make sure that the recipe's :term:`Register constraint`\s
|
||||||
|
are satisfied. Most ISAs have separate integer and floating point registers,
|
||||||
|
and instructions can usually only use registers from one of the banks. Some
|
||||||
|
instruction encodings are even more constrained and can only use a subset of
|
||||||
|
the registers in a bank. These constraints are expressed in terms of register
|
||||||
|
classes.
|
||||||
|
|
||||||
|
Sometimes the result of an instruction is placed in a register that must be the
|
||||||
|
same as one of the input registers. Some instructions even use a fixed register
|
||||||
|
for inputs or results.
|
||||||
|
|
||||||
|
Each encoding recipe specifies separate constraints for its value operands and
|
||||||
|
result. These constraints are separate from the instruction predicate which can
|
||||||
|
only evaluate the instruction's immediate operands.
|
||||||
|
|
||||||
|
.. module:: cdsl.registers
|
||||||
|
.. autoclass:: RegBank
|
||||||
|
|
||||||
|
Register class constraints
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
The most common type of register constraint is the register class. It specifies
|
||||||
|
that an operand or result must be allocated one of the registers from the given
|
||||||
|
register class::
|
||||||
|
|
||||||
|
IntRegs = RegBank('IntRegs', ISA, 'General purpose registers', units=16, prefix='r')
|
||||||
|
GPR = RegClass(IntRegs)
|
||||||
|
R = EncRecipe('R', Binary, ins=(GPR, GPR), outs=GPR)
|
||||||
|
|
||||||
|
This defines an encoding recipe for the ``Binary`` instruction format where
|
||||||
|
both input operands must be allocated from the ``GPR`` register class.
|
||||||
|
|
||||||
|
.. autoclass:: RegClass
|
||||||
|
|
||||||
|
Tied register operands
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
In more compact machine code encodings, it is common to require that the result
|
||||||
|
register is the same as one of the inputs. This is represented with tied
|
||||||
|
operands::
|
||||||
|
|
||||||
|
CR = EncRecipe('CR', Binary, ins=(GPR, GPR), outs=0)
|
||||||
|
|
||||||
|
This indicates that the result value must be allocated to the same register as
|
||||||
|
the first input value. Tied operand constraints can only be used for result
|
||||||
|
values, so the number always refers to one of the input values.
|
||||||
|
|
||||||
|
Fixed register operands
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Some instructions use hard-coded input and output registers for some value
|
||||||
|
operands. An example is the ``pblendvb`` Intel SSE instruction which takes one
|
||||||
|
of its three value operands in the hard-coded ``%xmm0`` register::
|
||||||
|
|
||||||
|
XMM0 = FPR[0]
|
||||||
|
SSE66_XMM0 = EncRecipe('SSE66_XMM0', Ternary, ins=(FPR, FPR, XMM0), outs=0)
|
||||||
|
|
||||||
|
The syntax ``FPR[0]`` selects the first register from the ``FPR`` register
|
||||||
|
class which consists of all the XMM registers.
|
||||||
|
|
||||||
|
Stack operands
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Cretonne's register allocator can assign an SSA value to a stack slot if there
|
||||||
|
isn't enough registers. It will insert :cton:inst:`spill` and :cton:inst:`fill`
|
||||||
|
instructions as needed to satisfy instruction operand constraints, but it is
|
||||||
|
also possible to have instructions that can access stack slots directly::
|
||||||
|
|
||||||
|
CSS = EncRecipe('CSS', Unary, ins=GPR, outs=Stack(GPR))
|
||||||
|
|
||||||
|
An output stack value implies a store to the stack, an input value implies a
|
||||||
|
load.
|
||||||
|
|
||||||
.. module:: cdsl.isa
|
.. module:: cdsl.isa
|
||||||
|
|
||||||
Targets
|
Targets
|
||||||
@@ -366,6 +441,9 @@ The definitions for each supported target live in a package under
|
|||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. automodule:: isa.riscv
|
.. automodule:: isa.riscv
|
||||||
|
.. automodule:: isa.intel
|
||||||
|
.. automodule:: isa.arm32
|
||||||
|
.. automodule:: isa.arm64
|
||||||
|
|
||||||
|
|
||||||
Glossary
|
Glossary
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"""Defining instruction set architectures."""
|
"""Defining instruction set architectures."""
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from .predicates import And
|
from .predicates import And
|
||||||
|
from .registers import RegClass, Register
|
||||||
|
|
||||||
# The typing module is only required by mypy, and we don't use these imports
|
# The typing module is only required by mypy, and we don't use these imports
|
||||||
# outside type comments.
|
# outside type comments.
|
||||||
@@ -12,6 +13,8 @@ try:
|
|||||||
from .types import ValueType # noqa
|
from .types import ValueType # noqa
|
||||||
from .registers import RegBank # noqa
|
from .registers import RegBank # noqa
|
||||||
AnyPredicate = Union[Predicate, FieldPredicate]
|
AnyPredicate = Union[Predicate, FieldPredicate]
|
||||||
|
OperandConstraint = Union[RegClass, Register, int]
|
||||||
|
ConstraintSeq = Union[OperandConstraint, Tuple[OperandConstraint, ...]]
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -133,13 +136,24 @@ class EncRecipe(object):
|
|||||||
Many different instructions can be encoded by the same recipe, but they
|
Many different instructions can be encoded by the same recipe, but they
|
||||||
must all have the same instruction format.
|
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 name: Short mnemonic name for this recipe.
|
||||||
:param format: All encoded instructions must have this
|
:param format: All encoded instructions must have this
|
||||||
:py:class:`InstructionFormat`.
|
: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):
|
def __init__(self, name, format, ins, outs, instp=None, isap=None):
|
||||||
# type: (str, InstructionFormat, AnyPredicate, AnyPredicate) -> None
|
# type: (str, InstructionFormat, ConstraintSeq, ConstraintSeq, AnyPredicate, AnyPredicate) -> None # noqa
|
||||||
self.name = name
|
self.name = name
|
||||||
self.format = format
|
self.format = format
|
||||||
self.instp = instp
|
self.instp = instp
|
||||||
@@ -148,10 +162,29 @@ class EncRecipe(object):
|
|||||||
assert instp.predicate_context() == format
|
assert instp.predicate_context() == format
|
||||||
self.number = None # type: int
|
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):
|
def __str__(self):
|
||||||
# type: () -> str
|
# type: () -> str
|
||||||
return self.name
|
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):
|
class Encoding(object):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class RegBank(object):
|
|||||||
`units`, the remaining units are named using `prefix`.
|
`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
|
# type: (str, TargetISA, str, int, str, Sequence[str]) -> None
|
||||||
self.name = name
|
self.name = name
|
||||||
self.isa = isa
|
self.isa = isa
|
||||||
@@ -124,12 +124,16 @@ class RegClass(object):
|
|||||||
|
|
||||||
bank.classes.append(self)
|
bank.classes.append(self)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
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'
|
||||||
@@ -142,6 +146,7 @@ class RegClass(object):
|
|||||||
return RegClass(self.bank, count=c, width=w, start=s)
|
return RegClass(self.bank, count=c, width=w, start=s)
|
||||||
|
|
||||||
def mask(self):
|
def mask(self):
|
||||||
|
# type: () -> List[int]
|
||||||
"""
|
"""
|
||||||
Compute a bit-mask of the register units allocated by this register
|
Compute a bit-mask of the register units allocated by this register
|
||||||
class.
|
class.
|
||||||
@@ -173,3 +178,22 @@ class RegClass(object):
|
|||||||
if isinstance(obj, RegClass):
|
if isinstance(obj, RegClass):
|
||||||
assert obj.name is None
|
assert obj.name is None
|
||||||
obj.name = name
|
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.
|
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
|
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.
|
`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
|
from collections import OrderedDict, defaultdict
|
||||||
import math
|
import math
|
||||||
import itertools
|
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):
|
def emit_instp(instp, fmt):
|
||||||
@@ -399,6 +406,7 @@ def offset_type(length):
|
|||||||
|
|
||||||
|
|
||||||
def emit_recipe_names(isa, fmt):
|
def emit_recipe_names(isa, fmt):
|
||||||
|
# type: (TargetISA, srcgen.Formatter) -> None
|
||||||
"""
|
"""
|
||||||
Emit a table of encoding recipe names keyed by recipe number.
|
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))
|
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):
|
def gen_isa(isa, fmt):
|
||||||
# First assign numbers to relevant instruction predicates and generate the
|
# First assign numbers to relevant instruction predicates and generate the
|
||||||
# check_instp() function..
|
# check_instp() function..
|
||||||
@@ -446,6 +496,7 @@ def gen_isa(isa, fmt):
|
|||||||
cpumode, level1_tables[cpumode], level1_offt, fmt)
|
cpumode, level1_tables[cpumode], level1_offt, fmt)
|
||||||
|
|
||||||
emit_recipe_names(isa, fmt)
|
emit_recipe_names(isa, fmt)
|
||||||
|
emit_recipe_constraints(isa, fmt)
|
||||||
|
|
||||||
|
|
||||||
def generate(isas, out_dir):
|
def generate(isas, out_dir):
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from __future__ import absolute_import
|
|||||||
from cdsl.isa import EncRecipe
|
from cdsl.isa import EncRecipe
|
||||||
from cdsl.predicates import IsSignedInt
|
from cdsl.predicates import IsSignedInt
|
||||||
from base.formats import Binary, BinaryImm
|
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
|
# 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
|
# 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.
|
# R-type 32-bit instructions: These are mostly binary arithmetic instructions.
|
||||||
# The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8)
|
# 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.
|
# 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))
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ use ir::InstructionData;
|
|||||||
use ir::instructions::InstructionFormat;
|
use ir::instructions::InstructionFormat;
|
||||||
use ir::types;
|
use ir::types;
|
||||||
use isa::enc_tables::{Level1Entry, Level2Entry};
|
use isa::enc_tables::{Level1Entry, Level2Entry};
|
||||||
|
use isa::constraints::*;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/encoding-arm32.rs"));
|
include!(concat!(env!("OUT_DIR"), "/encoding-arm32.rs"));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod registers;
|
|||||||
use super::super::settings as shared_settings;
|
use super::super::settings as shared_settings;
|
||||||
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
||||||
use isa::Builder as IsaBuilder;
|
use isa::Builder as IsaBuilder;
|
||||||
use isa::{TargetIsa, RegInfo, Encoding, Legalize};
|
use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints};
|
||||||
use ir::{InstructionData, DataFlowGraph};
|
use ir::{InstructionData, DataFlowGraph};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -70,4 +70,8 @@ impl TargetIsa for Isa {
|
|||||||
fn recipe_names(&self) -> &'static [&'static str] {
|
fn recipe_names(&self) -> &'static [&'static str] {
|
||||||
&enc_tables::RECIPE_NAMES[..]
|
&enc_tables::RECIPE_NAMES[..]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recipe_constraints(&self) -> &'static [RecipeConstraints] {
|
||||||
|
&enc_tables::RECIPE_CONSTRAINTS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ use ir::InstructionData;
|
|||||||
use ir::instructions::InstructionFormat;
|
use ir::instructions::InstructionFormat;
|
||||||
use ir::types;
|
use ir::types;
|
||||||
use isa::enc_tables::{Level1Entry, Level2Entry};
|
use isa::enc_tables::{Level1Entry, Level2Entry};
|
||||||
|
use isa::constraints::*;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/encoding-arm64.rs"));
|
include!(concat!(env!("OUT_DIR"), "/encoding-arm64.rs"));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod registers;
|
|||||||
use super::super::settings as shared_settings;
|
use super::super::settings as shared_settings;
|
||||||
use isa::enc_tables::{lookup_enclist, general_encoding};
|
use isa::enc_tables::{lookup_enclist, general_encoding};
|
||||||
use isa::Builder as IsaBuilder;
|
use isa::Builder as IsaBuilder;
|
||||||
use isa::{TargetIsa, RegInfo, Encoding, Legalize};
|
use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints};
|
||||||
use ir::{InstructionData, DataFlowGraph};
|
use ir::{InstructionData, DataFlowGraph};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -63,4 +63,8 @@ impl TargetIsa for Isa {
|
|||||||
fn recipe_names(&self) -> &'static [&'static str] {
|
fn recipe_names(&self) -> &'static [&'static str] {
|
||||||
&enc_tables::RECIPE_NAMES[..]
|
&enc_tables::RECIPE_NAMES[..]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recipe_constraints(&self) -> &'static [RecipeConstraints] {
|
||||||
|
&enc_tables::RECIPE_CONSTRAINTS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
lib/cretonne/src/isa/constraints.rs
Normal file
66
lib/cretonne/src/isa/constraints.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
//! Register constraints for instruction operands.
|
||||||
|
//!
|
||||||
|
//! An encoding recipe specifies how an instruction is encoded as binary machine code, but it only
|
||||||
|
//! works if the operands and results satisfy certain constraints. Constraints on immediate
|
||||||
|
//! operands are checked by instruction predicates when the recipe is chosen.
|
||||||
|
//!
|
||||||
|
//! It is the register allocator's job to make sure that the register constraints on value operands
|
||||||
|
//! are satisfied.
|
||||||
|
|
||||||
|
use isa::{RegClass, RegUnit};
|
||||||
|
|
||||||
|
/// Register constraint for a single value operand or instruction result.
|
||||||
|
pub struct OperandConstraint {
|
||||||
|
/// The kind of constraint.
|
||||||
|
pub kind: ConstraintKind,
|
||||||
|
|
||||||
|
/// The register class of the operand.
|
||||||
|
///
|
||||||
|
/// This applies to all kinds of constraints, but with slightly different meaning.
|
||||||
|
pub regclass: RegClass,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The different kinds of operand constraints.
|
||||||
|
pub enum ConstraintKind {
|
||||||
|
/// This operand or result must be a register from the given register class.
|
||||||
|
Reg,
|
||||||
|
|
||||||
|
/// This operand or result must be a fixed register.
|
||||||
|
///
|
||||||
|
/// The constraint's `regclass` field is the top-level register class containing the fixed
|
||||||
|
/// register.
|
||||||
|
FixedReg(RegUnit),
|
||||||
|
|
||||||
|
/// This result value must use the same register as an input value operand. Input operands
|
||||||
|
/// can't be tied.
|
||||||
|
///
|
||||||
|
/// The associated number is the index of the input value operand this result is tied to.
|
||||||
|
///
|
||||||
|
/// The constraint's `regclass` field is the top-level register class containing the tied
|
||||||
|
/// operand's register class.
|
||||||
|
Tied(u8),
|
||||||
|
|
||||||
|
/// This operand must be a value in a stack slot.
|
||||||
|
///
|
||||||
|
/// The constraint's `regclass` field is the register class that would normally be used to load
|
||||||
|
/// and store values of this type.
|
||||||
|
Stack,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constraints for an encoding recipe.
|
||||||
|
pub struct RecipeConstraints {
|
||||||
|
/// Constraints for the instruction's fixed value operands.
|
||||||
|
///
|
||||||
|
/// If the instruction takes a variable number of operands, the register constraints for those
|
||||||
|
/// operands must be computed dynamically.
|
||||||
|
///
|
||||||
|
/// - For branches and jumps, EBB arguments must match the expectations of the destination EBB.
|
||||||
|
/// - For calls and returns, the calling convention ABI specifies constraints.
|
||||||
|
pub ins: &'static [OperandConstraint],
|
||||||
|
|
||||||
|
/// Constraints for the instruction's fixed results.
|
||||||
|
///
|
||||||
|
/// If the instruction produces a variable number of results, it's probably a call and the
|
||||||
|
/// constraints must be derived from the calling convention ABI.
|
||||||
|
pub outs: &'static [OperandConstraint],
|
||||||
|
}
|
||||||
@@ -4,5 +4,6 @@ use ir::InstructionData;
|
|||||||
use ir::instructions::InstructionFormat;
|
use ir::instructions::InstructionFormat;
|
||||||
use ir::types;
|
use ir::types;
|
||||||
use isa::enc_tables::{Level1Entry, Level2Entry};
|
use isa::enc_tables::{Level1Entry, Level2Entry};
|
||||||
|
use isa::constraints::*;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/encoding-intel.rs"));
|
include!(concat!(env!("OUT_DIR"), "/encoding-intel.rs"));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod registers;
|
|||||||
use super::super::settings as shared_settings;
|
use super::super::settings as shared_settings;
|
||||||
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
||||||
use isa::Builder as IsaBuilder;
|
use isa::Builder as IsaBuilder;
|
||||||
use isa::{TargetIsa, RegInfo, Encoding, Legalize};
|
use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints};
|
||||||
use ir::{InstructionData, DataFlowGraph};
|
use ir::{InstructionData, DataFlowGraph};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -70,4 +70,8 @@ impl TargetIsa for Isa {
|
|||||||
fn recipe_names(&self) -> &'static [&'static str] {
|
fn recipe_names(&self) -> &'static [&'static str] {
|
||||||
&enc_tables::RECIPE_NAMES[..]
|
&enc_tables::RECIPE_NAMES[..]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recipe_constraints(&self) -> &'static [RecipeConstraints] {
|
||||||
|
&enc_tables::RECIPE_CONSTRAINTS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@
|
|||||||
|
|
||||||
pub use isa::encoding::Encoding;
|
pub use isa::encoding::Encoding;
|
||||||
pub use isa::registers::{RegInfo, RegUnit, RegClass};
|
pub use isa::registers::{RegInfo, RegUnit, RegClass};
|
||||||
|
pub use isa::constraints::RecipeConstraints;
|
||||||
|
|
||||||
use settings;
|
use settings;
|
||||||
use ir::{InstructionData, DataFlowGraph};
|
use ir::{InstructionData, DataFlowGraph};
|
||||||
|
|
||||||
@@ -52,6 +54,7 @@ pub mod arm64;
|
|||||||
pub mod registers;
|
pub mod registers;
|
||||||
mod encoding;
|
mod encoding;
|
||||||
mod enc_tables;
|
mod enc_tables;
|
||||||
|
mod constraints;
|
||||||
|
|
||||||
/// Look for a supported ISA with the given `name`.
|
/// Look for a supported ISA with the given `name`.
|
||||||
/// Return a builder that can create a corresponding `TargetIsa`.
|
/// Return a builder that can create a corresponding `TargetIsa`.
|
||||||
@@ -140,11 +143,17 @@ pub trait TargetIsa {
|
|||||||
fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize>;
|
fn encode(&self, dfg: &DataFlowGraph, inst: &InstructionData) -> Result<Encoding, Legalize>;
|
||||||
|
|
||||||
/// Get a static array of names associated with encoding recipes in this ISA. Encoding recipes
|
/// Get a static array of names associated with encoding recipes in this ISA. Encoding recipes
|
||||||
/// are numbered starting from 0, corresponding to indexes into th name array.
|
/// are numbered starting from 0, corresponding to indexes into the name array.
|
||||||
///
|
///
|
||||||
/// This is just used for printing and parsing encodings in the textual IL format.
|
/// This is just used for printing and parsing encodings in the textual IL format.
|
||||||
fn recipe_names(&self) -> &'static [&'static str];
|
fn recipe_names(&self) -> &'static [&'static str];
|
||||||
|
|
||||||
|
/// Get a static array of value operand constraints associated with encoding recipes in this
|
||||||
|
/// ISA.
|
||||||
|
///
|
||||||
|
/// The constraints describe which registers can be used with an encoding recipe.
|
||||||
|
fn recipe_constraints(&self) -> &'static [RecipeConstraints];
|
||||||
|
|
||||||
/// Create an object that can display an ISA-dependent encoding properly.
|
/// Create an object that can display an ISA-dependent encoding properly.
|
||||||
fn display_enc(&self, enc: Encoding) -> encoding::DisplayEncoding {
|
fn display_enc(&self, enc: Encoding) -> encoding::DisplayEncoding {
|
||||||
encoding::DisplayEncoding {
|
encoding::DisplayEncoding {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use ir::instructions::InstructionFormat;
|
|||||||
use ir::types;
|
use ir::types;
|
||||||
use predicates;
|
use predicates;
|
||||||
use isa::enc_tables::{Level1Entry, Level2Entry};
|
use isa::enc_tables::{Level1Entry, Level2Entry};
|
||||||
|
use isa::constraints::*;
|
||||||
|
use super::registers::*;
|
||||||
|
|
||||||
// Include the generated encoding tables:
|
// Include the generated encoding tables:
|
||||||
// - `LEVEL1_RV32`
|
// - `LEVEL1_RV32`
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod registers;
|
|||||||
use super::super::settings as shared_settings;
|
use super::super::settings as shared_settings;
|
||||||
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
||||||
use isa::Builder as IsaBuilder;
|
use isa::Builder as IsaBuilder;
|
||||||
use isa::{TargetIsa, RegInfo, Encoding, Legalize};
|
use isa::{TargetIsa, RegInfo, Encoding, Legalize, RecipeConstraints};
|
||||||
use ir::{InstructionData, DataFlowGraph};
|
use ir::{InstructionData, DataFlowGraph};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -70,6 +70,10 @@ impl TargetIsa for Isa {
|
|||||||
fn recipe_names(&self) -> &'static [&'static str] {
|
fn recipe_names(&self) -> &'static [&'static str] {
|
||||||
&enc_tables::RECIPE_NAMES[..]
|
&enc_tables::RECIPE_NAMES[..]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recipe_constraints(&self) -> &'static [RecipeConstraints] {
|
||||||
|
&enc_tables::RECIPE_CONSTRAINTS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
Reference in New Issue
Block a user