| // -*- mode:c++ -*- | |
| // Copyright (c) 2007 The Hewlett-Packard Development Company | |
| // All rights reserved. | |
| // | |
| // The license below extends only to copyright in the software and shall | |
| // not be construed as granting a license to any other intellectual | |
| // property including but not limited to intellectual property relating | |
| // to a hardware implementation of the functionality of the software | |
| // licensed hereunder. You may use the software subject to the license | |
| // terms below provided that you ensure that this notice is replicated | |
| // unmodified and in its entirety in all distributions of the software, | |
| // modified or unmodified, in source code or in binary form. | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are | |
| // met: redistributions of source code must retain the above copyright | |
| // notice, this list of conditions and the following disclaimer; | |
| // redistributions in binary form must reproduce the above copyright | |
| // notice, this list of conditions and the following disclaimer in the | |
| // documentation and/or other materials provided with the distribution; | |
| // neither the name of the copyright holders nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| ////////////////////////////////////////////////////////////////////////////// | |
| // | |
| // Architecture independent | |
| // | |
| // Execute method for macroops. | |
| def template MacroExecPanic {{ | |
| Fault | |
| execute(ExecContext *, Trace::InstRecord *) const override | |
| { | |
| panic("Tried to execute macroop directly!"); | |
| return NoFault; | |
| } | |
| }}; | |
| output header {{ | |
| // Base class for combinationally generated macroops | |
| class Macroop : public X86ISA::MacroopBase | |
| { | |
| public: | |
| Macroop(const char *mnem, ExtMachInst _machInst, | |
| uint32_t _numMicroops, X86ISA::EmulEnv _env) | |
| : MacroopBase(mnem, _machInst, _numMicroops, _env) | |
| {} | |
| Fault | |
| execute(ExecContext *, Trace::InstRecord *) const override | |
| { | |
| panic("Tried to execute macroop directly!"); | |
| } | |
| }; | |
| }}; | |
| ////////////////////////////////////////////////////////////////////////////// | |
| // | |
| // X86 specific | |
| // | |
| ////////////////////////////////////////////////////////////////////////////// | |
| // Basic instruction class declaration template. | |
| def template MacroDeclare {{ | |
| GEM5_DEPRECATED_NAMESPACE(X86Macroop, x86_macroop); | |
| namespace x86_macroop | |
| { | |
| /** | |
| * Static instruction class for "%(mnemonic)s". | |
| */ | |
| class %(class_name)s : public %(base_class)s | |
| { | |
| private: | |
| %(declareLabels)s | |
| public: | |
| // Constructor. | |
| %(class_name)s(ExtMachInst machInst, X86ISA::EmulEnv _env); | |
| std::string generateDisassembly( | |
| Addr pc, const loader::SymbolTable *symtab) const override; | |
| }; | |
| } | |
| }}; | |
| def template MacroDisassembly {{ | |
| std::string | |
| x86_macroop::%(class_name)s::generateDisassembly( | |
| Addr pc, const loader::SymbolTable *symtab) const | |
| { | |
| std::stringstream out; | |
| out << mnemonic << "\t"; | |
| int regSize = %(regSize)s; | |
| %(disassembly)s | |
| // Shut up gcc. | |
| regSize = regSize; | |
| return out.str(); | |
| } | |
| }}; | |
| // Basic instruction class constructor template. | |
| def template MacroConstructor {{ | |
| x86_macroop::%(class_name)s::%(class_name)s( | |
| ExtMachInst machInst, EmulEnv _env) | |
| : %(base_class)s("%(mnemonic)s", machInst, %(num_microops)s, _env) | |
| { | |
| %(adjust_env)s; | |
| %(adjust_imm)s; | |
| %(adjust_disp)s; | |
| %(init_env)s; | |
| %(constructor)s; | |
| const char *macrocodeBlock = "%(class_name)s"; | |
| //alloc_microops is the code that sets up the microops | |
| //array in the parent class. | |
| %(alloc_microops)s; | |
| } | |
| }}; | |
| let {{ | |
| from micro_asm import CombinationalMacroop, RomMacroop | |
| class X86Macroop(CombinationalMacroop): | |
| def setAdjustEnv(self, val): | |
| self.adjust_env = val | |
| def adjustImm(self, val): | |
| self.adjust_imm += val | |
| def adjustDisp(self, val): | |
| self.adjust_disp += val | |
| def serializeBefore(self): | |
| self.serialize_before = True | |
| def serializeAfter(self): | |
| self.serialize_after = True | |
| def function_call(self): | |
| self.function_call = True | |
| def function_return(self): | |
| self.function_return = True | |
| def control_direct(self): | |
| self.control_direct = True | |
| def control_indirect(self): | |
| self.control_indirect = True | |
| def __init__(self, name): | |
| super().__init__(name) | |
| self.directives = { | |
| "adjust_env" : self.setAdjustEnv, | |
| "adjust_imm" : self.adjustImm, | |
| "adjust_disp" : self.adjustDisp, | |
| "serialize_before" : self.serializeBefore, | |
| "serialize_after" : self.serializeAfter, | |
| "function_call" : self.function_call, | |
| "function_return" : self.function_return, | |
| "control_direct" : self.control_direct, | |
| "control_indirect" : self.control_indirect | |
| } | |
| self.declared = False | |
| self.adjust_env = "" | |
| self.init_env = "" | |
| self.adjust_imm = ''' | |
| uint64_t adjustedImm = IMMEDIATE; | |
| //This is to pacify gcc in case the immediate isn't used. | |
| adjustedImm = adjustedImm; | |
| ''' | |
| self.adjust_disp = ''' | |
| uint64_t adjustedDisp = DISPLACEMENT; | |
| //This is to pacify gcc in case the displacement isn't used. | |
| adjustedDisp = adjustedDisp; | |
| ''' | |
| self.serialize_before = False | |
| self.serialize_after = False | |
| self.function_call = False | |
| self.function_return = False | |
| self.control_direct = False | |
| self.control_indirect = False | |
| def getAllocator(self, env): | |
| return "new x86_macroop::%s(machInst, %s)" % \ | |
| (self.name, env.getAllocator()) | |
| def getMnemonic(self): | |
| mnemonic = self.name.lower() | |
| mnemonic = re.match(r'[^_]*', mnemonic).group(0) | |
| return mnemonic | |
| def getDeclaration(self): | |
| #FIXME This first parameter should be the mnemonic. I need to | |
| #write some code which pulls that out | |
| declareLabels = "" | |
| for (label, microop) in self.labels.items(): | |
| declareLabels += "const static uint64_t label_%s = %d;\n" \ | |
| % (label, microop.micropc) | |
| iop = InstObjParams(self.getMnemonic(), self.name, "Macroop", | |
| {"code" : "", | |
| "declareLabels" : declareLabels | |
| }) | |
| return MacroDeclare.subst(iop); | |
| def getDefinition(self, env): | |
| #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: | |
| flags = ["IsMicroop"] | |
| if micropc == 0: | |
| flags.append("IsFirstMicroop") | |
| if self.serialize_before: | |
| flags.append("IsSerializing") | |
| flags.append("IsSerializeBefore") | |
| if micropc == numMicroops - 1: | |
| flags.append("IsLastMicroop") | |
| if self.serialize_after: | |
| flags.append("IsSerializing") | |
| flags.append("IsSerializeAfter") | |
| if self.function_call: | |
| flags.append("IsCall") | |
| flags.append("IsUncondControl") | |
| if self.function_return: | |
| flags.append("IsReturn") | |
| flags.append("IsUncondControl") | |
| if self.control_direct: | |
| flags.append("IsDirectControl") | |
| if self.control_indirect: | |
| flags.append("IsIndirectControl") | |
| else: | |
| flags.append("IsDelayedCommit") | |
| allocMicroops += \ | |
| "microops[%d] = %s;\n" % \ | |
| (micropc, op.getAllocator(flags)) | |
| micropc += 1 | |
| if env.useStackSize: | |
| useStackSize = "true" | |
| else: | |
| useStackSize = "false" | |
| if env.memoryInst: | |
| memoryInst = "true" | |
| else: | |
| memoryInst = "false" | |
| regSize = '''(%s || (env.base == int_reg::Rsp && %s) ? | |
| env.stackSize : | |
| env.dataSize)''' % (useStackSize, memoryInst) | |
| iop = InstObjParams(self.getMnemonic(), self.name, "Macroop", | |
| {"code" : "", "num_microops" : numMicroops, | |
| "alloc_microops" : allocMicroops, | |
| "adjust_env" : self.adjust_env, | |
| "adjust_imm" : self.adjust_imm, | |
| "adjust_disp" : self.adjust_disp, | |
| "disassembly" : env.disassembly, | |
| "regSize" : regSize, | |
| "init_env" : self.initEnv}) | |
| return MacroConstructor.subst(iop) + \ | |
| MacroDisassembly.subst(iop); | |
| }}; | |
| let {{ | |
| class EmulEnv(object): | |
| def __init__(self): | |
| self.reg = "0" | |
| self.regUsed = False | |
| self.regm = "0" | |
| self.regmUsed = False | |
| self.seg = "segment_idx::Ds" | |
| self.size = None | |
| self.addressSize = "ADDRSIZE" | |
| self.dataSize = "OPSIZE" | |
| self.stackSize = "STACKSIZE" | |
| self.doModRM = False | |
| self.disassembly = "" | |
| self.firstArgument = True | |
| self.useStackSize = False | |
| self.memoryInst = False | |
| def addToDisassembly(self, code): | |
| if not self.firstArgument: | |
| self.disassembly += "out << \", \";\n" | |
| self.firstArgument = False | |
| self.disassembly += code | |
| def getAllocator(self): | |
| if self.size == 'b': | |
| self.dataSize = 1 | |
| elif self.size == 'd': | |
| self.dataSize = 4 | |
| #This is for "double plus" which is normally a double word unless | |
| #the REX W bit is set, in which case it's a quad word. It's used | |
| #for some SSE instructions. | |
| elif self.size == 'dp': | |
| self.dataSize = "(REX_W ? 8 : 4)" | |
| elif self.size == 'q': | |
| self.dataSize = 8 | |
| elif self.size == 'v': | |
| self.dataSize = "OPSIZE" | |
| elif self.size == 'w': | |
| self.dataSize = 2 | |
| elif self.size == 'z': | |
| self.dataSize = "((OPSIZE == 8) ? 4 : OPSIZE)" | |
| elif self.size: | |
| raise Exception("Unrecognized size type {}!".format(self.size)) | |
| return '''EmulEnv(%(reg)s, | |
| %(regm)s, | |
| %(dataSize)s, | |
| %(addressSize)s, | |
| %(stackSize)s)''' % \ | |
| self.__dict__ | |
| def addReg(self, reg): | |
| if not self.regUsed: | |
| self.reg = reg | |
| self.regUsed = True | |
| elif not self.regmUsed: | |
| self.regm = reg | |
| self.regmUsed = True | |
| else: | |
| raise Exception("EmulEnv is out of register specialization " + | |
| "spots.") | |
| def setSize(self, size): | |
| if not self.size: | |
| self.size = size | |
| else: | |
| if self.size != size: | |
| raise Exception("Conflicting register sizes " + | |
| "{} and {}!".format(self.size, size)) | |
| }}; | |
| let {{ | |
| doModRMString = "env.doModRM(machInst);\n" | |
| noModRMString = "env.setSeg(machInst);\n" | |
| def genMacroop(Name, env): | |
| blocks = OutputBlocks() | |
| if not Name in macroopDict: | |
| raise Exception("Unrecognized instruction: {}".format(Name)) | |
| macroop = macroopDict[Name] | |
| if not macroop.declared: | |
| if env.doModRM: | |
| macroop.initEnv = doModRMString | |
| else: | |
| macroop.initEnv = noModRMString | |
| blocks.header_output = macroop.getDeclaration() | |
| blocks.decoder_output = macroop.getDefinition(env) | |
| macroop.declared = True | |
| blocks.decode_block = "return %s;\n" % macroop.getAllocator(env) | |
| return blocks | |
| }}; |