From 1f7ed5b7b4f0435ef61f5db6c701f22aacee369d Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Fri, 8 Jun 2007 16:09:43 +0000 Subject: Big changes to use the new microcode assembler. --HG-- extra : convert_revision : 7d1a43c5791a2e7e30533746da3dd7036a5b8799 --- src/arch/x86/isa/formats/multi.isa | 20 +----- src/arch/x86/isa/includes.isa | 4 ++ src/arch/x86/isa/macroop.isa | 117 +++++++++++++++++++++++++++--------- src/arch/x86/isa/main.isa | 38 ++++-------- src/arch/x86/isa/microasm.isa | 17 +++++- src/arch/x86/isa/microops/regop.isa | 31 +++++----- src/arch/x86/isa/specialize.isa | 83 ++++++++++++------------- 7 files changed, 179 insertions(+), 131 deletions(-) diff --git a/src/arch/x86/isa/formats/multi.isa b/src/arch/x86/isa/formats/multi.isa index 8f91c249c..e47c8776e 100644 --- a/src/arch/x86/isa/formats/multi.isa +++ b/src/arch/x86/isa/formats/multi.isa @@ -60,27 +60,13 @@ // Instructions that do the same thing to multiple sets of arguments. // -let {{ - def doInst(name, Name, opTypeSet): - if not instDict.has_key(Name): - raise Exception, "Unrecognized instruction: %s" % Name - inst = instDict[Name]() - return inst.emit(opTypeSet) -}}; - def format Inst(*opTypeSet) {{ - (header_output, - decoder_output, - decode_block, - exce_output) = doInst(name, Name, list(opTypeSet)).makeList() + decode_block = specializeInst(Name, list(opTypeSet), EmulEnv()) }}; def format MultiInst(switchVal, *opTypeSets) {{ switcher = {} for (count, opTypeSet) in zip(xrange(len(opTypeSets)), opTypeSets): - switcher[count] = (opTypeSet,) - (header_output, - decoder_output, - decode_block, - exec_output) = doSplitDecode(name, Name, doInst, switchVal, switcher).makeList() + switcher[count] = (opTypeSet, EmulEnv()) + decode_block = doSplitDecode(Name, specializeInst, switchVal, switcher) }}; diff --git a/src/arch/x86/isa/includes.isa b/src/arch/x86/isa/includes.isa index 3440ec5da..269233c07 100644 --- a/src/arch/x86/isa/includes.isa +++ b/src/arch/x86/isa/includes.isa @@ -99,6 +99,7 @@ output header {{ #include "arch/x86/faults.hh" #include "arch/x86/isa_traits.hh" #include "arch/x86/regfile.hh" +#include "arch/x86/types.hh" #include "base/misc.hh" #include "cpu/static_inst.hh" #include "mem/packet.hh" @@ -106,6 +107,9 @@ output header {{ }}; output decoder {{ + +namespace X86Macroop { +}; #include "base/cprintf.hh" #include "base/loader/symtab.hh" #include "cpu/thread_context.hh" // for Jump::branchTarget() diff --git a/src/arch/x86/isa/macroop.isa b/src/arch/x86/isa/macroop.isa index ba21c41a7..a7477c5a6 100644 --- a/src/arch/x86/isa/macroop.isa +++ b/src/arch/x86/isa/macroop.isa @@ -71,7 +71,7 @@ def template MacroExecPanic {{ output header {{ - // Base class for macroops + // Base class for combinationally generated macroops class MacroOp : public StaticInst { protected: @@ -113,14 +113,17 @@ output header {{ // Basic instruction class declaration template. def template MacroDeclare {{ - /** - * Static instruction class for "%(mnemonic)s". - */ - class %(class_name)s : public %(base_class)s + namespace X86Microop { - public: - // Constructor. - %(class_name)s(ExtMachInst machInst); + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + public: + // Constructor. + %(class_name)s(ExtMachInst machInst); + }; }; }}; @@ -142,24 +145,82 @@ def template MacroConstructor {{ // let {{ - def genMacroOp(name, Name, opSeq): - numMicroOps = len(opSeq) - allocMicroOps = '' - micropc = 0 - for op in opSeq: - allocMicroOps += \ - "microOps[%d] = %s;\n" % \ - (micropc, op.getAllocator('"' + name + '"', True, False, - #op.delayed, - micropc == 0, - micropc == numMicroOps - 1)) - micropc += 1 - iop = InstObjParams(name, Name, 'MacroOp', - {'code' : '', 'num_micro_ops' : numMicroOps, - 'alloc_micro_ops' : allocMicroOps}) - header_output = MacroDeclare.subst(iop) - decoder_output = MacroConstructor.subst(iop) - decode_block = BasicDecode.subst(iop) - exec_output = '' - return (header_output, decoder_output, decode_block, exec_output) + from micro_asm import Combinational_Macroop, Rom_Macroop + class X86Macroop(Combinational_Macroop): + def __init__(self, name): + super(X86Macroop, self).__init__(name) + self.directives = { + } + self.declared = False + def getAllocator(self, env): + return "new X86Macroop::%s(machInst)" % self.name + def getDeclaration(self): + #FIXME This first parameter should be the mnemonic. I need to + #write some code which pulls that out + iop = InstObjParams(self.name, self.name, "Macroop", {"code" : ""}) + return MacroDeclare.subst(iop); + def getDefinition(self): + #FIXME This first parameter should be the mnemonic. I need to + #write some code which pulls that out + numMicroops = len(self.microops) + allocMicroops = '' + micropc = 0 + for op in self.microops: + allocMicroops += \ + "microOps[%d] = %s;\n" % \ + (micropc, op.getAllocator(True, False, + micropc == 0, + micropc == numMicroops - 1)) + micropc += 1 + iop = InstObjParams(self.name, self.name, "Macroop", + {"code" : "", "num_micro_ops" : numMicroops, + "alloc_micro_ops" : allocMicroops}) + return MacroConstructor.subst(iop); +}}; + +output header {{ + struct EmulEnv + { + X86ISA::RegIndex reg; + X86ISA::RegIndex regm; + uint64_t immediate; + uint64_t displacement; + int addressSize; + int dataSize; + + EmulEnv(X86ISA::RegIndex _reg, X86ISA::RegIndex _regm, + uint64_t _immediate, uint64_t _displacement, + int _addressSize, int _dataSize) : + reg(_reg), regm(_regm), + immediate(_immediate), displacement(_displacement), + addressSize(_addressSize), dataSize(_dataSize) + {;} + }; +}}; + +let {{ + class EmulEnv(object): + def __init__(self): + self.reg = "Not specified" + self.regm = "Not specified" + self.immediate = "IMMEDIATE" + self.displacement = "DISPLACEMENT" + self.addressSize = "ADDRSIZE" + self.dataSize = "OPSIZE" + def getAllocator(self): + return "EmulEmv(%(reg)s, %(regm)s, %(immediate)s, %(displacement)s, %(addressSize)s, %(dataSize)s)" % \ + self.__dict__() +}}; + +let {{ + def genMacroop(Name, env): + if not macroopDict.has_key(Name): + raise Exception, "Unrecognized instruction: %s" % Name + macroop = macroopDict[Name] + if not macroop.declared: + global header_output + global decoder_output + header_output = macroop.getDeclaration() + decoder_output = macroop.getDefinition() + return "return %s;\n" % macroop.getAllocator(env) }}; diff --git a/src/arch/x86/isa/main.isa b/src/arch/x86/isa/main.isa index a9f01d3e0..509f4e222 100644 --- a/src/arch/x86/isa/main.isa +++ b/src/arch/x86/isa/main.isa @@ -72,34 +72,9 @@ namespace X86ISA; -//////////////////////////////////////////////////////////////////// -// -// General infrastructure code. These files provide infrastructure -// which was developed to support x86 but isn't specific to it. -// - -//Include code to build macroops. -##include "macroop.isa" - -//////////////////////////////////////////////////////////////////// -// -// X86 only infrastructure code. -// - //Include the base class for x86 instructions, and some support code. ##include "base.isa" -//Include code to specialize an instruction template to operate on -//a particular set of operands. This is specific to x86 and the x86 -//microcode ISA. -##include "specialize.isa" - -//////////////////////////////////////////////////////////////////// -// -// Code which directly specifies isa components like instructions -// microops, and the decoder. -// - //Include the definitions for the instruction formats ##include "formats/formats.isa" @@ -112,8 +87,17 @@ namespace X86ISA; //internal instruction set. ##include "microops/microops.isa" -//Include the instruction definitions which are microop assembler programs. -##include "insts/insts.isa" +//Include code to build macroops. +##include "macroop.isa" + +//Include the simple microcode assembler. This will hopefully stay +//unspecialized for x86 and can later be made available to other ISAs. +##include "microasm.isa" + +//Include code to specialize an instruction template to operate on +//a particular set of operands. This is specific to x86 and the x86 +//microcode ISA. +##include "specialize.isa" //Include the bitfield definitions ##include "bitfields.isa" diff --git a/src/arch/x86/isa/microasm.isa b/src/arch/x86/isa/microasm.isa index 50a0b10e7..e8033d31c 100644 --- a/src/arch/x86/isa/microasm.isa +++ b/src/arch/x86/isa/microasm.isa @@ -55,6 +55,20 @@ // // Authors: Gabe Black +//##include "microops/microops.isa" +//##include "macroop.isa" + +let {{ + import sys + sys.path[0:0] = ["src/arch/x86/isa/"] + from insts import microcode + print microcode + from micro_asm import MicroAssembler, Rom_Macroop, Rom + mainRom = Rom('main ROM') + assembler = MicroAssembler(X86Macroop, microopClasses, mainRom, Rom_Macroop) + macroopDict = assembler.assemble(microcode) +}}; + //////////////////////////////////////////////////////////////////// // // Microcode assembler specialization for x86 @@ -88,6 +102,5 @@ let {{ return text def getAllocator(self, mnemonic, *microFlags): - args = '' - return 'new %s(machInst, %s%s%s)' % (self.className, mnemonic, self.microFlagsText(microFlags), args) + return 'new %s(machInst, %s)' % (self.className, mnemonic, self.microFlagsText(microFlags)) }}; diff --git a/src/arch/x86/isa/microops/regop.isa b/src/arch/x86/isa/microops/regop.isa index 7411f6a14..52c13231c 100644 --- a/src/arch/x86/isa/microops/regop.isa +++ b/src/arch/x86/isa/microops/regop.isa @@ -221,7 +221,7 @@ def template MicroRegOpImmConstructor {{ }}; let {{ - class RegOp(object): + class RegOp(X86Microop): def __init__(self, dest, src1, src2): self.dest = dest self.src1 = src1 @@ -243,7 +243,7 @@ let {{ "dataSize" : self.dataSize, "ext" : self.ext} - class RegOpImm(object): + class RegOpImm(X86Microop): def __init__(self, dest, src1, imm): self.dest = dest self.src1 = src1 @@ -274,10 +274,11 @@ let {{ decoder_output = "" exec_output = "" - def defineMicroIntOp(mnemonic, code): + def defineMicroRegOp(mnemonic, code): global header_output global decoder_output global exec_output + global microopClasses Name = mnemonic name = mnemonic.lower() @@ -296,7 +297,8 @@ let {{ class RegOpChild(RegOp): def __init__(self, dest, src1, src2): - super(RegOpChild, self).__init__(self, dest, src1, src2) + super(RegOpChild, self).__init__(dest, src1, src2) + self.className = Name self.mnemonic = name microopClasses[name] = RegOpChild @@ -310,19 +312,20 @@ let {{ class RegOpImmChild(RegOpImm): def __init__(self, dest, src1, imm): - super(RegOpImmChild, self).__init__(self, dest, src1, imm) + super(RegOpImmChild, self).__init__(dest, src1, imm) + self.className = Name + "Imm" self.mnemonic = name + "i" microopClasses[name + "i"] = RegOpChild - defineMicroIntOp('Add', 'DestReg = merge(DestReg, SrcReg1 + op2, dataSize)') #Needs to set OF,CF,SF - defineMicroIntOp('Or', 'DestReg = merge(DestReg, SrcReg1 | op2, dataSize)') - defineMicroIntOp('Adc', 'DestReg = merge(DestReg, SrcReg1 + op2, dataSize)') #Needs to add in CF, set OF,CF,SF - defineMicroIntOp('Sbb', 'DestReg = merge(DestReg, SrcReg1 - op2, dataSize)') #Needs to subtract CF, set OF,CF,SF - defineMicroIntOp('And', 'DestReg = merge(DestReg, SrcReg1 & op2, dataSize)') - defineMicroIntOp('Sub', 'DestReg = merge(DestReg, SrcReg1 - op2, dataSize)') #Needs to set OF,CF,SF - defineMicroIntOp('Xor', 'DestReg = merge(DestReg, SrcReg1 ^ op2, dataSize)') - defineMicroIntOp('Cmp', 'DestReg = merge(DestReg, DestReg - op2, dataSize)') #Needs to set OF,CF,SF and not DestReg - defineMicroIntOp('Mov', 'DestReg = merge(SrcReg1, op2, dataSize)') + defineMicroRegOp('Add', 'DestReg = merge(DestReg, SrcReg1 + op2, dataSize)') #Needs to set OF,CF,SF + defineMicroRegOp('Or', 'DestReg = merge(DestReg, SrcReg1 | op2, dataSize)') + defineMicroRegOp('Adc', 'DestReg = merge(DestReg, SrcReg1 + op2, dataSize)') #Needs to add in CF, set OF,CF,SF + defineMicroRegOp('Sbb', 'DestReg = merge(DestReg, SrcReg1 - op2, dataSize)') #Needs to subtract CF, set OF,CF,SF + defineMicroRegOp('And', 'DestReg = merge(DestReg, SrcReg1 & op2, dataSize)') + defineMicroRegOp('Sub', 'DestReg = merge(DestReg, SrcReg1 - op2, dataSize)') #Needs to set OF,CF,SF + defineMicroRegOp('Xor', 'DestReg = merge(DestReg, SrcReg1 ^ op2, dataSize)') + defineMicroRegOp('Cmp', 'DestReg = merge(DestReg, DestReg - op2, dataSize)') #Needs to set OF,CF,SF and not DestReg + defineMicroRegOp('Mov', 'DestReg = merge(SrcReg1, op2, dataSize)') }}; diff --git a/src/arch/x86/isa/specialize.isa b/src/arch/x86/isa/specialize.isa index ff92c3551..de77f130b 100644 --- a/src/arch/x86/isa/specialize.isa +++ b/src/arch/x86/isa/specialize.isa @@ -66,24 +66,23 @@ let {{ # 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): - blocks = OutputBlocks() - blocks.decode_block += 'switch(%s) {\n' % switchVal + def doSplitDecode(Name, builder, switchVal, vals, default = None): + decode_block = 'switch(%s) {\n' % switchVal for (val, todo) in vals.items(): - built = builder(name, Name, *todo) - built.decode_block = '\tcase %s: %s\n' % (val, built.decode_block) - blocks.append(built) + new_block = builder(Name, *todo) + new_block = '\tcase %s: %s\n' % (val, new_block) + decode_block += new_block if default: - built = builder(name, Name, *default) - built.decode_block = '\tdefault: %s\n' % built.decode_block - blocks.append(built) - blocks.decode_block += '}\n' - return blocks + new_block = builder(Name, *default) + new_block = '\tdefault: %s\n' % new_block + decode_block += new_block + decode_block += '}\n' + return decode_block }}; let {{ class OpType(object): - parser = re.compile(r"(?P[A-Z][A-Z]*)(?P[a-z][a-z]*)|(r(?P[A-Za-z0-9][A-Za-z0-9]*))") + parser = re.compile(r"(?P[A-Z][A-Z]*)(?P[a-z][a-z]*)|(r(?P[A-Z0-9])(?P[a-z]*))") def __init__(self, opTypeString): match = OpType.parser.search(opTypeString) if match == None: @@ -91,74 +90,72 @@ let {{ self.reg = match.group("reg") self.tag = match.group("tag") self.size = match.group("size") + self.rsize = match.group("rsize") # 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 + # set of argument types described by "opTypes". + def specializeInst(Name, opTypes, env): 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%d)(?=[^0-9]|$)" % opNum) - # Parse the operand type strign we're working with + # Parse the operand type string we're working with 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 + #This is the index to use, so we should stick it some place. + print "INTREG_R%s" % (opType.reg + opType.size.upper()) + if opType.size: + if opType.rsize in ("l", "h", "b"): + print "byte" + elif opType.rsize == "x": + print "word" + else: + print "Didn't recognize fixed register size %s!" % opType.rsize 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) + print "(uint8_t)MODRM_REG" 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) + print "(uint8_t)MODRM_RM" regTypes = copy.copy(opTypes) - regTypes.pop(-1) + regTypes.pop(0) + regEnv = copy.copy(env) # This needs to refer to memory, but we'll fill in the details # later. It needs to take into account unaligned memory # addresses. - code = "GenFault ${new UnimpInstFault}\n" + code - memCode = opRe.sub("%0", code) + # code = "GenFault #${new UnimpInstFault}#\n" + code + print "%0" memTypes = copy.copy(opTypes) - memTypes.pop(-1) - return doSplitDecode(name, Name, specializeInst, "MODRM_MOD", - {"3" : (regCode, regTypes)}, (memCode, memTypes)) + memTypes.pop(0) + memEnv = copy.copy(env) + return doSplitDecode(Name, specializeInst, "MODRM_MOD", + {"3" : (regTypes, memEnv)}, (memTypes, memEnv)) elif opType.tag in ("I", "J"): # Immediates are already in the instruction, so don't leave in # those parameters - code = opRe.sub("${IMMEDIATE}", code) + print "IMMEDIATE" 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 = "GenFault ${new UnimpInstFault}\n" + code - code = opRe.sub("%0", code) + #code = "GenFault #${new UnimpInstFault}#\n" + code + print "%0" 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) + print "(uint8_t)MODRM_RM" else: raise Exception, "Unrecognized tag %s." % opType.tag - opTypes.pop(-1) + opTypes.pop(0) # 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 StaticInst. - blocks = OutputBlocks() - blocks.append(assembleMicro(name, Name, code)) - return blocks + return genMacroop(Name, env) }}; -- cgit v1.2.3