""" Intel Encoding recipes. """ from __future__ import absolute_import from cdsl.isa import EncRecipe from cdsl.predicates import IsSignedInt, IsEqual from base.formats import UnaryImm, Binary, BinaryImm, Store, Load from base.formats import MultiAry, Call, IndirectCall from .registers import GPR, ABCD try: from typing import Tuple, Dict # noqa from cdsl.instructions import InstructionFormat # noqa from cdsl.isa import ConstraintSeq, BranchRange, PredNode # noqa except ImportError: pass # Opcode representation. # # Cretonne requires each recipe to have a single encoding size in bytes, and # Intel opcodes are variable length, so we use separate recipes for different # styles of opcodes and prefixes. The opcode format is indicated by the recipe # name prefix: OPCODE_PREFIX = { # Prefix bytes Name mmpp (): ('Op1', 0b0000), (0x66,): ('Mp1', 0b0001), (0xf3,): ('Mp1', 0b0010), (0xf2,): ('Mp1', 0b0011), (0x0f,): ('Op2', 0b0100), (0x66, 0x0f): ('Mp2', 0b0101), (0xf3, 0x0f): ('Mp2', 0b0110), (0xf2, 0x0f): ('Mp2', 0b0111), (0x0f, 0x38): ('Op3', 0b1000), (0x66, 0x0f, 0x38): ('Mp3', 0b1001), (0xf3, 0x0f, 0x38): ('Mp3', 0b1010), (0xf2, 0x0f, 0x38): ('Mp3', 0b1011), (0x0f, 0x3a): ('Op3', 0b1100), (0x66, 0x0f, 0x3a): ('Mp3', 0b1101), (0xf3, 0x0f, 0x3a): ('Mp3', 0b1110), (0xf2, 0x0f, 0x3a): ('Mp3', 0b1111) } # VEX/XOP and EVEX prefixes are not yet supported. # # The encoding bits are: # # 0-7: The opcode byte . # 8-9: pp, mandatory prefix: # 00 none (Op*) # 01 66 (Mp*) # 10 F3 (Mp*) # 11 F2 (Mp*) # 10-11: mm, opcode map: # 00 (Op1/Mp1) # 01 0F (Op2/Mp2) # 10 0F 38 (Op3/Mp3) # 11 0F 3A (Op3/Mp3) # 12-14 rrr, opcode bits for the ModR/M byte for certain opcodes. # 15: REX.W bit (or VEX.W/E) # # There is some redundancy between bits 8-11 and the recipe names, but we have # enough bits, and the pp+mm format is ready for supporting VEX prefixes. def decode_ops(ops, rrr=0, w=0): # type: (Tuple[int, ...], int, int) -> Tuple[str, int] """ Given a sequence of opcode bytes, compute the recipe name prefix and encoding bits. """ assert rrr <= 0b111 assert w <= 1 name, mmpp = OPCODE_PREFIX[ops[:-1]] op = ops[-1] assert op <= 256 return (name, op | (mmpp << 8) | (rrr << 12) | (w << 15)) class TailRecipe: """ Generate encoding recipes on demand. Intel encodings are somewhat orthogonal with the opcode representation on one side and the ModR/M, SIB and immediate fields on the other side. A `TailRecipe` represents the part of an encoding that follow the opcode. It is used to generate full encoding recipes on demand when combined with an opcode. The arguments are the same as for an `EncRecipe`, except for `size` which does not include the size of the opcode. """ def __init__( self, name, # type: str format, # type: InstructionFormat size, # type: int ins, # type: ConstraintSeq outs, # type: ConstraintSeq branch_range=None, # type: BranchRange instp=None, # type: PredNode isap=None # type: PredNode ): # type: (...) -> None self.name = name self.format = format self.size = size self.ins = ins self.outs = outs self.branch_range = branch_range self.instp = instp self.isap = isap # Cached recipes, keyed by name prefix. self.recipes = dict() # type: Dict[str, EncRecipe] def __call__(self, *ops, **kwargs): # type: (*int, **int) -> Tuple[EncRecipe, int] """ Create an encoding recipe and encoding bits for the opcode bytes in `ops`. """ rrr = kwargs.get('rrr', 0) w = kwargs.get('w', 0) name, bits = decode_ops(ops, rrr, w) if name not in self.recipes: self.recipes[name] = EncRecipe( name + self.name, self.format, len(ops) + self.size, ins=self.ins, outs=self.outs, branch_range=self.branch_range, instp=self.instp, isap=self.isap) return (self.recipes[name], bits) # XX /r rr = TailRecipe('rr', Binary, size=1, ins=(GPR, GPR), outs=0) # XX /n with one arg in %rcx, for shifts. rc = TailRecipe('rc', Binary, size=1, ins=(GPR, GPR.rcx), outs=0) # XX /n ib with 8-bit immediate sign-extended. rib = TailRecipe( 'rib', BinaryImm, size=2, ins=GPR, outs=0, instp=IsSignedInt(BinaryImm.imm, 8)) # XX /n id with 32-bit immediate sign-extended. rid = TailRecipe( 'rid', BinaryImm, size=5, ins=GPR, outs=0, instp=IsSignedInt(BinaryImm.imm, 32)) # XX+rd id unary with 32-bit immediate. uid = TailRecipe( 'uid', UnaryImm, size=4, ins=(), outs=GPR, instp=IsSignedInt(UnaryImm.imm, 32)) # # Store recipes. # # XX /r register-indirect store with no offset. st = TailRecipe( 'st', Store, size=1, ins=(GPR, GPR), outs=(), instp=IsEqual(Store.offset, 0)) # XX /r register-indirect store with no offset. # Only ABCD allowed for stored value. This is for byte stores. st_abcd = TailRecipe( 'st_abcd', Store, size=1, ins=(ABCD, GPR), outs=(), instp=IsEqual(Store.offset, 0)) # XX /r register-indirect store with 8-bit offset. stDisp8 = TailRecipe( 'stDisp8', Store, size=2, ins=(GPR, GPR), outs=(), instp=IsSignedInt(Store.offset, 8)) stDisp8_abcd = TailRecipe( 'stDisp8_abcd', Store, size=2, ins=(ABCD, GPR), outs=(), instp=IsSignedInt(Store.offset, 8)) # XX /r register-indirect store with 32-bit offset. stDisp32 = TailRecipe('stDisp32', Store, size=5, ins=(GPR, GPR), outs=()) stDisp32_abcd = TailRecipe( 'stDisp32_abcd', Store, size=5, ins=(ABCD, GPR), outs=()) # # Load recipes # # XX /r load with no offset. ld = TailRecipe( 'ld', Load, size=1, ins=(GPR), outs=(GPR), instp=IsEqual(Load.offset, 0)) # XX /r load with 8-bit offset. ldDisp8 = TailRecipe( 'ldDisp8', Load, size=2, ins=(GPR), outs=(GPR), instp=IsSignedInt(Load.offset, 8)) # XX /r load with 32-bit offset. ldDisp32 = TailRecipe( 'ldDisp32', Load, size=5, ins=(GPR), outs=(GPR), instp=IsSignedInt(Load.offset, 32)) # # Call/return # call_id = TailRecipe('call_id', Call, size=4, ins=(), outs=()) call_r = TailRecipe('call_r', IndirectCall, size=1, ins=GPR, outs=()) ret = TailRecipe('ret', MultiAry, size=0, ins=(), outs=())