| // Copyright (c) 2006-2007 The Regents of The University of Michigan | |
| // All rights reserved. | |
| // | |
| // 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. | |
| // | |
| // Authors: Ali Saidi | |
| // Gabe Black | |
| // Steve Reinhardt | |
| //////////////////////////////////////////////////////////////////// | |
| // | |
| // Base class for sparc instructions, and some support functions | |
| // | |
| output header {{ | |
| union CondCodes | |
| { | |
| struct | |
| { | |
| uint8_t c:1; | |
| uint8_t v:1; | |
| uint8_t z:1; | |
| uint8_t n:1; | |
| }; | |
| uint32_t bits; | |
| }; | |
| enum CondTest | |
| { | |
| Always=0x8, | |
| Never=0x0, | |
| NotEqual=0x9, | |
| Equal=0x1, | |
| Greater=0xA, | |
| LessOrEqual=0x2, | |
| GreaterOrEqual=0xB, | |
| Less=0x3, | |
| GreaterUnsigned=0xC, | |
| LessOrEqualUnsigned=0x4, | |
| CarryClear=0xD, | |
| CarrySet=0x5, | |
| Positive=0xE, | |
| Negative=0x6, | |
| OverflowClear=0xF, | |
| OverflowSet=0x7 | |
| }; | |
| enum FpCondTest | |
| { | |
| FAlways=0x8, | |
| FNever=0x0, | |
| FUnordered=0x7, | |
| FGreater=0x6, | |
| FUnorderedOrGreater=0x5, | |
| FLess=0x4, | |
| FUnorderedOrLess=0x3, | |
| FLessOrGreater=0x2, | |
| FNotEqual=0x1, | |
| FEqual=0x9, | |
| FUnorderedOrEqual=0xA, | |
| FGreaterOrEqual=0xB, | |
| FUnorderedOrGreaterOrEqual=0xC, | |
| FLessOrEqual=0xD, | |
| FUnorderedOrLessOrEqual=0xE, | |
| FOrdered=0xF | |
| }; | |
| extern const char *CondTestAbbrev[]; | |
| /** | |
| * Base class for all SPARC static instructions. | |
| */ | |
| class SparcStaticInst : public StaticInst | |
| { | |
| protected: | |
| // Constructor. | |
| SparcStaticInst(const char *mnem, | |
| ExtMachInst _machInst, OpClass __opClass) | |
| : StaticInst(mnem, _machInst, __opClass) | |
| { | |
| } | |
| std::string generateDisassembly(Addr pc, | |
| const SymbolTable *symtab) const; | |
| void printReg(std::ostream &os, int reg) const; | |
| void printSrcReg(std::ostream &os, int reg) const; | |
| void printDestReg(std::ostream &os, int reg) const; | |
| void printRegArray(std::ostream &os, | |
| const RegIndex indexArray[], int num) const; | |
| void advancePC(SparcISA::PCState &pcState) const; | |
| }; | |
| bool passesFpCondition(uint32_t fcc, uint32_t condition); | |
| bool passesCondition(uint32_t codes, uint32_t condition); | |
| inline int64_t | |
| sign_ext(uint64_t data, int origWidth) | |
| { | |
| int shiftAmount = 64 - origWidth; | |
| return (((int64_t)data) << shiftAmount) >> shiftAmount; | |
| } | |
| }}; | |
| output decoder {{ | |
| const char *CondTestAbbrev[] = | |
| { | |
| "nev", // Never | |
| "e", // Equal | |
| "le", // Less or Equal | |
| "l", // Less | |
| "leu", // Less or Equal Unsigned | |
| "c", // Carry set | |
| "n", // Negative | |
| "o", // Overflow set | |
| "a", // Always | |
| "ne", // Not Equal | |
| "g", // Greater | |
| "ge", // Greater or Equal | |
| "gu", // Greater Unsigned | |
| "cc", // Carry clear | |
| "p", // Positive | |
| "oc" // Overflow Clear | |
| }; | |
| }}; | |
| def template ROrImmDecode {{ | |
| { | |
| return (I ? (SparcStaticInst *)(new %(class_name)sImm(machInst)) | |
| : (SparcStaticInst *)(new %(class_name)s(machInst))); | |
| } | |
| }}; | |
| output header {{ | |
| union DoubleSingle | |
| { | |
| double d; | |
| uint64_t ui; | |
| uint32_t s[2]; | |
| DoubleSingle(double _d) : d(_d) | |
| {} | |
| DoubleSingle(uint64_t _ui) : ui(_ui) | |
| {} | |
| DoubleSingle(uint32_t _s0, uint32_t _s1) | |
| { | |
| s[0] = _s0; | |
| s[1] = _s1; | |
| } | |
| }; | |
| }}; | |
| let {{ | |
| def filterDoubles(code): | |
| assignRE = re.compile(r'\s*=(?!=)', re.MULTILINE) | |
| for opName in ("Frd", "Frs1", "Frs2", "Frd_N"): | |
| next_pos = 0 | |
| operandsREString = (r''' | |
| (?<!\w) # neg. lookbehind assertion: prevent partial matches | |
| ((%s)(?:_([^\W_]+))?) # match: operand with optional '.' then suffix | |
| (?!\w) # neg. lookahead assertion: prevent partial matches | |
| ''' % opName) | |
| operandsRE = re.compile(operandsREString, re.MULTILINE|re.VERBOSE) | |
| is_src = False | |
| is_dest = False | |
| extension = None | |
| foundOne = False | |
| while 1: | |
| match = operandsRE.search(code, next_pos) | |
| if not match: | |
| break | |
| foundOne = True | |
| op = match.groups() | |
| (op_full, op_base, op_ext) = op | |
| is_dest_local = (assignRE.match(code, match.end()) != None) | |
| is_dest = is_dest or is_dest_local | |
| is_src = is_src or not is_dest_local | |
| if extension and extension != op_ext: | |
| raise Exception, "Inconsistent extensions in double filter." | |
| extension = op_ext | |
| next_pos = match.end() | |
| if foundOne: | |
| # Get rid of any unwanted extension | |
| code = operandsRE.sub(op_base, code) | |
| is_int = False | |
| member = "d" | |
| if extension in ("sb", "ub", "shw", "uhw", "sw", "uw", "sdw", "udw"): | |
| is_int = True | |
| member = "ui" | |
| if is_src: | |
| code = ("%s = DoubleSingle(%s_high, %s_low).%s;" % \ | |
| (opName, opName, opName, member)) + code | |
| if is_dest: | |
| code += ''' | |
| %s_low = DoubleSingle(%s).s[1]; | |
| %s_high = DoubleSingle(%s).s[0];''' % \ | |
| (opName, opName, opName, opName) | |
| if is_int: | |
| code = ("uint64_t %s;" % opName) + code | |
| else: | |
| code = ("double %s;" % opName) + code | |
| return code | |
| }}; | |
| let {{ | |
| def splitOutImm(code): | |
| matcher = re.compile(r'Rs(?P<rNum>\d)_or_imm(?P<iNum>\d+)(?P<typeQual>_[^\W_]+)?') | |
| rOrImmMatch = matcher.search(code) | |
| if (rOrImmMatch == None): | |
| return (False, code, '', '', '') | |
| rString = rOrImmMatch.group("rNum") | |
| if (rOrImmMatch.group("typeQual") != None): | |
| rString += rOrImmMatch.group("typeQual") | |
| iString = rOrImmMatch.group("iNum") | |
| orig_code = code | |
| code = matcher.sub('Rs' + rString, orig_code) | |
| imm_code = matcher.sub('imm', orig_code) | |
| return (True, code, imm_code, rString, iString) | |
| }}; | |
| output decoder {{ | |
| inline void printMnemonic(std::ostream &os, const char * mnemonic) | |
| { | |
| ccprintf(os, "\t%s ", mnemonic); | |
| } | |
| void SparcStaticInst::printRegArray(std::ostream &os, | |
| const RegIndex indexArray[], int num) const | |
| { | |
| if (num <= 0) | |
| return; | |
| printReg(os, indexArray[0]); | |
| for (int x = 1; x < num; x++) { | |
| os << ", "; | |
| printReg(os, indexArray[x]); | |
| } | |
| } | |
| void | |
| SparcStaticInst::advancePC(SparcISA::PCState &pcState) const | |
| { | |
| pcState.advance(); | |
| } | |
| void | |
| SparcStaticInst::printSrcReg(std::ostream &os, int reg) const | |
| { | |
| if (_numSrcRegs > reg) | |
| printReg(os, _srcRegIdx[reg]); | |
| } | |
| void | |
| SparcStaticInst::printDestReg(std::ostream &os, int reg) const | |
| { | |
| if (_numDestRegs > reg) | |
| printReg(os, _destRegIdx[reg]); | |
| } | |
| void | |
| SparcStaticInst::printReg(std::ostream &os, int reg) const | |
| { | |
| const int MaxGlobal = 8; | |
| const int MaxOutput = 16; | |
| const int MaxLocal = 24; | |
| const int MaxInput = 32; | |
| const int MaxMicroReg = 40; | |
| if (reg < FP_Reg_Base) { | |
| // If we used a register from the next or previous window, | |
| // take out the offset. | |
| while (reg >= MaxMicroReg) | |
| reg -= MaxMicroReg; | |
| if (reg == FramePointerReg) | |
| ccprintf(os, "%%fp"); | |
| else if (reg == StackPointerReg) | |
| ccprintf(os, "%%sp"); | |
| else if (reg < MaxGlobal) | |
| ccprintf(os, "%%g%d", reg); | |
| else if (reg < MaxOutput) | |
| ccprintf(os, "%%o%d", reg - MaxGlobal); | |
| else if (reg < MaxLocal) | |
| ccprintf(os, "%%l%d", reg - MaxOutput); | |
| else if (reg < MaxInput) | |
| ccprintf(os, "%%i%d", reg - MaxLocal); | |
| else if (reg < MaxMicroReg) | |
| ccprintf(os, "%%u%d", reg - MaxInput); | |
| // The fake int regs that are really control regs | |
| else { | |
| switch (reg - MaxMicroReg) { | |
| case 1: | |
| ccprintf(os, "%%y"); | |
| break; | |
| case 2: | |
| ccprintf(os, "%%ccr"); | |
| break; | |
| case 3: | |
| ccprintf(os, "%%cansave"); | |
| break; | |
| case 4: | |
| ccprintf(os, "%%canrestore"); | |
| break; | |
| case 5: | |
| ccprintf(os, "%%cleanwin"); | |
| break; | |
| case 6: | |
| ccprintf(os, "%%otherwin"); | |
| break; | |
| case 7: | |
| ccprintf(os, "%%wstate"); | |
| break; | |
| } | |
| } | |
| } else if (reg < Misc_Reg_Base) { | |
| ccprintf(os, "%%f%d", reg - FP_Reg_Base); | |
| } else { | |
| switch (reg - Misc_Reg_Base) { | |
| case MISCREG_ASI: | |
| ccprintf(os, "%%asi"); | |
| break; | |
| case MISCREG_FPRS: | |
| ccprintf(os, "%%fprs"); | |
| break; | |
| case MISCREG_PCR: | |
| ccprintf(os, "%%pcr"); | |
| break; | |
| case MISCREG_PIC: | |
| ccprintf(os, "%%pic"); | |
| break; | |
| case MISCREG_GSR: | |
| ccprintf(os, "%%gsr"); | |
| break; | |
| case MISCREG_SOFTINT: | |
| ccprintf(os, "%%softint"); | |
| break; | |
| case MISCREG_SOFTINT_SET: | |
| ccprintf(os, "%%softint_set"); | |
| break; | |
| case MISCREG_SOFTINT_CLR: | |
| ccprintf(os, "%%softint_clr"); | |
| break; | |
| case MISCREG_TICK_CMPR: | |
| ccprintf(os, "%%tick_cmpr"); | |
| break; | |
| case MISCREG_STICK: | |
| ccprintf(os, "%%stick"); | |
| break; | |
| case MISCREG_STICK_CMPR: | |
| ccprintf(os, "%%stick_cmpr"); | |
| break; | |
| case MISCREG_TPC: | |
| ccprintf(os, "%%tpc"); | |
| break; | |
| case MISCREG_TNPC: | |
| ccprintf(os, "%%tnpc"); | |
| break; | |
| case MISCREG_TSTATE: | |
| ccprintf(os, "%%tstate"); | |
| break; | |
| case MISCREG_TT: | |
| ccprintf(os, "%%tt"); | |
| break; | |
| case MISCREG_TICK: | |
| ccprintf(os, "%%tick"); | |
| break; | |
| case MISCREG_TBA: | |
| ccprintf(os, "%%tba"); | |
| break; | |
| case MISCREG_PSTATE: | |
| ccprintf(os, "%%pstate"); | |
| break; | |
| case MISCREG_TL: | |
| ccprintf(os, "%%tl"); | |
| break; | |
| case MISCREG_PIL: | |
| ccprintf(os, "%%pil"); | |
| break; | |
| case MISCREG_CWP: | |
| ccprintf(os, "%%cwp"); | |
| break; | |
| case MISCREG_GL: | |
| ccprintf(os, "%%gl"); | |
| break; | |
| case MISCREG_HPSTATE: | |
| ccprintf(os, "%%hpstate"); | |
| break; | |
| case MISCREG_HTSTATE: | |
| ccprintf(os, "%%htstate"); | |
| break; | |
| case MISCREG_HINTP: | |
| ccprintf(os, "%%hintp"); | |
| break; | |
| case MISCREG_HTBA: | |
| ccprintf(os, "%%htba"); | |
| break; | |
| case MISCREG_HSTICK_CMPR: | |
| ccprintf(os, "%%hstick_cmpr"); | |
| break; | |
| case MISCREG_HVER: | |
| ccprintf(os, "%%hver"); | |
| break; | |
| case MISCREG_STRAND_STS_REG: | |
| ccprintf(os, "%%strand_sts_reg"); | |
| break; | |
| case MISCREG_FSR: | |
| ccprintf(os, "%%fsr"); | |
| break; | |
| default: | |
| ccprintf(os, "%%ctrl%d", reg - Misc_Reg_Base); | |
| } | |
| } | |
| } | |
| std::string | |
| SparcStaticInst::generateDisassembly(Addr pc, | |
| const SymbolTable *symtab) const | |
| { | |
| std::stringstream ss; | |
| printMnemonic(ss, 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(); | |
| } | |
| bool | |
| passesFpCondition(uint32_t fcc, uint32_t condition) | |
| { | |
| bool u = (fcc == 3); | |
| bool g = (fcc == 2); | |
| bool l = (fcc == 1); | |
| bool e = (fcc == 0); | |
| switch (condition) { | |
| case FAlways: | |
| return 1; | |
| case FNever: | |
| return 0; | |
| case FUnordered: | |
| return u; | |
| case FGreater: | |
| return g; | |
| case FUnorderedOrGreater: | |
| return u || g; | |
| case FLess: | |
| return l; | |
| case FUnorderedOrLess: | |
| return u || l; | |
| case FLessOrGreater: | |
| return l || g; | |
| case FNotEqual: | |
| return l || g || u; | |
| case FEqual: | |
| return e; | |
| case FUnorderedOrEqual: | |
| return u || e; | |
| case FGreaterOrEqual: | |
| return g || e; | |
| case FUnorderedOrGreaterOrEqual: | |
| return u || g || e; | |
| case FLessOrEqual: | |
| return l || e; | |
| case FUnorderedOrLessOrEqual: | |
| return u || l || e; | |
| case FOrdered: | |
| return e || l || g; | |
| } | |
| panic("Tried testing condition nonexistant " | |
| "condition code %d", condition); | |
| } | |
| bool | |
| passesCondition(uint32_t codes, uint32_t condition) | |
| { | |
| CondCodes condCodes; | |
| condCodes.bits = 0; | |
| condCodes.c = codes & 0x1 ? 1 : 0; | |
| condCodes.v = codes & 0x2 ? 1 : 0; | |
| condCodes.z = codes & 0x4 ? 1 : 0; | |
| condCodes.n = codes & 0x8 ? 1 : 0; | |
| switch (condition) { | |
| case Always: | |
| return true; | |
| case Never: | |
| return false; | |
| case NotEqual: | |
| return !condCodes.z; | |
| case Equal: | |
| return condCodes.z; | |
| case Greater: | |
| return !(condCodes.z | (condCodes.n ^ condCodes.v)); | |
| case LessOrEqual: | |
| return condCodes.z | (condCodes.n ^ condCodes.v); | |
| case GreaterOrEqual: | |
| return !(condCodes.n ^ condCodes.v); | |
| case Less: | |
| return (condCodes.n ^ condCodes.v); | |
| case GreaterUnsigned: | |
| return !(condCodes.c | condCodes.z); | |
| case LessOrEqualUnsigned: | |
| return (condCodes.c | condCodes.z); | |
| case CarryClear: | |
| return !condCodes.c; | |
| case CarrySet: | |
| return condCodes.c; | |
| case Positive: | |
| return !condCodes.n; | |
| case Negative: | |
| return condCodes.n; | |
| case OverflowClear: | |
| return !condCodes.v; | |
| case OverflowSet: | |
| return condCodes.v; | |
| } | |
| panic("Tried testing condition nonexistant " | |
| "condition code %d", condition); | |
| } | |
| }}; | |
| output exec {{ | |
| /// Check "FP enabled" machine status bit. Called when executing any FP | |
| /// instruction. | |
| /// @retval Full-system mode: NoFault if FP is enabled, FpDisabled | |
| /// if not. Non-full-system mode: always returns NoFault. | |
| static inline Fault | |
| checkFpEnableFault(CPU_EXEC_CONTEXT *xc) | |
| { | |
| if (FullSystem) { | |
| PSTATE pstate = xc->readMiscReg(MISCREG_PSTATE); | |
| if (pstate.pef && xc->readMiscReg(MISCREG_FPRS) & 0x4) { | |
| return NoFault; | |
| } else { | |
| return std::make_shared<FpDisabled>(); | |
| } | |
| } else { | |
| return NoFault; | |
| } | |
| } | |
| }}; | |