Avoid widening TailRecipe register constraints automatically.

Most recipes with an ABCD constraint can handle the full GPR register
class when a REX prefix is applied, but not all. The "icscc" macro
recipe always generates a setCC instruction with no REX prefix, so it
can only write the ABCD registers, even in its REX form.

Don't automatically rewrite ABCD constraints to GPR constraints when
applying a REX prefix to a tail recipe. Instead, allow individual ABCD
recipes to specify a "when_prefixed" alternative recipe to use. This
also eliminates the spurious Rex*abcd recipe names which didn't have an
ABCD constraint.

Also allow recipes to specify that a REX prefix is required by setting
the prefix_required flag. This is used by recipes like t8jccb which
explicitly accesses an 8-bit register with a GPR constraint which is
only valid with a prefix.
This commit is contained in:
Jakob Stoklund Olesen
2017-10-06 14:13:15 -07:00
parent ac8c8a676a
commit ecd537ecd6

View File

@@ -104,19 +104,10 @@ NOREX_MAP = {
FPR: FPR8 FPR: FPR8
} }
# Register class mapping for REX instructions. The ABCD constraint no longer
# applies.
REX_MAP = {
ABCD: GPR
}
def map_regs_norex(regs):
def map_regs( # type: (Sequence[OperandConstraint]) -> Sequence[OperandConstraint]
regs, # type: Sequence[OperandConstraint] return tuple(NOREX_MAP.get(rc, rc) if isinstance(rc, RegClass) else rc
mapping # type: Dict[RegClass, RegClass]
):
# type: (...) -> Sequence[OperandConstraint]
return tuple(mapping.get(rc, rc) if isinstance(rc, RegClass) else rc
for rc in regs) for rc in regs)
@@ -134,6 +125,14 @@ class TailRecipe:
The arguments are the same as for an `EncRecipe`, except for `size` which The arguments are the same as for an `EncRecipe`, except for `size` which
does not include the size of the opcode. does not include the size of the opcode.
The `when_prefixed` parameter specifies a recipe that should be substituted
for this one when a REX (or VEX) prefix is present. This is relevant for
recipes that can only access the ABCD registers without a REX prefix, but
are able to access all registers with a prefix.
The `requires_prefix` parameter indicates that the recipe can't be used
without a REX prefix.
The `emit` parameter contains Rust code to actually emit an encoding, like The `emit` parameter contains Rust code to actually emit an encoding, like
`EncRecipe` does it. Additionally, the text `PUT_OP` is substituted with `EncRecipe` does it. Additionally, the text `PUT_OP` is substituted with
the proper `put_*` function from the `intel/binemit.rs` module. the proper `put_*` function from the `intel/binemit.rs` module.
@@ -149,6 +148,8 @@ class TailRecipe:
branch_range=None, # type: int branch_range=None, # type: int
instp=None, # type: PredNode instp=None, # type: PredNode
isap=None, # type: PredNode isap=None, # type: PredNode
when_prefixed=None, # type: TailRecipe
requires_prefix=False, # type: bool
emit=None # type: str emit=None # type: str
): ):
# type: (...) -> None # type: (...) -> None
@@ -160,6 +161,8 @@ class TailRecipe:
self.branch_range = branch_range self.branch_range = branch_range
self.instp = instp self.instp = instp
self.isap = isap self.isap = isap
self.when_prefixed = when_prefixed
self.requires_prefix = requires_prefix
self.emit = emit self.emit = emit
# Cached recipes, keyed by name prefix. # Cached recipes, keyed by name prefix.
@@ -171,6 +174,7 @@ class TailRecipe:
Create an encoding recipe and encoding bits for the opcode bytes in Create an encoding recipe and encoding bits for the opcode bytes in
`ops`. `ops`.
""" """
assert not self.requires_prefix, "Tail recipe requires REX prefix."
rrr = kwargs.get('rrr', 0) rrr = kwargs.get('rrr', 0)
w = kwargs.get('w', 0) w = kwargs.get('w', 0)
name, bits = decode_ops(ops, rrr, w) name, bits = decode_ops(ops, rrr, w)
@@ -193,8 +197,8 @@ class TailRecipe:
isap=self.isap, isap=self.isap,
emit=replace_put_op(self.emit, name)) emit=replace_put_op(self.emit, name))
recipe.ins = map_regs(recipe.ins, NOREX_MAP) recipe.ins = map_regs_norex(recipe.ins)
recipe.outs = map_regs(recipe.outs, NOREX_MAP) recipe.outs = map_regs_norex(recipe.outs)
self.recipes[name] = recipe self.recipes[name] = recipe
return (self.recipes[name], bits) return (self.recipes[name], bits)
@@ -208,6 +212,10 @@ class TailRecipe:
not. For instructions that don't require a REX prefix, two encodings not. For instructions that don't require a REX prefix, two encodings
should be added: One with REX and one without. should be added: One with REX and one without.
""" """
# Use the prefixed alternative recipe when applicable.
if self.when_prefixed:
return self.when_prefixed.rex(*ops, **kwargs)
rrr = kwargs.get('rrr', 0) rrr = kwargs.get('rrr', 0)
w = kwargs.get('w', 0) w = kwargs.get('w', 0)
name, bits = decode_ops(ops, rrr, w) name, bits = decode_ops(ops, rrr, w)
@@ -230,8 +238,6 @@ class TailRecipe:
instp=self.instp, instp=self.instp,
isap=self.isap, isap=self.isap,
emit=replace_put_op(self.emit, name)) emit=replace_put_op(self.emit, name))
recipe.ins = map_regs(recipe.ins, REX_MAP)
recipe.outs = map_regs(recipe.outs, REX_MAP)
self.recipes[name] = recipe self.recipes[name] = recipe
return (self.recipes[name], bits) return (self.recipes[name], bits)
@@ -314,6 +320,7 @@ urm = TailRecipe(
# XX /r. Same as urm, but input limited to ABCD. # XX /r. Same as urm, but input limited to ABCD.
urm_abcd = TailRecipe( urm_abcd = TailRecipe(
'urm_abcd', Unary, size=1, ins=ABCD, outs=GPR, 'urm_abcd', Unary, size=1, ins=ABCD, outs=GPR,
when_prefixed=urm,
emit=''' emit='''
PUT_OP(bits, rex2(in_reg0, out_reg0), sink); PUT_OP(bits, rex2(in_reg0, out_reg0), sink);
modrm_rr(in_reg0, out_reg0, sink); modrm_rr(in_reg0, out_reg0, sink);
@@ -478,10 +485,11 @@ st = TailRecipe(
''') ''')
# XX /r register-indirect store with no offset. # XX /r register-indirect store with no offset.
# Only ABCD allowed for stored value. This is for byte stores. # Only ABCD allowed for stored value. This is for byte stores with no REX.
st_abcd = TailRecipe( st_abcd = TailRecipe(
'st_abcd', Store, size=1, ins=(ABCD, GPR), outs=(), 'st_abcd', Store, size=1, ins=(ABCD, GPR), outs=(),
instp=IsEqual(Store.offset, 0), instp=IsEqual(Store.offset, 0),
when_prefixed=st,
emit=''' emit='''
PUT_OP(bits, rex2(in_reg1, in_reg0), sink); PUT_OP(bits, rex2(in_reg1, in_reg0), sink);
modrm_rm(in_reg1, in_reg0, sink); modrm_rm(in_reg1, in_reg0, sink);
@@ -509,6 +517,7 @@ stDisp8 = TailRecipe(
stDisp8_abcd = TailRecipe( stDisp8_abcd = TailRecipe(
'stDisp8_abcd', Store, size=2, ins=(ABCD, GPR), outs=(), 'stDisp8_abcd', Store, size=2, ins=(ABCD, GPR), outs=(),
instp=IsSignedInt(Store.offset, 8), instp=IsSignedInt(Store.offset, 8),
when_prefixed=stDisp8,
emit=''' emit='''
PUT_OP(bits, rex2(in_reg1, in_reg0), sink); PUT_OP(bits, rex2(in_reg1, in_reg0), sink);
modrm_disp8(in_reg1, in_reg0, sink); modrm_disp8(in_reg1, in_reg0, sink);
@@ -536,6 +545,7 @@ stDisp32 = TailRecipe(
''') ''')
stDisp32_abcd = TailRecipe( stDisp32_abcd = TailRecipe(
'stDisp32_abcd', Store, size=5, ins=(ABCD, GPR), outs=(), 'stDisp32_abcd', Store, size=5, ins=(ABCD, GPR), outs=(),
when_prefixed=stDisp32,
emit=''' emit='''
PUT_OP(bits, rex2(in_reg1, in_reg0), sink); PUT_OP(bits, rex2(in_reg1, in_reg0), sink);
modrm_disp32(in_reg1, in_reg0, sink); modrm_disp32(in_reg1, in_reg0, sink);
@@ -786,9 +796,22 @@ tjccd = TailRecipe(
# #
# Same as tjccb, but only looks at the low 8 bits of the register, for b1 # Same as tjccb, but only looks at the low 8 bits of the register, for b1
# types. # types.
t8jccb = TailRecipe(
't8jccb', Branch, size=1 + 2, ins=GPR, outs=(),
branch_range=8,
requires_prefix=True,
emit='''
// test8 r, r.
PUT_OP((bits & 0xff00) | 0x84, rex2(in_reg0, in_reg0), sink);
modrm_rr(in_reg0, in_reg0, sink);
// Jcc instruction.
sink.put1(bits as u8);
disp1(destination, func, sink);
''')
t8jccb_abcd = TailRecipe( t8jccb_abcd = TailRecipe(
't8jccb_abcd', Branch, size=1 + 2, ins=ABCD, outs=(), 't8jccb_abcd', Branch, size=1 + 2, ins=ABCD, outs=(),
branch_range=8, branch_range=8,
when_prefixed=t8jccb,
emit=''' emit='''
// test8 r, r. // test8 r, r.
PUT_OP((bits & 0xff00) | 0x84, rex2(in_reg0, in_reg0), sink); PUT_OP((bits & 0xff00) | 0x84, rex2(in_reg0, in_reg0), sink);
@@ -798,9 +821,23 @@ t8jccb_abcd = TailRecipe(
disp1(destination, func, sink); disp1(destination, func, sink);
''') ''')
t8jccd = TailRecipe(
't8jccd', Branch, size=1 + 6, ins=GPR, outs=(),
branch_range=32,
requires_prefix=True,
emit='''
// test8 r, r.
PUT_OP((bits & 0xff00) | 0x84, rex2(in_reg0, in_reg0), sink);
modrm_rr(in_reg0, in_reg0, sink);
// Jcc instruction.
sink.put1(0x0f);
sink.put1(bits as u8);
disp4(destination, func, sink);
''')
t8jccd_abcd = TailRecipe( t8jccd_abcd = TailRecipe(
't8jccd_abcd', Branch, size=1 + 6, ins=ABCD, outs=(), 't8jccd_abcd', Branch, size=1 + 6, ins=ABCD, outs=(),
branch_range=32, branch_range=32,
when_prefixed=t8jccd,
emit=''' emit='''
// test8 r, r. // test8 r, r.
PUT_OP((bits & 0xff00) | 0x84, rex2(in_reg0, in_reg0), sink); PUT_OP((bits & 0xff00) | 0x84, rex2(in_reg0, in_reg0), sink);
@@ -827,6 +864,7 @@ t8jccd_abcd = TailRecipe(
# #
# This bandaid macro doesn't support a REX prefix for the final `setCC` # This bandaid macro doesn't support a REX prefix for the final `setCC`
# instruction, so it is limited to the `ABCD` register class for booleans. # instruction, so it is limited to the `ABCD` register class for booleans.
# The omission of a `when_prefixed` alternative is deliberate here.
icscc = TailRecipe( icscc = TailRecipe(
'icscc', IntCompare, size=1 + 3, ins=(GPR, GPR), outs=ABCD, 'icscc', IntCompare, size=1 + 3, ins=(GPR, GPR), outs=ABCD,
emit=''' emit='''
@@ -866,6 +904,7 @@ icscc = TailRecipe(
# EQ 100 000 # EQ 100 000
# #
# Not all floating point condition codes are supported. # Not all floating point condition codes are supported.
# The omission of a `when_prefixed` alternative is deliberate here.
fcscc = TailRecipe( fcscc = TailRecipe(
'fcscc', FloatCompare, size=1 + 3, ins=(FPR, FPR), outs=ABCD, 'fcscc', FloatCompare, size=1 + 3, ins=(FPR, FPR), outs=ABCD,
instp=Or(*(IsEqual(FloatCompare.cond, cc) instp=Or(*(IsEqual(FloatCompare.cond, cc)