From 1d545281b96a6df358201c7b0e610bfaf9e8f213 Mon Sep 17 00:00:00 2001 From: Steve Reinhardt Date: Mon, 17 May 2004 11:49:46 -0700 Subject: [PATCH] Significant changes to ISA description to completely factor out CPU model. ISA description now generates multiple output source files to (in theory) reduce compilation time. arch/alpha/isa_desc: Update for parser changes. Move most constructors out of class declarations (which are now in decoder.hh) and into decoder.cc. Move all execute() methods into exec output. arch/isa_parser.py: Significant changes to make ISA description completely independent of CPU model, and isolate model-dependent parts of parser into one little class (CpuModel). Also split up code output into multiple files (a header, a main source file, and per-cpu execute() method files). Noticeable changes to language as a result. See updated Doxygen documentation. cpu/simple_cpu/simple_cpu.hh: SimpleCPUExecContext typedef no longer needed. Add forward declaration of Process. cpu/static_inst.hh: SimpleCPUExecContext and FullCPUExecContext typedefs no longer needed. Make eaCompInst() and memAccInst() return const refs. --HG-- extra : convert_revision : 71471f267804fafd0a881bac7445677e76334daf --- arch/alpha/isa_desc | 1432 +++++++++++++++++++--------------- arch/isa_parser.py | 674 ++++++++++------ cpu/simple_cpu/simple_cpu.hh | 9 +- cpu/static_inst.hh | 14 +- 4 files changed, 1244 insertions(+), 885 deletions(-) diff --git a/arch/alpha/isa_desc b/arch/alpha/isa_desc index 9bbdac9b4..f964101df 100644 --- a/arch/alpha/isa_desc +++ b/arch/alpha/isa_desc @@ -1,39 +1,67 @@ // -*- mode:c++ -*- + +//////////////////////////////////////////////////////////////////// // // Alpha ISA description file. // - -let {{ - global rcs_id - rcs_id = "$Id$" -}}; +//////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// +// +// Output include file directives. +// + +output header {{ #include #include #include +#include "cpu/static_inst.hh" +#include "mem/mem_req.hh" // some constructors use MemReq flags +}}; + +output decoder {{ +#include "base/cprintf.hh" +#include "base/loader/symtab.hh" +#include "cpu/exec_context.hh" // for Jump::branchTarget() + #include #if defined(linux) #include #endif +}}; -#include "base/cprintf.hh" -#include "base/misc.hh" -#include "cpu/exec_context.hh" +output exec {{ +#include +#if defined(linux) +#include +#endif + +#include "cpu/base_cpu.hh" #include "cpu/exetrace.hh" -#include "cpu/full_cpu/dyn_inst.hh" -#include "cpu/simple_cpu/simple_cpu.hh" -#include "cpu/static_inst.hh" #include "sim/sim_exit.hh" #ifdef FULL_SYSTEM #include "arch/alpha/ev5.hh" #include "arch/alpha/pseudo_inst.hh" #endif +}}; + +//////////////////////////////////////////////////////////////////// +// +// Namespace statement. Everything below this line will be in the +// AlphaISAInst namespace. +// + namespace AlphaISA; +//////////////////////////////////////////////////////////////////// +// +// Bitfield definitions. +// + // Universal (format-independent) fields def bitfield OPCODE <31:26>; def bitfield RA <25:21>; @@ -92,70 +120,48 @@ def bitfield HW_IPR_IDX <15:0>; // IPR index // M5 instructions def bitfield M5FUNC <7:0>; -let {{ - global operandTypeMap - operandTypeMap = { - 'sb' : ('signed int', 8), - 'ub' : ('unsigned int', 8), - 'sw' : ('signed int', 16), - 'uw' : ('unsigned int', 16), - 'sl' : ('signed int', 32), - 'ul' : ('unsigned int', 32), - 'sq' : ('signed int', 64), - 'uq' : ('unsigned int', 64), - 'sf' : ('float', 32), - 'df' : ('float', 64) - } - - global operandTraitsMap - operandTraitsMap = { - # Int regs default to unsigned, but code should not count on this. - # For clarity, descriptions that depend on unsigned behavior should - # explicitly specify '.uq'. - 'Ra': IntRegOperandTraits('uq', 'RA', 'IsInteger', 1), - 'Rb': IntRegOperandTraits('uq', 'RB', 'IsInteger', 2), - 'Rc': IntRegOperandTraits('uq', 'RC', 'IsInteger', 3), - 'Fa': FloatRegOperandTraits('df', 'FA', 'IsFloating', 1), - 'Fb': FloatRegOperandTraits('df', 'FB', 'IsFloating', 2), - 'Fc': FloatRegOperandTraits('df', 'FC', 'IsFloating', 3), - 'Mem': MemOperandTraits('uq', None, - ('IsMemRef', 'IsLoad', 'IsStore'), 4), - 'NPC': NPCOperandTraits('uq', None, ( None, None, 'IsControl' ), 4), - 'Runiq': ControlRegOperandTraits('uq', 'Uniq', None, 1), - 'FPCR': ControlRegOperandTraits('uq', 'Fpcr', None, 1), - # The next two are hacks for non-full-system call-pal emulation - 'R0': IntRegOperandTraits('uq', '0', None, 1), - 'R16': IntRegOperandTraits('uq', '16', None, 1), - } - - defineDerivedOperandVars() +def operand_types {{ + 'sb' : ('signed int', 8), + 'ub' : ('unsigned int', 8), + 'sw' : ('signed int', 16), + 'uw' : ('unsigned int', 16), + 'sl' : ('signed int', 32), + 'ul' : ('unsigned int', 32), + 'sq' : ('signed int', 64), + 'uq' : ('unsigned int', 64), + 'sf' : ('float', 32), + 'df' : ('float', 64) }}; -declare {{ -// just temporary, while comparing with old code for debugging -// #define SS_COMPATIBLE_DISASSEMBLY +def operands {{ + # Int regs default to unsigned, but code should not count on this. + # For clarity, descriptions that depend on unsigned behavior should + # explicitly specify '.uq'. + 'Ra': IntRegOperandTraits('uq', 'RA', 'IsInteger', 1), + 'Rb': IntRegOperandTraits('uq', 'RB', 'IsInteger', 2), + 'Rc': IntRegOperandTraits('uq', 'RC', 'IsInteger', 3), + 'Fa': FloatRegOperandTraits('df', 'FA', 'IsFloating', 1), + 'Fb': FloatRegOperandTraits('df', 'FB', 'IsFloating', 2), + 'Fc': FloatRegOperandTraits('df', 'FC', 'IsFloating', 3), + 'Mem': MemOperandTraits('uq', None, + ('IsMemRef', 'IsLoad', 'IsStore'), 4), + 'NPC': NPCOperandTraits('uq', None, ( None, None, 'IsControl' ), 4), + 'Runiq': ControlRegOperandTraits('uq', 'Uniq', None, 1), + 'FPCR': ControlRegOperandTraits('uq', 'Fpcr', None, 1), + # The next two are hacks for non-full-system call-pal emulation + 'R0': IntRegOperandTraits('uq', '0', None, 1), + 'R16': IntRegOperandTraits('uq', '16', None, 1) +}}; - /// Check "FP enabled" machine status bit. Called when executing any FP - /// instruction in full-system mode. - /// @retval Full-system mode: No_Fault if FP is enabled, Fen_Fault - /// if not. Non-full-system mode: always returns No_Fault. -#ifdef FULL_SYSTEM - template - inline Fault checkFpEnableFault(XC *xc) - { - Fault fault = No_Fault; // dummy... this ipr access should not fault - if (!ICSR_FPE(xc->readIpr(AlphaISA::IPR_ICSR, fault))) { - fault = Fen_Fault; - } - return fault; - } -#else - template - inline Fault checkFpEnableFault(XC *xc) - { - return No_Fault; - } -#endif +//////////////////////////////////////////////////////////////////// +// +// Basic instruction classes/templates/formats etc. +// + +output header {{ +// uncomment the following to get SimpleScalar-compatible disassembly +// (useful for diffing output traces). +// #define SS_COMPATIBLE_DISASSEMBLY /** * Base class for all Alpha static instructions. @@ -183,47 +189,60 @@ declare {{ /// Print a register name for disassembly given the unique /// dependence tag number (FP or int). - void printReg(std::ostream &os, int reg) - { - if (reg < FP_Base_DepTag) { - ccprintf(os, "r%d", reg); - } - else { - ccprintf(os, "f%d", reg - FP_Base_DepTag); - } - } + void printReg(std::ostream &os, int reg); - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - std::stringstream ss; - - ccprintf(ss, "%-10s ", mnemonic); - - // just print the first two source regs... if there's - // a third one, it's a read-modify-write dest (Rc), - // e.g. for CMOVxx - if (_numSrcRegs > 0) { - printReg(ss, _srcRegIdx[0]); - } - if (_numSrcRegs > 1) { - ss << ","; - printReg(ss, _srcRegIdx[1]); - } - - // just print the first dest... if there's a second one, - // it's generally implicit - if (_numDestRegs > 0) { - if (_numSrcRegs > 0) - ss << ","; - printReg(ss, _destRegIdx[0]); - } - - return ss.str(); - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; }}; +output decoder {{ + void + AlphaStaticInst::printReg(std::ostream &os, int reg) + { + if (reg < FP_Base_DepTag) { + ccprintf(os, "r%d", reg); + } + else { + ccprintf(os, "f%d", reg - FP_Base_DepTag); + } + } + std::string + AlphaStaticInst::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + if (_numSrcRegs > 1) { + ss << ","; + printReg(ss, _srcRegIdx[1]); + } + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + if (_numSrcRegs > 0) + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } +}}; + +// Declarations for execute() methods. +def template BasicExecDeclare {{ + Fault execute(%(CPU_exec_context)s *, Trace::InstRecord *); +}}; + +// Basic instruction class declaration template. def template BasicDeclare {{ /** * Static instruction class for "%(mnemonic)s". @@ -232,18 +251,24 @@ def template BasicDeclare {{ { public: /// Constructor. - %(class_name)s(MachInst machInst) - : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) - { - %(constructor)s; - } + %(class_name)s(MachInst machInst); - %(exec_func_declarations)s + %(BasicExecDeclare)s }; }}; +// Basic instruction class constructor template. +def template BasicConstructor {{ + inline %(class_name)s::%(class_name)s(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + %(constructor)s; + } +}}; + +// Basic instruction class execute method template. def template BasicExecute {{ - Fault %(class_name)s::execute(%(cpu_model)s *xc, + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) { Fault fault = No_Fault; @@ -261,10 +286,12 @@ def template BasicExecute {{ } }}; +// Basic decode template. def template BasicDecode {{ return new %(class_name)s(machInst); }}; +// Basic decode template, passing mnemonic in as string arg to constructor. def template BasicDecodeWithMnemonic {{ return new %(class_name)s("%(mnemonic)s", machInst); }}; @@ -272,14 +299,20 @@ def template BasicDecodeWithMnemonic {{ // The most basic instruction format... used only for a few misc. insts def format BasicOperate(code, *flags) {{ iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code), flags) - return iop.subst('BasicDeclare', 'BasicDecode', 'BasicExecute') + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; //////////////////////////////////////////////////////////////////// +// +// Nop +// -declare {{ +output header {{ /** * Static instruction class for no-ops. This is a leaf class. */ @@ -299,21 +332,21 @@ declare {{ ~Nop() { } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { -#ifdef SS_COMPATIBLE_DISASSEMBLY - return originalDisassembly; -#else - return csprintf("%-10s (%s)", "nop", originalDisassembly); -#endif - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); - Fault execute(SimpleCPUExecContext *, Trace::InstRecord *) - { return No_Fault; } - - Fault execute(FullCPUExecContext *, Trace::InstRecord *) - { return No_Fault; } + %(BasicExecDeclare)s }; +}}; + +output decoder {{ + std::string Nop::generateDisassembly(Addr pc, const SymbolTable *symtab) + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return originalDisassembly; +#else + return csprintf("%-10s (%s)", "nop", originalDisassembly); +#endif + } /// Helper function for decoding nops. Substitute Nop object /// for original inst passed in as arg (and delete latter). @@ -327,18 +360,21 @@ declare {{ } }}; -def format Nop() {{ - return ('', 'return new Nop("%s", machInst);\n' % name, 'return No_Fault;') +output exec {{ + Fault + Nop::execute(%(CPU_exec_context)s *, Trace::InstRecord *) + { + return No_Fault; + } }}; - // integer & FP operate instructions use Rc as dest, so check for // Rc == 31 to detect nops def template OperateNopCheckDecode {{ { AlphaStaticInst *i = new %(class_name)s(machInst); if (RC == 31) { - i = makeNop(i); + i = makeNop(i); } return i; } @@ -348,7 +384,10 @@ def template OperateNopCheckDecode {{ def format BasicOperateWithNopCheck(code, *opt_args) {{ iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code), opt_args) - return iop.subst('BasicDeclare', 'OperateNopCheckDecode', 'BasicExecute') + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = OperateNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; @@ -357,7 +396,7 @@ def format BasicOperateWithNopCheck(code, *opt_args) {{ // Integer operate instructions // -declare {{ +output header {{ /** * Base class for integer immediate instructions. */ @@ -373,39 +412,45 @@ declare {{ { } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - std::stringstream ss; - - ccprintf(ss, "%-10s ", mnemonic); - - // just print the first source reg... if there's - // a second one, it's a read-modify-write dest (Rc), - // e.g. for CMOVxx - if (_numSrcRegs > 0) { - printReg(ss, _srcRegIdx[0]); - ss << ","; - } - - ss << (int)imm; - - if (_numDestRegs > 0) { - ss << ","; - printReg(ss, _destRegIdx[0]); - } - - return ss.str(); - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; }}; +output decoder {{ + std::string + IntegerImm::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // just print the first source reg... if there's + // a second one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + ss << ","; + } + + ss << (int)imm; + + if (_numDestRegs > 0) { + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } +}}; + + def template RegOrImmDecode {{ { AlphaStaticInst *i = - (IMM) ? (AlphaStaticInst *)new %(class_name)sImm(machInst) - : (AlphaStaticInst *)new %(class_name)s(machInst); + (IMM) ? (AlphaStaticInst *)new %(class_name)sImm(machInst) + : (AlphaStaticInst *)new %(class_name)s(machInst); if (RC == 31) { - i = makeNop(i); + i = makeNop(i); } return i; } @@ -432,24 +477,23 @@ def format IntegerOperate(code, *opt_flags) {{ # generate declaration for register version cblk = CodeBlock(code) iop = InstObjParams(name, Name, 'AlphaStaticInst', cblk, opt_flags) - (decls, exec_code) = iop.subst('BasicDeclare', 'BasicExecute') + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = BasicExecute.subst(iop) if uses_imm: # append declaration for imm version imm_cblk = CodeBlock(imm_code) imm_iop = InstObjParams(name, Name + 'Imm', 'IntegerImm', imm_cblk, opt_flags) - (imm_decls, imm_exec_code) = \ - imm_iop.subst('BasicDeclare', 'BasicExecute') - decls += imm_decls - exec_code += imm_exec_code + header_output += BasicDeclare.subst(imm_iop) + decoder_output += BasicConstructor.subst(imm_iop) + exec_output += BasicExecute.subst(imm_iop) # decode checks IMM bit to pick correct version - decode = iop.subst('RegOrImmDecode') + decode_block = RegOrImmDecode.subst(iop) else: # no imm version: just check for nop - decode = iop.subst('OperateNopCheckDecode') - - return (decls, decode, exec_code) + decode_block = OperateNopCheckDecode.subst(iop) }}; @@ -462,7 +506,29 @@ def format IntegerOperate(code, *opt_flags) {{ // BasicOperateWithNopCheck. // -declare {{ +output exec {{ + /// Check "FP enabled" machine status bit. Called when executing any FP + /// instruction in full-system mode. + /// @retval Full-system mode: No_Fault if FP is enabled, Fen_Fault + /// if not. Non-full-system mode: always returns No_Fault. +#ifdef FULL_SYSTEM + inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc) + { + Fault fault = No_Fault; // dummy... this ipr access should not fault + if (!ICSR_FPE(xc->readIpr(AlphaISA::IPR_ICSR, fault))) { + fault = Fen_Fault; + } + return fault; + } +#else + inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc) + { + return No_Fault; + } +#endif +}}; + +output header {{ /** * Base class for general floating-point instructions. Includes * support for various Alpha rounding and trapping modes. Only FP @@ -524,78 +590,14 @@ declare {{ } #if defined(linux) - int - getC99RoundingMode(uint64_t fpcr_val) - { - if (roundingMode == Dynamic) { - return alphaToC99RoundingMode[bits(fpcr_val, 59, 58)]; - } - else { - return alphaToC99RoundingMode[roundingMode]; - } - } + int getC99RoundingMode(uint64_t fpcr_val); #endif // This differs from the AlphaStaticInst version only in // printing suffixes for non-default rounding & trapping modes. - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - std::string mnem_str(mnemonic); - -#ifndef SS_COMPATIBLE_DISASSEMBLY - std::string suffix(""); - suffix += ((_destRegIdx[0] >= FP_Base_DepTag) - ? fpTrappingModeSuffix[trappingMode] - : intTrappingModeSuffix[trappingMode]); - suffix += roundingModeSuffix[roundingMode]; - - if (suffix != "") { - mnem_str = csprintf("%s/%s", mnemonic, suffix); - } -#endif - - std::stringstream ss; - ccprintf(ss, "%-10s ", mnem_str.c_str()); - - // just print the first two source regs... if there's - // a third one, it's a read-modify-write dest (Rc), - // e.g. for CMOVxx - if (_numSrcRegs > 0) { - printReg(ss, _srcRegIdx[0]); - } - if (_numSrcRegs > 1) { - ss << ","; - printReg(ss, _srcRegIdx[1]); - } - - // just print the first dest... if there's a second one, - // it's generally implicit - if (_numDestRegs > 0) { - if (_numSrcRegs > 0) - ss << ","; - printReg(ss, _destRegIdx[0]); - } - - return ss.str(); - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; -#if defined(linux) - const int AlphaFP::alphaToC99RoundingMode[] = { - FE_TOWARDZERO, // Chopped - FE_DOWNWARD, // Minus_Infinity - FE_TONEAREST, // Normal - FE_UPWARD // Dynamic in inst, Plus_Infinity in FPCR - }; -#endif - - const char *AlphaFP::roundingModeSuffix[] = { "c", "m", "", "d" }; - // mark invalid trapping modes, but don't fail on them, because - // you could decode anything on a misspeculated path - const char *AlphaFP::fpTrappingModeSuffix[] = - { "", "u", "INVTM2", "INVTM3", "INVTM4", "su", "INVTM6", "sui" }; - const char *AlphaFP::intTrappingModeSuffix[] = - { "", "v", "INVTM2", "INVTM3", "INVTM4", "sv", "INVTM6", "svi" }; }}; @@ -615,17 +617,93 @@ def template FloatingPointDecode {{ } }}; +output decoder {{ +#if defined(linux) + int + AlphaFP::getC99RoundingMode(uint64_t fpcr_val) + { + if (roundingMode == Dynamic) { + return alphaToC99RoundingMode[bits(fpcr_val, 59, 58)]; + } + else { + return alphaToC99RoundingMode[roundingMode]; + } + } +#endif + + std::string + AlphaFP::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::string mnem_str(mnemonic); + +#ifndef SS_COMPATIBLE_DISASSEMBLY + std::string suffix(""); + suffix += ((_destRegIdx[0] >= FP_Base_DepTag) + ? fpTrappingModeSuffix[trappingMode] + : intTrappingModeSuffix[trappingMode]); + suffix += roundingModeSuffix[roundingMode]; + + if (suffix != "") { + mnem_str = csprintf("%s/%s", mnemonic, suffix); + } +#endif + + std::stringstream ss; + ccprintf(ss, "%-10s ", mnem_str.c_str()); + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + if (_numSrcRegs > 1) { + ss << ","; + printReg(ss, _srcRegIdx[1]); + } + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + if (_numSrcRegs > 0) + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } + +#if defined(linux) + const int AlphaFP::alphaToC99RoundingMode[] = { + FE_TOWARDZERO, // Chopped + FE_DOWNWARD, // Minus_Infinity + FE_TONEAREST, // Normal + FE_UPWARD // Dynamic in inst, Plus_Infinity in FPCR + }; +#endif + + const char *AlphaFP::roundingModeSuffix[] = { "c", "m", "", "d" }; + // mark invalid trapping modes, but don't fail on them, because + // you could decode anything on a misspeculated path + const char *AlphaFP::fpTrappingModeSuffix[] = + { "", "u", "INVTM2", "INVTM3", "INVTM4", "su", "INVTM6", "sui" }; + const char *AlphaFP::intTrappingModeSuffix[] = + { "", "v", "INVTM2", "INVTM3", "INVTM4", "sv", "INVTM6", "svi" }; +}}; + // General format for floating-point operate instructions: // - Checks trapping and rounding mode flags. Trapping modes // currently unimplemented (will fail). // - Generates NOP if FC == 31. def format FloatingPointOperate(code, *opt_args) {{ iop = InstObjParams(name, Name, 'AlphaFP', CodeBlock(code), opt_args) - decode = iop.subst('FloatingPointDecode') + decode_block = FloatingPointDecode.subst(iop) fast_iop = InstObjParams(name, Name + 'Fast', 'AlphaFP', - CodeBlock(code), opt_args) - (fast_declare, fast_exec) = fast_iop.subst('BasicDeclare', 'BasicExecute') + CodeBlock(code), opt_args) + header_output = BasicDeclare.subst(fast_iop) + decoder_output = BasicConstructor.subst(fast_iop) + exec_output = BasicExecute.subst(fast_iop) gen_code_prefix = r''' #if defined(linux) @@ -640,9 +718,9 @@ def format FloatingPointOperate(code, *opt_args) {{ gen_iop = InstObjParams(name, Name + 'General', 'AlphaFP', CodeBlock(gen_code_prefix + code + gen_code_suffix), opt_args) - (gen_declare, gen_exec) = gen_iop.subst('BasicDeclare', 'BasicExecute') - - return (fast_declare + gen_declare, decode, fast_exec + gen_exec) + header_output += BasicDeclare.subst(gen_iop) + decoder_output += BasicConstructor.subst(gen_iop) + exec_output += BasicExecute.subst(gen_iop) }}; @@ -651,7 +729,7 @@ def format FloatingPointOperate(code, *opt_args) {{ // Memory-format instructions: LoadAddress, Load, Store // -declare {{ +output header {{ /** * Base class for general Alpha memory-format instructions. */ @@ -659,49 +737,71 @@ declare {{ { protected: - /// Displacement for EA calculation (signed). - int32_t disp; /// Memory request flags. See mem_req_base.hh. unsigned memAccessFlags; + /// Pointer to EAComp object. + const StaticInstPtr eaCompPtr; + /// Pointer to MemAcc object. + const StaticInstPtr memAccPtr; /// Constructor - Memory(const char *mnem, MachInst _machInst, OpClass __opClass) + Memory(const char *mnem, MachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr) : AlphaStaticInst(mnem, _machInst, __opClass), - disp(MEMDISP), memAccessFlags(0) + memAccessFlags(0), eaCompPtr(_eaCompPtr), memAccPtr(_memAccPtr) { } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); + + public: + + const StaticInstPtr &eaCompInst() const { return eaCompPtr; } + const StaticInstPtr &memAccInst() const { return memAccPtr; } + }; + + /** + * Base class for memory-format instructions using a 32-bit + * displacement (i.e. most of them). + */ + class MemoryDisp32 : public Memory + { + protected: + /// Displacement for EA calculation (signed). + int32_t disp; + + /// Constructor. + MemoryDisp32(const char *mnem, MachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr) + : Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr), + disp(MEMDISP) { - return csprintf("%-10s %c%d,%d(r%d)", mnemonic, - flags[IsFloating] ? 'f' : 'r', RA, MEMDISP, RB); } }; + /** * Base class for a few miscellaneous memory-format insts * that don't interpret the disp field: wh64, fetch, fetch_m, ecb. * None of these instructions has a destination register either. */ - class MemoryNoDisp : public AlphaStaticInst + class MemoryNoDisp : public Memory { protected: - /// Memory request flags. See mem_req_base.hh. - unsigned memAccessFlags; - /// Constructor - MemoryNoDisp(const char *mnem, MachInst _machInst, OpClass __opClass) - : AlphaStaticInst(mnem, _machInst, __opClass), - memAccessFlags(0) + MemoryNoDisp(const char *mnem, MachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr, + StaticInstPtr _memAccPtr) + : Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr) { } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - return csprintf("%-10s (r%d)", mnemonic, RB); - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; + /** * Base class for "fake" effective-address computation * instructions returnded by eaCompInst(). @@ -715,11 +815,7 @@ declare {{ { } - Fault execute(SimpleCPUExecContext *, Trace::InstRecord *) - { panic("attempt to execute eacomp"); } - - Fault execute(FullCPUExecContext *, Trace::InstRecord *) - { panic("attempt to execute eacomp"); } + %(BasicExecDeclare)s }; /** @@ -735,19 +831,48 @@ declare {{ { } - Fault execute(SimpleCPUExecContext *, Trace::InstRecord *) - { panic("attempt to execute memacc"); } - - Fault execute(FullCPUExecContext *, Trace::InstRecord *) - { panic("attempt to execute memacc"); } + %(BasicExecDeclare)s }; }}; +output decoder {{ + std::string + Memory::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s %c%d,%d(r%d)", mnemonic, + flags[IsFloating] ? 'f' : 'r', RA, MEMDISP, RB); + } + + std::string + MemoryNoDisp::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s (r%d)", mnemonic, RB); + } +}}; + +output exec {{ + Fault + EACompBase::execute(%(CPU_exec_context)s *, Trace::InstRecord *) + { + panic("attempt to execute eacomp"); + } + + Fault + MemAccBase::execute(%(CPU_exec_context)s *, Trace::InstRecord *) + { + panic("attempt to execute memacc"); + } +}}; + + def format LoadAddress(code) {{ - iop = InstObjParams(name, Name, 'Memory', CodeBlock(code)) - return iop.subst('BasicDeclare', 'BasicDecode', 'BasicExecute') + iop = InstObjParams(name, Name, 'MemoryDisp32', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; @@ -766,11 +891,7 @@ def template LoadStoreDeclare {{ { public: /// Constructor - EAComp(MachInst machInst) - : EACompBase(machInst) - { - %(ea_constructor)s; - } + EAComp(MachInst machInst); }; /** @@ -780,37 +901,41 @@ def template LoadStoreDeclare {{ { public: /// Constructor - MemAcc(MachInst machInst) - : MemAccBase(machInst, %(op_class)s) - { - %(memacc_constructor)s; - } + MemAcc(MachInst machInst); }; - /// Pointer to EAComp object. - StaticInstPtr eaCompPtr; - /// Pointer to MemAcc object. - StaticInstPtr memAccPtr; - public: - StaticInstPtr eaCompInst() { return eaCompPtr; } - StaticInstPtr memAccInst() { return memAccPtr; } - /// Constructor. - %(class_name)s(MachInst machInst) - : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s), - eaCompPtr(new EAComp(machInst)), memAccPtr(new MemAcc(machInst)) - { - %(constructor)s; - } + %(class_name)s(MachInst machInst); - %(exec_func_declarations)s + %(BasicExecDeclare)s }; }}; +def template LoadStoreConstructor {{ + inline %(class_name)s::EAComp::EAComp(MachInst machInst) + : EACompBase(machInst) + { + %(ea_constructor)s; + } + + inline %(class_name)s::MemAcc::MemAcc(MachInst machInst) + : MemAccBase(machInst, %(op_class)s) + { + %(memacc_constructor)s; + } + + inline %(class_name)s::%(class_name)s(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, + new EAComp(machInst), new MemAcc(machInst)) + { + %(constructor)s; + } +}}; + def template LoadStoreExecute {{ - Fault %(class_name)s::execute(%(cpu_model)s *xc, + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) { Addr EA; @@ -842,66 +967,9 @@ def template LoadStoreExecute {{ } }}; -def template PrefetchDeclare {{ - /** - * Static instruction class for "%(mnemonic)s". - */ - class %(class_name)s : public %(base_class)s - { - protected: - - /** - * "Fake" effective address computation class for "%(mnemonic)s". - */ - class EAComp : public EACompBase - { - public: - /// Constructor - EAComp(MachInst machInst) - : EACompBase(machInst) - { - %(ea_constructor)s; - } - }; - - /** - * "Fake" memory access instruction class for "%(mnemonic)s". - */ - class MemAcc : public MemAccBase - { - public: - /// Constructor - MemAcc(MachInst machInst) - : MemAccBase(machInst, %(op_class)s) - { - %(memacc_constructor)s; - } - }; - - /// Pointer to EAComp object. - StaticInstPtr eaCompPtr; - /// Pointer to MemAcc object. - StaticInstPtr memAccPtr; - - public: - - StaticInstPtr eaCompInst() { return eaCompPtr; } - StaticInstPtr memAccInst() { return memAccPtr; } - - /// Constructor - %(class_name)s(MachInst machInst) - : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s), - eaCompPtr(new EAComp(machInst)), memAccPtr(new MemAcc(machInst)) - { - %(constructor)s; - } - - %(exec_func_declarations)s - }; -}}; def template PrefetchExecute {{ - Fault %(class_name)s::execute(%(cpu_model)s *xc, + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) { Addr EA; @@ -947,12 +1015,10 @@ def template LoadPrefetchCheckDecode {{ let {{ -global LoadStoreBase def LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code = '', - base_class = 'Memory', flags = [], - declare_template = 'LoadStoreDeclare', - decode_template = 'BasicDecode', - exec_template = 'LoadStoreExecute'): + base_class = 'MemoryDisp32', flags = [], + decode_template = BasicDecode, + exec_template = LoadStoreExecute): # Segregate flags into instruction flags (handled by InstObjParams) # and memory access flags (handled here). @@ -983,62 +1049,68 @@ def LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code = '', if mem_flags != '': iop.constructor += '\n\tmemAccessFlags = ' + mem_flags + ';' - return iop.subst(declare_template, decode_template, exec_template) + # (header_output, decoder_output, decode_block, exec_output) + return (LoadStoreDeclare.subst(iop), LoadStoreConstructor.subst(iop), + decode_template.subst(iop), exec_template.subst(iop)) }}; def format LoadOrNop(ea_code, memacc_code, *flags) {{ - return LoadStoreBase(name, Name, ea_code, memacc_code, - flags = flags, - decode_template = 'LoadNopCheckDecode') + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags, + decode_template = LoadNopCheckDecode) }}; // Note that the flags passed in apply only to the prefetch version def format LoadOrPrefetch(ea_code, memacc_code, *pf_flags) {{ # declare the load instruction object and generate the decode block - (decls, decode, exec_code) = \ + (header_output, decoder_output, decode_block, exec_output) = \ LoadStoreBase(name, Name, ea_code, memacc_code, - decode_template = 'LoadPrefetchCheckDecode') + decode_template = LoadPrefetchCheckDecode) # Declare the prefetch instruction object. # convert flags from tuple to list to make them mutable pf_flags = list(pf_flags) + ['IsMemRef', 'IsLoad', 'IsDataPrefetch', 'RdPort'] - (pfdecls, pfdecode, pfexec) = \ + (pf_header_output, pf_decoder_output, _, pf_exec_output) = \ LoadStoreBase(name, Name + 'Prefetch', ea_code, '', - flags = pf_flags, - declare_template = 'PrefetchDeclare', - exec_template = 'PrefetchExecute') + flags = pf_flags, exec_template = PrefetchExecute) - return (decls + pfdecls, decode, exec_code + pfexec) + header_output += pf_header_output + decoder_output += pf_decoder_output + exec_output += pf_exec_output }}; def format Store(ea_code, memacc_code, *flags) {{ - return LoadStoreBase(name, Name, ea_code, memacc_code, - flags = flags) + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags) }}; def format StoreCond(ea_code, memacc_code, postacc_code, *flags) {{ - return LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code, - flags = flags) + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, postacc_code, + flags = flags) }}; // Use 'MemoryNoDisp' as base: for wh64, fetch, ecb def format MiscPrefetch(ea_code, memacc_code, *flags) {{ - return LoadStoreBase(name, Name, ea_code, memacc_code, - flags = flags, base_class = 'MemoryNoDisp') + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, flags = flags, + base_class = 'MemoryNoDisp') }}; //////////////////////////////////////////////////////////////////// +// +// Control transfer instructions +// - -declare {{ +output header {{ /** * Base class for instructions whose disassembly is not purely a @@ -1065,22 +1137,7 @@ declare {{ { } - const std::string &disassemble(Addr pc, const SymbolTable *symtab) - { - if (!cachedDisassembly || - pc != cachedPC || symtab != cachedSymtab) - { - if (cachedDisassembly) - delete cachedDisassembly; - - cachedDisassembly = - new std::string(generateDisassembly(pc, symtab)); - cachedPC = pc; - cachedSymtab = symtab; - } - - return *cachedDisassembly; - } + const std::string &disassemble(Addr pc, const SymbolTable *symtab); }; /** @@ -1100,47 +1157,9 @@ declare {{ { } - Addr branchTarget(Addr branchPC) const - { - return branchPC + 4 + disp; - } + Addr branchTarget(Addr branchPC) const; - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - std::stringstream ss; - - ccprintf(ss, "%-10s ", mnemonic); - - // There's only one register arg (RA), but it could be - // either a source (the condition for conditional - // branches) or a destination (the link reg for - // unconditional branches) - if (_numSrcRegs > 0) { - printReg(ss, _srcRegIdx[0]); - ss << ","; - } - else if (_numDestRegs > 0) { - printReg(ss, _destRegIdx[0]); - ss << ","; - } - -#ifdef SS_COMPATIBLE_DISASSEMBLY - if (_numSrcRegs == 0 && _numDestRegs == 0) { - printReg(ss, 31); - ss << ","; - } -#endif - - Addr target = pc + 4 + disp; - - std::string str; - if (symtab && symtab->findSymbol(target, str)) - ss << str; - else - ccprintf(ss, "0x%x", target); - - return ss.str(); - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; /** @@ -1162,36 +1181,106 @@ declare {{ { } - Addr branchTarget(ExecContext *xc) const + Addr branchTarget(ExecContext *xc) const; + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); + }; +}}; + +output decoder {{ + Addr + Branch::branchTarget(Addr branchPC) const + { + return branchPC + 4 + disp; + } + + Addr + Jump::branchTarget(ExecContext *xc) const + { + Addr NPC = xc->readPC() + 4; + uint64_t Rb = xc->readIntReg(_srcRegIdx[0]); + return (Rb & ~3) | (NPC & 1); + } + + const std::string & + PCDependentDisassembly::disassemble(Addr pc, const SymbolTable *symtab) + { + if (!cachedDisassembly || + pc != cachedPC || symtab != cachedSymtab) { - Addr NPC = xc->readPC() + 4; - uint64_t Rb = xc->readIntReg(_srcRegIdx[0]); - return (Rb & ~3) | (NPC & 1); + if (cachedDisassembly) + delete cachedDisassembly; + + cachedDisassembly = + new std::string(generateDisassembly(pc, symtab)); + cachedPC = pc; + cachedSymtab = symtab; } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - std::stringstream ss; + return *cachedDisassembly; + } - ccprintf(ss, "%-10s ", mnemonic); + std::string + Branch::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // There's only one register arg (RA), but it could be + // either a source (the condition for conditional + // branches) or a destination (the link reg for + // unconditional branches) + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + ss << ","; + } + else if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + ss << ","; + } #ifdef SS_COMPATIBLE_DISASSEMBLY - if (_numDestRegs == 0) { - printReg(ss, 31); - ss << ","; - } + if (_numSrcRegs == 0 && _numDestRegs == 0) { + printReg(ss, 31); + ss << ","; + } #endif - if (_numDestRegs > 0) { - printReg(ss, _destRegIdx[0]); - ss << ","; - } + Addr target = pc + 4 + disp; - ccprintf(ss, "(r%d)", RB); + std::string str; + if (symtab && symtab->findSymbol(target, str)) + ss << str; + else + ccprintf(ss, "0x%x", target); - return ss.str(); + return ss.str(); + } + + std::string + Jump::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + +#ifdef SS_COMPATIBLE_DISASSEMBLY + if (_numDestRegs == 0) { + printReg(ss, 31); + ss << ","; } - }; +#endif + + if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + ss << ","; + } + + ccprintf(ss, "(r%d)", RB); + + return ss.str(); + } }}; def template JumpOrBranchDecode {{ @@ -1204,44 +1293,56 @@ def format CondBranch(code) {{ code = 'bool cond;\n' + code + '\nif (cond) NPC = NPC + disp;\n'; iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), ('IsDirectControl', 'IsCondControl')) - return iop.subst('BasicDeclare', 'BasicDecode', 'BasicExecute') + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; let {{ -global UncondCtrlBase def UncondCtrlBase(name, Name, base_class, npc_expr, flags): # Declare basic control transfer w/o link (i.e. link reg is R31) nolink_code = 'NPC = %s;\n' % npc_expr nolink_iop = InstObjParams(name, Name, base_class, CodeBlock(nolink_code), flags) - (decls, exec_code) = nolink_iop.subst('BasicDeclare', 'BasicExecute') + header_output = BasicDeclare.subst(nolink_iop) + decoder_output = BasicConstructor.subst(nolink_iop) + exec_output = BasicExecute.subst(nolink_iop) # Generate declaration of '*AndLink' version, append to decls link_code = 'Ra = NPC & ~3;\n' + nolink_code link_iop = InstObjParams(name, Name + 'AndLink', base_class, CodeBlock(link_code), flags) - (link_decls, link_exec_code) = \ - link_iop.subst('BasicDeclare', 'BasicExecute') - decls += link_decls - exec_code += link_exec_code + header_output += BasicDeclare.subst(link_iop) + decoder_output += BasicConstructor.subst(link_iop) + exec_output += BasicExecute.subst(link_iop) # need to use link_iop for the decode template since it is expecting # the shorter version of class_name (w/o "AndLink") - return (decls, nolink_iop.subst('JumpOrBranchDecode'), exec_code) + + return (header_output, decoder_output, + JumpOrBranchDecode.subst(nolink_iop), exec_output) }}; def format UncondBranch(*flags) {{ flags += ('IsUncondControl', 'IsDirectControl') - return UncondCtrlBase(name, Name, 'Branch', 'NPC + disp', flags) + (header_output, decoder_output, decode_block, exec_output) = \ + UncondCtrlBase(name, Name, 'Branch', 'NPC + disp', flags) }}; def format Jump(*flags) {{ flags += ('IsUncondControl', 'IsIndirectControl') - return UncondCtrlBase(name, Name, 'Jump', '(Rb & ~3) | (NPC & 1)', flags) + (header_output, decoder_output, decode_block, exec_output) = \ + UncondCtrlBase(name, Name, 'Jump', '(Rb & ~3) | (NPC & 1)', flags) }}; -declare {{ +//////////////////////////////////////////////////////////////////// +// +// PAL calls +// + +output header {{ /** * Base class for emulated call_pal calls (used only in * non-full-system mode). @@ -1257,23 +1358,31 @@ declare {{ { } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { -#ifdef SS_COMPATIBLE_DISASSEMBLY - return csprintf("%s %s", "call_pal", mnemonic); -#else - return csprintf("%-10s %s", "call_pal", mnemonic); -#endif - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; }}; +output decoder {{ + std::string + EmulatedCallPal::generateDisassembly(Addr pc, const SymbolTable *symtab) + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%s %s", "call_pal", mnemonic); +#else + return csprintf("%-10s %s", "call_pal", mnemonic); +#endif + } +}}; + def format EmulatedCallPal(code) {{ iop = InstObjParams(name, Name, 'EmulatedCallPal', CodeBlock(code)) - return iop.subst('BasicDeclare', 'BasicDecode', 'BasicExecute') + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; -declare {{ +output header {{ /** * Base class for full-system-mode call_pal instructions. * Probably could turn this into a leaf class and get rid of the @@ -1289,106 +1398,130 @@ declare {{ /// Constructor. CallPalBase(const char *mnem, MachInst _machInst, - OpClass __opClass) - : AlphaStaticInst(mnem, _machInst, __opClass), - palFunc(PALFUNC) - { - // From the 21164 HRM (paraphrased): - // Bit 7 of the function code (mask 0x80) indicates - // whether the call is privileged (bit 7 == 0) or - // unprivileged (bit 7 == 1). The privileged call table - // starts at 0x2000, the unprivielged call table starts at - // 0x3000. Bits 5-0 (mask 0x3f) are used to calculate the - // offset. - const int palPrivMask = 0x80; - const int palOffsetMask = 0x3f; + OpClass __opClass); - // Pal call is invalid unless all other bits are 0 - palValid = ((machInst & ~(palPrivMask | palOffsetMask)) == 0); - palPriv = ((machInst & palPrivMask) == 0); - int shortPalFunc = (machInst & palOffsetMask); - // Add 1 to base to set pal-mode bit - palOffset = (palPriv ? 0x2001 : 0x3001) + (shortPalFunc << 6); - } - - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - return csprintf("%-10s %#x", "call_pal", palFunc); - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; }}; +output decoder {{ + inline + CallPalBase::CallPalBase(const char *mnem, MachInst _machInst, + OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + palFunc(PALFUNC) + { + // From the 21164 HRM (paraphrased): + // Bit 7 of the function code (mask 0x80) indicates + // whether the call is privileged (bit 7 == 0) or + // unprivileged (bit 7 == 1). The privileged call table + // starts at 0x2000, the unprivielged call table starts at + // 0x3000. Bits 5-0 (mask 0x3f) are used to calculate the + // offset. + const int palPrivMask = 0x80; + const int palOffsetMask = 0x3f; + + // Pal call is invalid unless all other bits are 0 + palValid = ((machInst & ~(palPrivMask | palOffsetMask)) == 0); + palPriv = ((machInst & palPrivMask) == 0); + int shortPalFunc = (machInst & palOffsetMask); + // Add 1 to base to set pal-mode bit + palOffset = (palPriv ? 0x2001 : 0x3001) + (shortPalFunc << 6); + } + + std::string + CallPalBase::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s %#x", "call_pal", palFunc); + } +}}; def format CallPal(code) {{ iop = InstObjParams(name, Name, 'CallPalBase', CodeBlock(code)) - return iop.subst('BasicDeclare', 'BasicDecode', 'BasicExecute') + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; +//////////////////////////////////////////////////////////////////// // // hw_ld, hw_st // -declare {{ + +output header {{ /** * Base class for hw_ld and hw_st. */ - class HwLoadStore : public AlphaStaticInst + class HwLoadStore : public Memory { protected: /// Displacement for EA calculation (signed). int16_t disp; - /// Memory request flags. See mem_req_base.hh. - unsigned memAccessFlags; /// Constructor - HwLoadStore(const char *mnem, MachInst _machInst, OpClass __opClass) - : AlphaStaticInst(mnem, _machInst, __opClass), disp(HW_LDST_DISP) - { - memAccessFlags = 0; - if (HW_LDST_PHYS) memAccessFlags |= PHYSICAL; - if (HW_LDST_ALT) memAccessFlags |= ALTMODE; - if (HW_LDST_VPTE) memAccessFlags |= VPTE; - if (HW_LDST_LOCK) memAccessFlags |= LOCKED; - } + HwLoadStore(const char *mnem, MachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr, + StaticInstPtr _memAccPtr); - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { -#ifdef SS_COMPATIBLE_DISASSEMBLY - return csprintf("%-10s r%d,%d(r%d)", mnemonic, RA, disp, RB); -#else - // HW_LDST_LOCK and HW_LDST_COND are the same bit. - const char *lock_str = - (HW_LDST_LOCK) ? (flags[IsLoad] ? ",LOCK" : ",COND") : ""; - - return csprintf("%-10s r%d,%d(r%d)%s%s%s%s%s", - mnemonic, RA, disp, RB, - HW_LDST_PHYS ? ",PHYS" : "", - HW_LDST_ALT ? ",ALT" : "", - HW_LDST_QUAD ? ",QUAD" : "", - HW_LDST_VPTE ? ",VPTE" : "", - lock_str); -#endif - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; }}; +output decoder {{ + inline + HwLoadStore::HwLoadStore(const char *mnem, MachInst _machInst, + OpClass __opClass, + StaticInstPtr _eaCompPtr, + StaticInstPtr _memAccPtr) + : Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr), + disp(HW_LDST_DISP) + { + memAccessFlags = 0; + if (HW_LDST_PHYS) memAccessFlags |= PHYSICAL; + if (HW_LDST_ALT) memAccessFlags |= ALTMODE; + if (HW_LDST_VPTE) memAccessFlags |= VPTE; + if (HW_LDST_LOCK) memAccessFlags |= LOCKED; + } + + std::string + HwLoadStore::generateDisassembly(Addr pc, const SymbolTable *symtab) + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%-10s r%d,%d(r%d)", mnemonic, RA, disp, RB); +#else + // HW_LDST_LOCK and HW_LDST_COND are the same bit. + const char *lock_str = + (HW_LDST_LOCK) ? (flags[IsLoad] ? ",LOCK" : ",COND") : ""; + + return csprintf("%-10s r%d,%d(r%d)%s%s%s%s%s", + mnemonic, RA, disp, RB, + HW_LDST_PHYS ? ",PHYS" : "", + HW_LDST_ALT ? ",ALT" : "", + HW_LDST_QUAD ? ",QUAD" : "", + HW_LDST_VPTE ? ",VPTE" : "", + lock_str); +#endif + } +}}; + def format HwLoadStore(ea_code, memacc_code, class_ext, *flags) {{ - return LoadStoreBase(name, Name + class_ext, ea_code, memacc_code, - flags = flags, - base_class = 'HwLoadStore') + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name + class_ext, ea_code, memacc_code, + flags = flags, base_class = 'HwLoadStore') }}; def format HwStoreCond(ea_code, memacc_code, postacc_code, class_ext, *flags) {{ - return LoadStoreBase(name, Name + class_ext, - ea_code, memacc_code, postacc_code, - flags = flags, - base_class = 'HwLoadStore') + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name + class_ext, ea_code, memacc_code, + postacc_code, flags = flags, base_class = 'HwLoadStore') }}; -declare {{ +output header {{ /** * Base class for hw_mfpr and hw_mtpr. */ @@ -1405,28 +1538,42 @@ declare {{ { } - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - if (_numSrcRegs > 0) { - // must be mtpr - return csprintf("%-10s r%d,IPR(%#x)", - mnemonic, RA, ipr_index); - } - else { - // must be mfpr - return csprintf("%-10s IPR(%#x),r%d", - mnemonic, ipr_index, RA); - } - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; }}; +output decoder {{ + std::string + HwMoveIPR::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + if (_numSrcRegs > 0) { + // must be mtpr + return csprintf("%-10s r%d,IPR(%#x)", + mnemonic, RA, ipr_index); + } + else { + // must be mfpr + return csprintf("%-10s IPR(%#x),r%d", + mnemonic, ipr_index, RA); + } + } +}}; + def format HwMoveIPR(code) {{ iop = InstObjParams(name, Name, 'HwMoveIPR', CodeBlock(code)) - return iop.subst('BasicDeclare', 'BasicDecode', 'BasicExecute') + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) }}; -declare {{ + +//////////////////////////////////////////////////////////////////// +// +// Unimplemented instructions +// + +output header {{ /** * Static instruction class for unimplemented instructions that * cause simulator termination. Note that these are recognized @@ -1443,29 +1590,9 @@ declare {{ { } - Fault execute(SimpleCPUExecContext *xc, - Trace::InstRecord *traceData) - { - panic("attempt to execute unimplemented instruction '%s' " - "(inst 0x%08x, opcode 0x%x)", mnemonic, machInst, OPCODE); - return Unimplemented_Opcode_Fault; - } + %(BasicExecDeclare)s - Fault execute(FullCPUExecContext *xc, - Trace::InstRecord *traceData) - { - // don't panic if this is a misspeculated instruction - if (!xc->misspeculating()) - panic("attempt to execute unimplemented instruction '%s' " - "(inst 0x%08x, opcode 0x%x)", - mnemonic, machInst, OPCODE); - return Unimplemented_Opcode_Fault; - } - - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - return csprintf("%-10s (unimplemented)", mnemonic); - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; /** @@ -1490,39 +1617,56 @@ declare {{ { } - Fault execute(SimpleCPUExecContext *xc, - Trace::InstRecord *traceData) - { + %(BasicExecDeclare)s + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); + }; +}}; + +output decoder {{ + std::string + FailUnimplemented::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s (unimplemented)", mnemonic); + } + + std::string + WarnUnimplemented::generateDisassembly(Addr pc, const SymbolTable *symtab) + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%-10s", mnemonic); +#else + return csprintf("%-10s (unimplemented)", mnemonic); +#endif + } +}}; + +output exec {{ + Fault + FailUnimplemented::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) + { + if (!xc->misspeculating()) + panic("attempt to execute unimplemented instruction '%s' " + "(inst 0x%08x, opcode 0x%x)", mnemonic, machInst, OPCODE); + return Unimplemented_Opcode_Fault; + } + + Fault + WarnUnimplemented::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) + { + if (!xc->misspeculating()) if (!warned) { warn("instruction '%s' unimplemented\n", mnemonic); warned = true; } - return No_Fault; - } - - Fault execute(FullCPUExecContext *xc, - Trace::InstRecord *traceData) - { - if (!xc->misspeculating() && !warned) { - warn("instruction '%s' unimplemented\n", mnemonic); - warned = true; - } - - return No_Fault; - } - - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { -#ifdef SS_COMPATIBLE_DISASSEMBLY - return csprintf("%-10s", mnemonic); -#else - return csprintf("%-10s (unimplemented)", mnemonic); -#endif - } - }; + return No_Fault; + } }}; + def template WarnUnimplDeclare {{ /** * Static instruction class for "%(mnemonic)s". @@ -1541,15 +1685,16 @@ def template WarnUnimplDeclare {{ def format FailUnimpl() {{ iop = InstObjParams(name, 'FailUnimplemented') - return ('', iop.subst('BasicDecodeWithMnemonic'), '') + decode_block = BasicDecodeWithMnemonic.subst(iop) }}; def format WarnUnimpl() {{ iop = InstObjParams(name, Name, 'WarnUnimplemented') - return iop.subst('WarnUnimplDeclare', 'BasicDecode') + [''] + header_output = WarnUnimplDeclare.subst(iop) + decode_block = BasicDecode.subst(iop) }}; -declare {{ +output header {{ /** * Static instruction class for unknown (illegal) instructions. * These cause simulator termination if they are executed in a @@ -1564,37 +1709,47 @@ declare {{ { } - Fault execute(SimpleCPUExecContext *xc, - Trace::InstRecord *traceData) - { - panic("attempt to execute unknown instruction " - "(inst 0x%08x, opcode 0x%x)", machInst, OPCODE); - return Unimplemented_Opcode_Fault; - } + %(BasicExecDeclare)s - Fault execute(FullCPUExecContext *xc, - Trace::InstRecord *traceData) - { - // don't panic if this is a misspeculated instruction - if (!xc->misspeculating()) - panic("attempt to execute unknown instruction " - "(inst 0x%08x, opcode 0x%x)", machInst, OPCODE); - return Unimplemented_Opcode_Fault; - } - - std::string generateDisassembly(Addr pc, const SymbolTable *symtab) - { - return csprintf("%-10s (inst 0x%x, opcode 0x%x)", - "unknown", machInst, OPCODE); - } + std::string generateDisassembly(Addr pc, const SymbolTable *symtab); }; }}; -def format Unknown() {{ - return ('', 'return new Unknown(machInst);\n', '') +//////////////////////////////////////////////////////////////////// +// +// Unknown instructions +// + +output decoder {{ + std::string + Unknown::generateDisassembly(Addr pc, const SymbolTable *symtab) + { + return csprintf("%-10s (inst 0x%x, opcode 0x%x)", + "unknown", machInst, OPCODE); + } }}; -declare {{ +output exec {{ + Fault + Unknown::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) + { + if (!xc->misspeculating()) + panic("attempt to execute unknown instruction " + "(inst 0x%08x, opcode 0x%x)", machInst, OPCODE); + return Unimplemented_Opcode_Fault; + } +}}; + +def format Unknown() {{ + decode_block = 'return new Unknown(machInst);\n' +}}; + +//////////////////////////////////////////////////////////////////// +// +// Utility functions for execute methods +// + +output exec {{ /// Return opa + opb, summing carry into third arg. inline uint64_t @@ -1608,7 +1763,7 @@ declare {{ /// Multiply two 64-bit values (opa * opb), returning the 128-bit /// product in res_hi and res_lo. - void + inline void mul128(uint64_t opa, uint64_t opb, uint64_t &res_hi, uint64_t &res_lo) { // do a 64x64 --> 128 multiply using four 32x32 --> 64 multiplies @@ -1678,6 +1833,11 @@ declare {{ } }}; +//////////////////////////////////////////////////////////////////// +// +// The actual decoder specification +// + decode OPCODE default Unknown::unknown() { format LoadAddress { diff --git a/arch/isa_parser.py b/arch/isa_parser.py index 0ee9e2e2d..621720709 100755 --- a/arch/isa_parser.py +++ b/arch/isa_parser.py @@ -63,8 +63,9 @@ import yacc # using the same regexp as generic IDs, but distinguished in the # t_ID() function. The PLY documentation suggests this approach. reserved = ( - 'BITFIELD', 'DECLARE', 'DECODE', 'DEFAULT', 'DEF', 'FORMAT', - 'LET', 'NAMESPACE', 'SIGNED', 'TEMPLATE' + 'BITFIELD', 'DECODE', 'DECODER', 'DEFAULT', 'DEF', 'EXEC', 'FORMAT', + 'HEADER', 'LET', 'NAMESPACE', 'OPERAND_TYPES', 'OPERANDS', + 'OUTPUT', 'SIGNED', 'TEMPLATE' ) # List of tokens. The lex module requires this. @@ -195,14 +196,6 @@ lex.lex() # (by assigning to t[0]). ##################################################################### -# Not sure why, but we get a handful of shift/reduce conflicts on DECLARE. -# By default these get resolved as shifts, which is correct, but -# warnings are printed. Explicitly marking DECLARE as right-associative -# suppresses the warnings. -precedence = ( - ('right', 'DECLARE'), - ) - # The LHS of the first grammar rule is used as the start symbol # (in this case, 'specification'). Note that this rule enforces # that there will be exactly one namespace declaration, with 0 or more @@ -210,163 +203,123 @@ precedence = ( # the namespace decl will be outside the namespace; those after # will be inside. The decoder function is always inside the namespace. def p_specification(t): - 'specification : opt_defs_and_declares name_decl opt_defs_and_declares decode_block' - global_decls1 = t[1] + 'specification : opt_defs_and_outputs name_decl opt_defs_and_outputs decode_block' + global_code = t[1] isa_name = t[2] namespace = isa_name + "Inst" - global_decls2 = t[3] - (inst_decls, decode_code, exec_code) = t[4] - decode_code = indent(decode_code) - # grab the last three path components of isa_desc_filename - filename = '/'.join(isa_desc_filename.split('/')[-3:]) - # if the isa_desc file defines a 'rcs_id' string, - # echo that into the output too - try: - local_rcs_id = rcs_id - # strip $s out of ID so it doesn't get re-substituted - local_rcs_id = re.sub(r'\$', '', local_rcs_id) - except NameError: - local_rcs_id = 'Id: no RCS id found' - output = open(decoder_filename, 'w') - # split string to keep rcs from substituting this file's RCS id in - print >> output, '/* $Id' + '''$ */ - -/* - * Copyright (c) 2003 - * The Regents of The University of Michigan - * All Rights Reserved - * - * This code is part of the M5 simulator, developed by Nathan Binkert, - * Erik Hallnor, Steve Raasch, and Steve Reinhardt, with contributions - * from Ron Dreslinski, Dave Greene, and Lisa Hsu. - * - * Permission is granted to use, copy, create derivative works and - * redistribute this software and such derivative works for any - * purpose, so long as the copyright notice above, this grant of - * permission, and the disclaimer below appear in all copies made; and - * so long as the name of The University of Michigan is not used in - * any advertising or publicity pertaining to the use or distribution - * of this software without specific, written prior authorization. - * - * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE - * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND - * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE - * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT, - * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM - * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN - * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGES. - */ - -/* - * DO NOT EDIT THIS FILE!!! - * - * It was automatically generated from this ISA description: - * Filename: %(filename)s - * RCS %(local_rcs_id)s - */ - -#include "base/bitfield.hh" // required for bitfield support - - -///////////////////////////////////// -// Global defs (outside namespace) // -///////////////////////////////////// - -%(global_decls1)s - -/** - * Namespace for %(isa_name)s static instruction objects. - */ -namespace %(namespace)s -{ - -///////////////////////////////////// -// Global defs (within namespace) // -///////////////////////////////////// - -%(global_decls2)s - -//////////////////////////////////// -// Declares from inst definitions // -//////////////////////////////////// - -%(inst_decls)s - -%(exec_code)s - -} // namespace %(namespace)s - -////////////////////// -// Decoder function // -////////////////////// - + # wrap the decode block as a function definition + t[4].wrap_decode_block(''' StaticInstPtr<%(isa_name)s> %(isa_name)s::decodeInst(%(isa_name)s::MachInst machInst) { using namespace %(namespace)s; -%(decode_code)s -} // decodeInst -''' % vars() - output.close() +''' % vars(), '}') + # both the latter output blocks and the decode block are in the namespace + namespace_code = t[3] + t[4] + # pass it all back to the caller of yacc.parse() + t[0] = (isa_name, namespace, global_code, namespace_code) # ISA name declaration looks like "namespace ;" def p_name_decl(t): 'name_decl : NAMESPACE ID SEMI' t[0] = t[2] -# 'opt_defs_and_declares' is a possibly empty sequence of -# defs and/or declares. -def p_opt_defs_and_declares_0(t): - 'opt_defs_and_declares : empty' - t[0] = '' +# 'opt_defs_and_outputs' is a possibly empty sequence of +# def and/or output statements. +def p_opt_defs_and_outputs_0(t): + 'opt_defs_and_outputs : empty' + t[0] = GenCode() -def p_opt_defs_and_declares_1(t): - 'opt_defs_and_declares : defs_and_declares' +def p_opt_defs_and_outputs_1(t): + 'opt_defs_and_outputs : defs_and_outputs' t[0] = t[1] -def p_defs_and_declares_0(t): - 'defs_and_declares : def_or_declare' +def p_defs_and_outputs_0(t): + 'defs_and_outputs : def_or_output' t[0] = t[1] -def p_defs_and_declares_1(t): - 'defs_and_declares : defs_and_declares def_or_declare' +def p_defs_and_outputs_1(t): + 'defs_and_outputs : defs_and_outputs def_or_output' t[0] = t[1] + t[2] -# The list of possible definition/declaration statements. -def p_def_or_declare(t): - '''def_or_declare : def_format - | def_bitfield - | def_template - | global_declare - | global_let - | cpp_directive''' +# The list of possible definition/output statements. +def p_def_or_output(t): + '''def_or_output : def_format + | def_bitfield + | def_template + | def_operand_types + | def_operands + | output_header + | output_decoder + | output_exec + | global_let''' t[0] = t[1] -# preprocessor directives are copied directly to the output. -def p_cpp_directive(t): - '''cpp_directive : CPPDIRECTIVE''' - t[0] = t[1] +# Output blocks 'output {{...}}' (C++ code blocks) are copied +# directly to the appropriate output section. -# Global declares 'declare {{...}}' (C++ code blocks) are copied -# directly to the output. -def p_global_declare(t): - 'global_declare : DECLARE CODELIT SEMI' - t[0] = substBitOps(t[2]) +# Massage output block by substituting in template definitions and bit +# operators. We handle '%'s embedded in the string that don't +# indicate template substitutions (or CPU-specific symbols, which get +# handled in GenCode) by doubling them first so that the format +# operation will reduce them back to single '%'s. +def process_output(s): + # protect any non-substitution '%'s (not followed by '(') + s = re.sub(r'%(?!\()', '%%', s) + # protects cpu-specific symbols too + s = protect_cpu_symbols(s) + return substBitOps(s % templateMap) + +def p_output_header(t): + 'output_header : OUTPUT HEADER CODELIT SEMI' + t[0] = GenCode(header_output = process_output(t[3])) + +def p_output_decoder(t): + 'output_decoder : OUTPUT DECODER CODELIT SEMI' + t[0] = GenCode(decoder_output = process_output(t[3])) + +def p_output_exec(t): + 'output_exec : OUTPUT EXEC CODELIT SEMI' + t[0] = GenCode(exec_output = process_output(t[3])) # global let blocks 'let {{...}}' (Python code blocks) are executed -# directly when seen. These are typically used to initialize global -# Python variables used in later format definitions. +# directly when seen. Note that these execute in a special variable +# context 'exportContext' to prevent the code from polluting this +# script's namespace. def p_global_let(t): 'global_let : LET CODELIT SEMI' + updateExportContext() try: - exec(fixPythonIndentation(t[2])) - except: - error_bt(t.lineno(1), 'error in global let block "%s".' % t[2]) - t[0] = '' # contributes nothing to the output C++ file + exec fixPythonIndentation(t[2]) in exportContext + except Exception, exc: + error(t.lineno(1), + 'error: %s in global let block "%s".' % (exc, t[2])) + t[0] = GenCode() # contributes nothing to the output C++ file + +# Define the mapping from operand type extensions to C++ types and bit +# widths (stored in operandTypeMap). +def p_def_operand_types(t): + 'def_operand_types : DEF OPERAND_TYPES CODELIT SEMI' + s = 'global operandTypeMap; operandTypeMap = {' + t[3] + '}' + try: + exec s + except Exception, exc: + error(t.lineno(1), + 'error: %s in def operand_types block "%s".' % (exc, t[3])) + t[0] = GenCode() # contributes nothing to the output C++ file + +# Define the mapping from operand names to operand classes and other +# traits. Stored in operandTraitsMap. +def p_def_operands(t): + 'def_operands : DEF OPERANDS CODELIT SEMI' + s = 'global operandTraitsMap; operandTraitsMap = {' + t[3] + '}' + try: + exec s + except Exception, exc: + error(t.lineno(1), + 'error: %s in def operands block "%s".' % (exc, t[3])) + defineDerivedOperandVars() + t[0] = GenCode() # contributes nothing to the output C++ file # A bitfield definition looks like: # 'def [signed] bitfield [:]' @@ -376,7 +329,8 @@ def p_def_bitfield_0(t): expr = 'bits(machInst, %2d, %2d)' % (t[6], t[8]) if (t[2] == 'signed'): expr = 'sext<%d>(%s)' % (t[6] - t[8] + 1, expr) - t[0] = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr) + hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr) + t[0] = GenCode(header_output = hash_define) # alternate form for single bit: 'def [signed] bitfield []' def p_def_bitfield_1(t): @@ -384,7 +338,8 @@ def p_def_bitfield_1(t): expr = 'bits(machInst, %2d, %2d)' % (t[6], t[6]) if (t[2] == 'signed'): expr = 'sext<%d>(%s)' % (1, expr) - t[0] = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr) + hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr) + t[0] = GenCode(header_output = hash_define) def p_opt_signed_0(t): 'opt_signed : SIGNED' @@ -399,8 +354,8 @@ templateMap = {} def p_def_template(t): 'def_template : DEF TEMPLATE ID CODELIT SEMI' - templateMap[t[3]] = t[4] - t[0] = '' + templateMap[t[3]] = Template(t[4]) + t[0] = GenCode() # An instruction format definition looks like # "def format () {{...}};" @@ -408,12 +363,7 @@ def p_def_format(t): 'def_format : DEF FORMAT ID LPAREN param_list RPAREN CODELIT SEMI' (id, params, code) = (t[3], t[5], t[7]) defFormat(id, params, code, t.lineno(1)) - # insert a comment into the output to note that the def was processed - t[0] = ''' -// -// parser: format %s defined -// -''' % id + t[0] = GenCode() # The formal parameter list for an instruction format is a possibly # empty list of comma-separated parameters. @@ -453,19 +403,13 @@ def p_param_1(t): def p_decode_block(t): 'decode_block : DECODE ID opt_default LBRACE decode_stmt_list RBRACE' default_defaults = defaultStack.pop() - (decls, decode_code, exec_code, has_default) = t[5] + codeObj = t[5] # use the "default defaults" only if there was no explicit # default statement in decode_stmt_list - if not has_default: - (default_decls, default_decode, default_exec) = default_defaults - decls += default_decls - decode_code += default_decode - exec_code += default_exec - t[0] = (decls, ''' -switch (%s) { -%s -} -''' % (t[2], indent(decode_code)), exec_code) + if not codeObj.has_decode_default: + codeObj += default_defaults + codeObj.wrap_decode_block('switch (%s) {\n' % t[2], '}\n') + t[0] = codeObj # The opt_default statement serves only to push the "default defaults" # onto defaultStack. This value will be used by nested decode blocks, @@ -481,8 +425,9 @@ def p_opt_default_0(t): def p_opt_default_1(t): 'opt_default : DEFAULT inst' # push the new default - (decls, decode_code, exec_code) = t[2] - defaultStack.push((decls, '\ndefault:\n%sbreak;' % decode_code, exec_code)) + codeObj = t[2] + codeObj.wrap_decode_block('\ndefault:\n', 'break;\n') + defaultStack.push(codeObj) # no meaningful value returned t[0] = None @@ -492,12 +437,9 @@ def p_decode_stmt_list_0(t): def p_decode_stmt_list_1(t): 'decode_stmt_list : decode_stmt decode_stmt_list' - (decls1, decode_code1, exec_code1, has_default1) = t[1] - (decls2, decode_code2, exec_code2, has_default2) = t[2] - if (has_default1 and has_default2): + if (t[1].has_decode_default and t[2].has_decode_default): error(t.lineno(1), 'Two default cases in decode block') - t[0] = (decls1 + '\n' + decls2, decode_code1 + '\n' + decode_code2, - exec_code1 + '\n' + exec_code2, has_default1 or has_default2) + t[0] = t[1] + t[2] # # Decode statement rules @@ -510,7 +452,7 @@ def p_decode_stmt_list_1(t): # Preprocessor directives found in a decode statement list are passed -# through to the output, replicated to both the declaration and decode +# through to the output, replicated to all of the output code # streams. This works well for ifdefs, so we can ifdef out both the # declarations and the decode cases generated by an instruction # definition. Handling them as part of the grammar makes it easy to @@ -518,7 +460,7 @@ def p_decode_stmt_list_1(t): # the other statements. def p_decode_stmt_cpp(t): 'decode_stmt : CPPDIRECTIVE' - t[0] = (t[1], t[1], t[1], 0) + t[0] = GenCode(t[1], t[1], t[1], t[1]) # A format block 'format { ... }' sets the default instruction # format used to handle instruction definitions inside the block. @@ -547,29 +489,31 @@ def p_push_format_id(t): # specified constant, do a nested decode on some other field. def p_decode_stmt_decode(t): 'decode_stmt : case_label COLON decode_block' - (label, is_default) = t[1] - (decls, decode_code, exec_code) = t[3] + label = t[1] + codeObj = t[3] # just wrap the decoding code from the block as a case in the # outer switch statement. - t[0] = (decls, '\n%s:\n%s' % (label, indent(decode_code)), - exec_code, is_default) + codeObj.wrap_decode_block('\n%s:\n' % label) + codeObj.has_decode_default = (label == 'default') + t[0] = codeObj # Instruction definition (finally!). def p_decode_stmt_inst(t): 'decode_stmt : case_label COLON inst SEMI' - (label, is_default) = t[1] - (decls, decode_code, exec_code) = t[3] - t[0] = (decls, '\n%s:%sbreak;' % (label, indent(decode_code)), - exec_code, is_default) + label = t[1] + codeObj = t[3] + codeObj.wrap_decode_block('\n%s:' % label, 'break;\n') + codeObj.has_decode_default = (label == 'default') + t[0] = codeObj # The case label is either a list of one or more constants or 'default' def p_case_label_0(t): 'case_label : intlit_list' - t[0] = (': '.join(map(lambda a: 'case %#x' % a, t[1])), 0) + t[0] = ': '.join(map(lambda a: 'case %#x' % a, t[1])) def p_case_label_1(t): 'case_label : DEFAULT' - t[0] = ('default', 1) + t[0] = 'default' # # The constant list for a decode case label must be non-empty, but may have @@ -591,13 +535,13 @@ def p_inst_0(t): 'inst : ID LPAREN arg_list RPAREN' # Pass the ID and arg list to the current format class to deal with. currentFormat = formatStack.top() - (decls, decode_code, exec_code) = \ - currentFormat.defineInst(t[1], t[3], t.lineno(1)) + codeObj = currentFormat.defineInst(t[1], t[3], t.lineno(1)) args = ','.join(map(str, t[3])) args = re.sub('(?m)^', '//', args) args = re.sub('^//', '', args) - comment = '// %s::%s(%s)\n' % (currentFormat.id, t[1], args) - t[0] = (comment + decls, comment + decode_code, comment + exec_code) + comment = '\n// %s::%s(%s)\n' % (currentFormat.id, t[1], args) + codeObj.prepend_all(comment) + t[0] = codeObj # Define an instruction using an explicitly specified format: # "::()" @@ -607,10 +551,10 @@ def p_inst_1(t): format = formatMap[t[1]] except KeyError: error(t.lineno(1), 'instruction format "%s" not defined.' % t[1]) - (decls, decode_code, exec_code) = \ - format.defineInst(t[3], t[5], t.lineno(1)) - comment = '// %s::%s(%s)\n' % (t[1], t[3], t[5]) - t[0] = (comment + decls, comment + decode_code, comment + exec_code) + codeObj = format.defineInst(t[3], t[5], t.lineno(1)) + comment = '\n// %s::%s(%s)\n' % (t[1], t[3], t[5]) + codeObj.prepend_all(comment) + t[0] = codeObj def p_arg_list_0(t): 'arg_list : empty' @@ -652,6 +596,133 @@ def p_error(t): # Now build the parser. yacc.yacc() + +##################################################################### +# +# Support Classes +# +##################################################################### + +################ +# CpuModel class +# +# The CpuModel class encapsulates everything we need to know about a +# particular CPU model. + +class CpuModel: + # List of all CPU models. Accessible as CpuModel.list. + list = [] + + # Constructor. Automatically adds models to CpuModel.list. + def __init__(self, name, filename, includes, strings): + self.name = name + self.filename = filename # filename for output exec code + self.includes = includes # include files needed in exec file + # The 'strings' dict holds all the per-CPU symbols we can + # substitute into templates etc. + self.strings = strings + # Add self to list. + CpuModel.list.append(self) + +# Define CPU models. The following lines should contain the only +# CPU-model-specific information in this file. Note that the ISA +# description itself should have *no* CPU-model-specific content. +CpuModel('SimpleCPU', 'simple_cpu_exec.cc', + '#include "cpu/simple_cpu/simple_cpu.hh"', + { 'CPU_exec_context': 'SimpleCPU' }) +CpuModel('FullCPU', 'full_cpu_exec.cc', + '#include "cpu/full_cpu/dyn_inst.hh"', + { 'CPU_exec_context': 'DynInst' }) + +# Expand template with CPU-specific references into a dictionary with +# an entry for each CPU model name. The entry key is the model name +# and the corresponding value is the template with the CPU-specific +# refs substituted for that model. +def expand_cpu_symbols_to_dict(template): + # Protect '%'s that don't go with CPU-specific terms + t = re.sub(r'%(?!\(CPU_)', '%%', template) + result = {} + for cpu in CpuModel.list: + result[cpu.name] = t % cpu.strings + return result + +# *If* the template has CPU-specific references, return a single +# string containing a copy of the template for each CPU model with the +# corresponding values substituted in. If the template has no +# CPU-specific references, it is returned unmodified. +def expand_cpu_symbols_to_string(template): + if template.find('%(CPU_') != -1: + return reduce(lambda x,y: x+y, + expand_cpu_symbols_to_dict(template).values()) + else: + return template + +# Protect CPU-specific references by doubling the corresponding '%'s +# (in preparation for substituting a different set of references into +# the template). +def protect_cpu_symbols(template): + return re.sub(r'%(?=\(CPU_)', '%%', template) + +############### +# GenCode class +# +# The GenCode class encapsulates generated code destined for various +# output files. The header_output and decoder_output attributes are +# strings containing code destined for decoder.hh and decoder.cc +# respectively. The decode_block attribute contains code to be +# incorporated in the decode function itself (that will also end up in +# decoder.cc). The exec_output attribute is a dictionary with a key +# for each CPU model name; the value associated with a particular key +# is the string of code for that CPU model's exec.cc file. The +# has_decode_default attribute is used in the decode block to allow +# explicit default clauses to override default default clauses. + +class GenCode: + # Constructor. At this point we substitute out all CPU-specific + # symbols. For the exec output, these go into the per-model + # dictionary. For all other output types they get collapsed into + # a single string. + def __init__(self, + header_output = '', decoder_output = '', exec_output = '', + decode_block = '', has_decode_default = False): + self.header_output = expand_cpu_symbols_to_string(header_output) + self.decoder_output = expand_cpu_symbols_to_string(decoder_output) + if isinstance(exec_output, dict): + self.exec_output = exec_output + elif isinstance(exec_output, str): + # If the exec_output arg is a single string, we replicate + # it for each of the CPU models, substituting and + # %(CPU_foo)s params appropriately. + self.exec_output = expand_cpu_symbols_to_dict(exec_output) + self.decode_block = expand_cpu_symbols_to_string(decode_block) + self.has_decode_default = has_decode_default + + # Override '+' operator: generate a new GenCode object that + # concatenates all the individual strings in the operands. + def __add__(self, other): + exec_output = {} + for cpu in CpuModel.list: + n = cpu.name + exec_output[n] = self.exec_output[n] + other.exec_output[n] + return GenCode(self.header_output + other.header_output, + self.decoder_output + other.decoder_output, + exec_output, + self.decode_block + other.decode_block, + self.has_decode_default or other.has_decode_default) + + # Prepend a string (typically a comment) to all the strings. + def prepend_all(self, pre): + self.header_output = pre + self.header_output + self.decoder_output = pre + self.decoder_output + self.decode_block = pre + self.decode_block + for cpu in CpuModel.list: + self.exec_output[cpu.name] = pre + self.exec_output[cpu.name] + + # Wrap the decode block in a pair of strings (e.g., 'case foo:' + # and 'break;'). Used to build the big nested switch statement. + def wrap_decode_block(self, pre, post = ''): + self.decode_block = pre + indent(self.decode_block) + post + ################ # Format object. # @@ -664,24 +735,31 @@ class Format: # constructor: just save away arguments self.id = id self.params = params - # strip blank lines from code (ones at the end are troublesome) - code = re.sub(r'(?m)^\s*$', '', code); - if code == '': - code = ' pass\n' + label = 'def format ' + id + self.user_code = compile(fixPythonIndentation(code), label, 'exec') param_list = string.join(params, ", ") - f = 'def defInst(name, Name, ' + param_list + '):\n' + code - c = compile(f, 'def format ' + id, 'exec') - exec(c) + f = '''def defInst(_code, _context, %s): + my_locals = vars().copy() + exec _code in _context, my_locals + return my_locals\n''' % param_list + c = compile(f, label + ' wrapper', 'exec') + exec c self.func = defInst def defineInst(self, name, args, lineno): - # automatically provide a capitalized version of mnemonic - Name = string.capitalize(name) + context = {} + updateExportContext() + context.update(exportContext) + context.update({ 'name': name, 'Name': string.capitalize(name) }) try: - retval = self.func(name, Name, *args) - except: - error_bt(lineno, 'error defining "%s".' % name) - return retval + vars = self.func(self.user_code, context, *args) + except Exception, exc: + error(lineno, 'error defining "%s": %s.' % (name, exc)) + for k in vars.keys(): + if k not in ('header_output', 'decoder_output', + 'exec_output', 'decode_block'): + del vars[k] + return GenCode(**vars) # Special null format to catch an implicit-format instruction # definition outside of any format block. @@ -766,13 +844,13 @@ def fixPythonIndentation(s): # Error handler. Just call exit. Output formatted to work under # Emacs compile-mode. def error(lineno, string): - sys.exit("%s:%d: %s" % (isa_desc_filename, lineno, string)) + sys.exit("%s:%d: %s" % (input_filename, lineno, string)) # Like error(), but include a Python stack backtrace (for processing # Python exceptions). def error_bt(lineno, string): traceback.print_exc() - print >> sys.stderr, "%s:%d: %s" % (isa_desc_filename, lineno, string) + print >> sys.stderr, "%s:%d: %s" % (input_filename, lineno, string) sys.exit(1) @@ -817,6 +895,37 @@ def substBitOps(code): return code +#################### +# Template objects. +# +# Template objects are format strings that allow substitution from +# the attribute spaces of other objects (e.g. InstObjParams instances). + +class Template: + def __init__(self, t): + self.template = t + + def subst(self, d): + # Start with the template namespace. Make a copy since we're + # going to modify it. + myDict = templateMap.copy() + # if the argument is a dictionary, we just use it. + if isinstance(d, dict): + myDict.update(d) + # if the argument is an object, we use its attribute map. + elif hasattr(d, '__dict__'): + myDict.update(d.__dict__) + else: + raise TypeError, "Template.subst() arg must be or have dictionary" + # CPU-model-specific substitutions are handled later (in GenCode). + return protect_cpu_symbols(self.template) % myDict + + # Convert to string. This handles the case when a template with a + # CPU-specific term gets interpolated into another template or into + # an output block. + def __str__(self): + return expand_cpu_symbols_to_string(self.template) + ##################################################################### # # Code Parser @@ -1111,6 +1220,22 @@ class NPCOperandTraits(OperandTraits): return 'xc->setNextPC(%s);\n' % op_desc.munged_name +exportContextSymbols = ('IntRegOperandTraits', 'FloatRegOperandTraits', + 'ControlRegOperandTraits', 'MemOperandTraits', + 'NPCOperandTraits', 'InstObjParams', 'CodeBlock', + 're', 'string') + +exportContext = {} + +def updateExportContext(): + exportContext.update(exportDict(*exportContextSymbols)) + exportContext.update(templateMap) + + +def exportDict(*symNames): + return dict([(s, eval(s)) for s in symNames]) + + # # Define operand variables that get derived from the basic declaration # of ISA-specific operands in operandTraitsMap. This function must be @@ -1385,10 +1510,6 @@ class InstObjParams: self.mnemonic = mnem self.class_name = class_name self.base_class = base_class - self.exec_func_declarations = ''' - Fault execute(SimpleCPUExecContext *, Trace::InstRecord *); - Fault execute(FullCPUExecContext *, Trace::InstRecord *); -''' if code_block: for code_attr in code_block.__dict__.keys(): setattr(self, code_attr, getattr(code_block, code_attr)) @@ -1419,48 +1540,125 @@ class InstObjParams: else: self.fp_enable_check = '' - def _subst(self, template): - try: - return template % self.__dict__ - except KeyError, key: - raise KeyError, 'InstObjParams.subst: no definition for %s' % key +####################### +# +# Output file template +# - def subst(self, *args): - result = [] - for t in args: - try: template = templateMap[t] - except KeyError: - error(0, 'InstObjParams::subst: undefined template "%s"' % t) - if template.find('%(cpu_model)') != -1: - tmp = '' - for cpu_model in ('SimpleCPUExecContext', 'FullCPUExecContext'): - self.cpu_model = cpu_model - tmp += self._subst(template) - result.append(tmp) - else: - result.append(self._subst(template)) - if len(args) == 1: - result = result[0] - return result +file_template = ''' +/* + * Copyright (c) 2003 + * The Regents of The University of Michigan + * All Rights Reserved + * + * This code is part of the M5 simulator, developed by Nathan Binkert, + * Erik Hallnor, Steve Raasch, and Steve Reinhardt, with contributions + * from Ron Dreslinski, Dave Greene, and Lisa Hsu. + * + * Permission is granted to use, copy, create derivative works and + * redistribute this software and such derivative works for any + * purpose, so long as the copyright notice above, this grant of + * permission, and the disclaimer below appear in all copies made; and + * so long as the name of The University of Michigan is not used in + * any advertising or publicity pertaining to the use or distribution + * of this software without specific, written prior authorization. + * + * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE + * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND + * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE + * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT, + * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM + * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN + * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGES. + */ + +/* + * DO NOT EDIT THIS FILE!!! + * + * It was automatically generated from the ISA description in %(filename)s + */ + +%(includes)s + +%(global_output)s + +namespace %(namespace)s { + +%(namespace_output)s + +} // namespace %(namespace)s +''' + + +# Update the output file only if the new contents are different from +# the current contents. Minimizes the files that need to be rebuilt +# after minor changes. +def update_if_needed(file, contents): + update = False + if os.access(file, os.R_OK): + f = open(file, 'r') + old_contents = f.read() + f.close() + if contents != old_contents: + print 'Updating', file + os.remove(file) # in case it's write-protected + update = True + else: + print 'File', file, 'is unchanged' + else: + print 'Generating', file + update = True + if update: + f = open(file, 'w') + f.write(contents) + f.close() # # Read in and parse the ISA description. # -def parse_isa_desc(isa_desc_file, decoder_file): - # Arguments are the name of the ISA description (input) file and - # the name of the C++ decoder (output) file. - global isa_desc_filename, decoder_filename - isa_desc_filename = isa_desc_file - decoder_filename = decoder_file +def parse_isa_desc(isa_desc_file, output_dir, include_path): + # set a global var for the input filename... used in error messages + global input_filename + input_filename = isa_desc_file # Suck the ISA description file in. - input = open(isa_desc_filename) + input = open(isa_desc_file) isa_desc = input.read() input.close() # Parse it. - yacc.parse(isa_desc) + (isa_name, namespace, global_code, namespace_code) = yacc.parse(isa_desc) + + # grab the last three path components of isa_desc_file to put in + # the output + filename = '/'.join(isa_desc_file.split('/')[-3:]) + + # generate decoder.hh + includes = '#include "base/bitfield.hh" // for bitfield support' + global_output = global_code.header_output + namespace_output = namespace_code.header_output + update_if_needed(output_dir + '/decoder.hh', file_template % vars()) + + # generate decoder.cc + includes = '#include "%s/decoder.hh"' % include_path + global_output = global_code.decoder_output + namespace_output = namespace_code.decoder_output + namespace_output += namespace_code.decode_block + update_if_needed(output_dir + '/decoder.cc', file_template % vars()) + + # generate per-cpu exec files + for cpu in CpuModel.list: + includes = '#include "%s/decoder.hh"\n' % include_path + includes += cpu.includes + global_output = global_code.exec_output[cpu.name] + namespace_output = namespace_code.exec_output[cpu.name] + update_if_needed(output_dir + '/' + cpu.filename, + file_template % vars()) # Called as script: get args from command line. if __name__ == '__main__': - parse_isa_desc(sys.argv[1], sys.argv[2]) + parse_isa_desc(sys.argv[1], sys.argv[2], sys.argv[3]) diff --git a/cpu/simple_cpu/simple_cpu.hh b/cpu/simple_cpu/simple_cpu.hh index 07d6cb0c9..a04dcd057 100644 --- a/cpu/simple_cpu/simple_cpu.hh +++ b/cpu/simple_cpu/simple_cpu.hh @@ -34,7 +34,7 @@ #include "base/loader/symtab.hh" #include "cpu/pc_event.hh" #include "base/statistics.hh" - +#include "cpu/exec_context.hh" // forward declarations #ifdef FULL_SYSTEM @@ -46,6 +46,11 @@ class PhysicalMemory; class RemoteGDB; class GDBListener; + +#else + +class Process; + #endif // FULL_SYSTEM class MemInterface; @@ -305,6 +310,4 @@ class SimpleCPU : public BaseCPU ExecContext *xcBase() { return xc; } }; -typedef SimpleCPU SimpleCPUExecContext; - #endif // __SIMPLE_CPU_HH__ diff --git a/cpu/static_inst.hh b/cpu/static_inst.hh index 57208f8e6..1065fa3d4 100644 --- a/cpu/static_inst.hh +++ b/cpu/static_inst.hh @@ -42,9 +42,7 @@ // forward declarations class ExecContext; class DynInst; -typedef DynInst FullCPUExecContext; class SimpleCPU; -typedef SimpleCPU SimpleCPUExecContext; class SymbolTable; namespace Trace { @@ -249,7 +247,8 @@ class StaticInst : public StaticInstBase * obtain the dependence info (numSrcRegs and srcRegIdx[]) for * just the EA computation. */ - virtual StaticInstPtr eaCompInst() { return nullStaticInstPtr; } + virtual const + StaticInstPtr &eaCompInst() const { return nullStaticInstPtr; } /** * Memory references only: returns "fake" instruction representing @@ -257,7 +256,8 @@ class StaticInst : public StaticInstBase * obtain the dependence info (numSrcRegs and srcRegIdx[]) for * just the memory access (not the EA computation). */ - virtual StaticInstPtr memAccInst() { return nullStaticInstPtr; } + virtual const + StaticInstPtr &memAccInst() const { return nullStaticInstPtr; } /// The binary machine instruction. const MachInst machInst; @@ -307,14 +307,12 @@ class StaticInst : public StaticInstBase /** * Execute this instruction under SimpleCPU model. */ - virtual Fault execute(SimpleCPUExecContext *xc, - Trace::InstRecord *traceData) = 0; + virtual Fault execute(SimpleCPU *xc, Trace::InstRecord *traceData) = 0; /** * Execute this instruction under detailed FullCPU model. */ - virtual Fault execute(FullCPUExecContext *xc, - Trace::InstRecord *traceData) = 0; + virtual Fault execute(DynInst *xc, Trace::InstRecord *traceData) = 0; /** * Return the target address for a PC-relative branch.