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:
@@ -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.
|
||||||
@@ -141,15 +140,17 @@ class TailRecipe:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name, # type: str
|
name, # type: str
|
||||||
format, # type: InstructionFormat
|
format, # type: InstructionFormat
|
||||||
size, # type: int
|
size, # type: int
|
||||||
ins, # type: ConstraintSeq
|
ins, # type: ConstraintSeq
|
||||||
outs, # type: ConstraintSeq
|
outs, # type: ConstraintSeq
|
||||||
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
|
||||||
emit=None # type: str
|
when_prefixed=None, # type: TailRecipe
|
||||||
|
requires_prefix=False, # type: bool
|
||||||
|
emit=None # type: str
|
||||||
):
|
):
|
||||||
# type: (...) -> None
|
# type: (...) -> None
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user