diff --git a/lib/cretonne/meta/cdsl/isa.py b/lib/cretonne/meta/cdsl/isa.py index a44a1e6333..f1125f59da 100644 --- a/lib/cretonne/meta/cdsl/isa.py +++ b/lib/cretonne/meta/cdsl/isa.py @@ -7,7 +7,7 @@ from .ast import Apply # The typing module is only required by mypy, and we don't use these imports # outside type comments. try: - from typing import Tuple, Union, Any, Iterable, Sequence, List, Set, TYPE_CHECKING # noqa + from typing import Tuple, Union, Any, Iterable, Sequence, List, Set, Dict, TYPE_CHECKING # noqa if TYPE_CHECKING: from .instructions import MaybeBoundInst, InstructionGroup, InstructionFormat # noqa from .predicates import PredNode # noqa @@ -220,13 +220,27 @@ class EncRecipe(object): if isinstance(c, int): # An integer constraint is bound to a value operand. # Check that it is in range. - assert c >= 0 - if not self.format.has_value_list: - assert c < self.format.num_value_operands + assert c >= 0 and c < len(self.ins) else: assert isinstance(c, RegClass) or isinstance(c, Register) return seq + def ties(self): + # type: () -> Tuple[Dict[int, int], Dict[int, int]] + """ + Return two dictionaries representing the tied operands. + + The first maps input number to tied output number, the second maps + output number to tied input number. + """ + i2o = dict() # type: Dict[int, int] + o2i = dict() # type: Dict[int, int] + for o, i in enumerate(self.outs): + if isinstance(i, int): + i2o[i] = o + o2i[o] = i + return (i2o, o2i) + class Encoding(object): """ diff --git a/lib/cretonne/meta/gen_encoding.py b/lib/cretonne/meta/gen_encoding.py index bbe1df7591..f19377b8bd 100644 --- a/lib/cretonne/meta/gen_encoding.py +++ b/lib/cretonne/meta/gen_encoding.py @@ -60,9 +60,9 @@ from cdsl.registers import RegClass, Register from cdsl.predicates import FieldPredicate try: - from typing import Sequence, Set, Tuple, List, Iterable, DefaultDict, TYPE_CHECKING # noqa + from typing import Sequence, Set, Tuple, List, Dict, Iterable, DefaultDict, TYPE_CHECKING # noqa if TYPE_CHECKING: - from cdsl.isa import TargetISA, OperandConstraint, Encoding, CPUMode # noqa + from cdsl.isa import TargetISA, OperandConstraint, Encoding, CPUMode, EncRecipe # noqa from cdsl.predicates import PredNode, PredLeaf # noqa from cdsl.types import ValueType # noqa from cdsl.instructions import Instruction # noqa @@ -470,13 +470,20 @@ def emit_recipe_constraints(isa, fmt): .format(len(isa.all_recipes)), '];'): for r in isa.all_recipes: fmt.comment(r.name) + tied_i2o, tied_o2i = r.ties() with fmt.indented('RecipeConstraints {', '},'): - emit_operand_constraints(r.ins, 'ins', fmt) - emit_operand_constraints(r.outs, 'outs', fmt) + emit_operand_constraints(r, r.ins, 'ins', tied_i2o, fmt) + emit_operand_constraints(r, r.outs, 'outs', tied_o2i, fmt) -def emit_operand_constraints(seq, field, fmt): - # type: (Sequence[OperandConstraint], str, srcgen.Formatter) -> None +def emit_operand_constraints( + recipe, # type: EncRecipe + seq, # type: Sequence[OperandConstraint] + field, # type: str + tied, # type: Dict[int, int] + fmt # type: srcgen.Formatter + ): + # type: (...) -> None """ Emit a struct field initializer for an array of operand constraints. """ @@ -484,16 +491,25 @@ def emit_operand_constraints(seq, field, fmt): fmt.line('{}: &[],'.format(field)) return with fmt.indented('{}: &['.format(field), '],'): - for cons in seq: + for n, cons in enumerate(seq): with fmt.indented('OperandConstraint {', '},'): if isinstance(cons, RegClass): - fmt.line('kind: ConstraintKind::Reg,') - fmt.line('regclass: {},'.format(cons)) + if n in tied: + fmt.format('kind: ConstraintKind::Tied({}),', tied[n]) + else: + fmt.line('kind: ConstraintKind::Reg,') + fmt.format('regclass: {},', cons) elif isinstance(cons, Register): - fmt.line( - 'kind: ConstraintKind::FixedReg({}),' - .format(cons.unit)) - fmt.line('regclass: {},'.format(cons.regclass)) + assert n not in tied, "Can't tie fixed register operand" + fmt.format( + 'kind: ConstraintKind::FixedReg({}),', cons.unit) + fmt.format('regclass: {},', cons.regclass) + elif isinstance(cons, int): + # This is a tied output constraint. It should never happen + # for input constraints. + assert cons == tied[n], "Invalid tied constraint" + fmt.format('kind: ConstraintKind::Tied({}),', cons) + fmt.format('regclass: {},', recipe.ins[cons]) else: raise AssertionError( 'Unsupported constraint {}'.format(cons)) diff --git a/lib/cretonne/src/isa/constraints.rs b/lib/cretonne/src/isa/constraints.rs index 23cd923f93..fa30fd24fe 100644 --- a/lib/cretonne/src/isa/constraints.rs +++ b/lib/cretonne/src/isa/constraints.rs @@ -33,13 +33,14 @@ pub enum ConstraintKind { /// register. FixedReg(RegUnit), - /// This result value must use the same register as an input value operand. Input operands - /// can't be tied. + /// This result value must use the same register as an input value operand. /// - /// The associated number is the index of the input value operand this result is tied to. + /// The associated number is the index of the input value operand this result is tied to. The + /// constraint's `regclass` field is the same as the tied operand's register class. /// - /// The constraint's `regclass` field is the top-level register class containing the tied - /// operand's register class. + /// When an (in, out) operand pair is tied, this constraint kind appears in both the `ins` and + /// the `outs` arrays. The constraint for the in operand is `Tied(out)`, and the constraint for + /// the out operand is `Tied(in)`. Tied(u8), /// This operand must be a value in a stack slot.