60c60,61
< // Code to "assemble" microcode sequences
---
> // Code to "specialize" a microcode sequence to use a particular
> // variety of operands
64c65,207
< class MicroOpStatement:
---
> # This builds either a regular or macro op to implement the sequence of
> # ops we give it.
> def genInst(name, Name, ops):
> # If we can implement this instruction with exactly one microop, just
> # use that directly.
> newStmnt = ''
> if len(ops) == 1:
> decode_block = "return (X86StaticInst *)(%s);" % \
> ops[0].getAllocator()
> return ('', '', decode_block, '')
> else:
> # Build a macroop to contain the sequence of microops we've
> # been given.
> return genMacroOp(name, Name, ops)
> }};
>
> let {{
> # This code builds up a decode block which decodes based on switchval.
> # vals is a dict which matches case values with what should be decoded to.
> # builder is called on the exploded contents of "vals" values to generate
> # whatever code should be used.
> def doSplitDecode(name, Name, builder, switchVal, vals, default = None):
> header_output = ''
> decoder_output = ''
> decode_block = 'switch(%s) {\n' % switchVal
> exec_output = ''
> for (val, todo) in vals.items():
> (new_header_output,
> new_decoder_output,
> new_decode_block,
> new_exec_output) = builder(name, Name, *todo)
> header_output += new_header_output
> decoder_output += new_decoder_output
> decode_block += '\tcase %s: %s\n' % (val, new_decode_block)
> exec_output += new_exec_output
> if default:
> (new_header_output,
> new_decoder_output,
> new_decode_block,
> new_exec_output) = builder(name, Name, *default)
> header_output += new_header_output
> decoder_output += new_decoder_output
> decode_block += '\tdefault: %s\n' % new_decode_block
> exec_output += new_exec_output
> decode_block += '}\n'
> return (header_output, decoder_output, decode_block, exec_output)
> }};
>
> let {{
> class OpType(object):
> parser = re.compile(r"(?P<tag>[A-Z][A-Z]*)(?P<size>[a-z][a-z]*)|(r(?P<reg>[A-Za-z0-9][A-Za-z0-9]*))")
> def __init__(self, opTypeString):
> match = OpType.parser.search(opTypeString)
> if match == None:
> raise Exception, "Problem parsing operand type %s" % opTypeString
> self.reg = match.group("reg")
> self.tag = match.group("tag")
> self.size = match.group("size")
> }};
>
> let {{
>
> # This function specializes the given piece of code to use a particular
> # set of argument types described by "opTypes". These are "implemented"
> # in reverse order.
> def specializeInst(name, Name, code, opTypes):
> opNum = len(opTypes) - 1
> while len(opTypes):
> # print "Building a composite op with tags", opTypes
> # print "And code", code
> opNum = len(opTypes) - 1
> # A regular expression to find the operand placeholders we're
> # interested in.
> opRe = re.compile("%%(?P<operandNum>%d)(?=[^0-9]|$)" % opNum)
>
> # Parse the operand type strign we're working with
> print "About to parse tag %s" % opTypes[opNum]
> opType = OpType(opTypes[opNum])
>
> if opType.reg:
> #Figure out what to do with fixed register operands
> if opType.reg in ("Ax", "Bx", "Cx", "Dx"):
> code = opRe.sub("{INTREG_R%s}" % opType.reg.upper(), code)
> elif opType.reg == "Al":
> # We need a way to specify register width
> code = opRe.sub("{INTREG_RAX}", code)
> else:
> print "Didn't know how to encode fixed register %s!" % opType.reg
> elif opType.tag == None or opType.size == None:
> raise Exception, "Problem parsing operand tag: %s" % opType.tag
> elif opType.tag in ("C", "D", "G", "P", "S", "T", "V"):
> # Use the "reg" field of the ModRM byte to select the register
> code = opRe.sub("{(uint8_t)MODRM_REG}", code)
> elif opType.tag in ("E", "Q", "W"):
> # This might refer to memory or to a register. We need to
> # divide it up farther.
> regCode = opRe.sub("{(uint8_t)MODRM_RM}", code)
> regTypes = copy.copy(opTypes)
> regTypes.pop(-1)
> # This needs to refer to memory, but we'll fill in the details
> # later. It needs to take into account unaligned memory
> # addresses.
> memCode = opRe.sub("0", code)
> memTypes = copy.copy(opTypes)
> memTypes.pop(-1)
> return doSplitDecode(name, Name, specializeInst, "MODRM_MOD",
> {"3" : (regCode, regTypes)}, (memCode, memTypes))
> elif opType.tag in ("I", "J"):
> # Immediates are already in the instruction, so don't leave in
> # those parameters
> code = opRe.sub("", code)
> elif opType.tag == "M":
> # This needs to refer to memory, but we'll fill in the details
> # later. It needs to take into account unaligned memory
> # addresses.
> code = opRe.sub("0", code)
> elif opType.tag in ("PR", "R", "VR"):
> # There should probably be a check here to verify that mod
> # is equal to 11b
> code = opRe.sub("{(uint8_t)MODRM_RM}", code)
> else:
> raise Exception, "Unrecognized tag %s." % opType.tag
> opTypes.pop(-1)
>
> # At this point, we've built up "code" to have all the necessary extra
> # instructions needed to implement whatever types of operands were
> # specified. Now we'll assemble it it into a microOp sequence.
> ops = assembleMicro(code)
>
> # Build a macroop to contain the sequence of microops we've
> # constructed. The decode block will be used to fill in our
> # inner decode structure, and the rest will be concatenated and
> # passed back.
> return genInst(name, Name, ops)
> }};
>
> ////////////////////////////////////////////////////////////////////
> //
> // The microcode assembler
> //
>
> let {{
> class MicroOpStatement(object):
103a247
> }};
104a249
> let{{