diff options
author | Gabe Black <gblack@eecs.umich.edu> | 2007-04-04 23:35:20 +0000 |
---|---|---|
committer | Gabe Black <gblack@eecs.umich.edu> | 2007-04-04 23:35:20 +0000 |
commit | ff7b89beeeabec340d5b84ed813466682a93f928 (patch) | |
tree | 186aa9a3cf198e9a3192af3debcfeb32d779de4a /src/arch/x86/isa/microasm.isa | |
parent | ab2bed349b356b4784e1a6c8fdf6f4a86e27f543 (diff) | |
download | gem5-ff7b89beeeabec340d5b84ed813466682a93f928.tar.xz |
The process of going from an instruction definition to an instruction to be returned by the decoder has been fleshed out more. The following steps describe how an instruction implementation becomes a StaticInst.
1. Microops are created. These are StaticInsts use templates to provide a basic form of polymorphism without having to make the microassembler smarter.
2. An instruction class is created which has a "templated" microcode program as it's docstring. The template parameters are refernced with ^ following by a number.
3. An instruction in the decoder references an instruction template using it's mnemonic. The parameters to it's format end up replacing the placeholders. These parameters describe a source for an operand which could be memory, a register, or an immediate. It it's a register, the register index is used. If it's memory, eventually a load/store will be pre/postpended to the instruction template and it's destination register will be used in place of the ^. If it's an immediate, the immediate is used. Some operand types, specifically those that come from the ModRM byte, need to be decoded further into memory vs. register versions. This is accomplished by making the decode_block text for these instructions another case statement based off ModRM.
4. Once all of the template parameters have been handled, the instruction goes throw the microcode assembler which resolves labels and creates a list of python op objects. If an operand is a register, it uses a % prefix, an immediate uses $, and a label uses @. If the operand is just letters, numbers, and underscores, it can appear immediately after the prefix. If it's not, it can be encolsed in non nested {}s.
5. If there is a single "op" object (which corresponds to a single microop) the decoder is set up to return it directly. If not, a macroop wrapper is created around it.
In the future, I'm considering seperating the operand type specialization from the template substitution step. A problem this introduces is that either the template arguments need to be kept around for the specialization step, or they need to be re-extracted. Re-extraction might be the way to go so that the operand formats can be coded directly into the micro assembler template without having to pass them in as parameters. I don't know if that's actually useful, though.
src/arch/x86/isa/decoder/one_byte_opcodes.isa:
src/arch/x86/isa/microasm.isa:
src/arch/x86/isa/microops/microops.isa:
src/arch/x86/isa/operands.isa:
src/arch/x86/isa/microops/base.isa:
Implemented polymorphic microops and changed around the microcode assembler syntax.
--HG--
extra : convert_revision : e341f7b8ea9350a31e586a3d33250137e5954f43
Diffstat (limited to 'src/arch/x86/isa/microasm.isa')
-rw-r--r-- | src/arch/x86/isa/microasm.isa | 53 |
1 files changed, 33 insertions, 20 deletions
diff --git a/src/arch/x86/isa/microasm.isa b/src/arch/x86/isa/microasm.isa index 6d428881e..b94b55aab 100644 --- a/src/arch/x86/isa/microasm.isa +++ b/src/arch/x86/isa/microasm.isa @@ -135,36 +135,35 @@ let {{ 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) + 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) + 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) + 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) + 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) + 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) + memCode = opRe.sub("%0", code) memTypes = copy.copy(opTypes) memTypes.pop(-1) return doSplitDecode(name, Name, specializeInst, "MODRM_MOD", @@ -172,16 +171,16 @@ let {{ elif opType.tag in ("I", "J"): # Immediates are already in the instruction, so don't leave in # those parameters - code = opRe.sub("", code) + code = opRe.sub("${IMMEDIATE}", 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) + 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) + code = opRe.sub("%{(uint8_t)MODRM_RM}", code) else: raise Exception, "Unrecognized tag %s." % opType.tag opTypes.pop(-1) @@ -223,16 +222,24 @@ let {{ def getAllocator(self, *microFlags): args = '' + signature = "<" + emptySig = True for arg in self.args: - if arg.has_key("operandConst"): - args += ", %s" % arg["operandConst"] - elif arg.has_key("operandCode"): - args += ", %s" % arg["operandCode"] + if not emptySig: + signature += ", " + emptySig = False + if arg.has_key("operandImm"): + args += ", %s" % arg["operandImm"] + signature += ImmOpType + elif arg.has_key("operandReg"): + args += ", %s" % arg["operandReg"] + signature += RegOpType elif arg.has_key("operandLabel"): raise Exception, "Found a label while creating allocator string." else: raise Exception, "Unrecognized operand type." - return 'new %s(machInst%s%s)' % (self.className, self.microFlagsText(microFlags), args) + signature += ">" + return 'new %s%s(machInst%s%s)' % (self.className, signature, self.microFlagsText(microFlags), args) }}; let {{ @@ -260,7 +267,7 @@ let{{ # time. Each expression expects the thing it's looking for to be at # the beginning of the line, so the previous component is stripped # before continuing. - labelRe = re.compile(r'^[ \t]*(?P<label>[a-zA-Z_]\w*)[ \t]:') + labelRe = re.compile(r'^[ \t]*(?P<label>\w\w*)[ \t]:') lineRe = re.compile(r'^(?P<line>[^\n][^\n]*)$') classRe = re.compile(r'^[ \t]*(?P<className>[a-zA-Z_]\w*)') # This recognizes three different flavors of operands: @@ -271,7 +278,12 @@ let{{ # underscore, which is optionally followed by a sequence of # capital or small letters, underscores, or digts between 0 and 9 opRe = re.compile( \ - r'^[ \t]*((?P<operandLabel>[a-zA-Z_]\w*)|(?P<operandConst>[0-9][0-9]*)|(\{(?P<operandCode>[^}]*)\}))') + r'^[ \t]*((\@(?P<operandLabel0>\w\w*))|' + + r'(\@\{(?P<operandLabel1>[^}]*)\})|' + + r'(\%(?P<operandReg0>\w\w*))|' + + r'(\%\{(?P<operandReg1>[^}]*)\})|' + + r'(\$(?P<operandImm0>\w\w*))|' + + r'(\$\{(?P<operandImm1>[^}]*)\}))') lineMatch = lineRe.search(code) while lineMatch != None: statement = MicroOpStatement() @@ -310,9 +322,10 @@ let{{ # representations of operand values. Different forms might be # needed in different places, for instance to replace a label # with an offset. - for opType in ("operandLabel", "operandConst", "operandCode"): + for opType in ("operandLabel0", "operandReg0", "operandImm0", + "operandLabel1", "operandReg1", "operandImm1"): if opMatch.group(opType): - statement.args[-1][opType] = opMatch.group(opType) + statement.args[-1][opType[:-1]] = opMatch.group(opType) if len(statement.args[-1]) == 0: print "Problem parsing operand in statement: %s" \ % orig_line @@ -338,7 +351,7 @@ let{{ # This is assuming that intra microcode branches go to # the next micropc + displacement, or # micropc + 1 + displacement. - arg["operandConst"] = labels[arg["operandLabel"]] - micropc - 1 + arg["operandImm"] = labels[arg["operandLabel"]] - micropc - 1 micropc += 1 return statements }}; |