| // 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_Base_DepTag) { |
| // 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 < Ctrl_Base_DepTag) { |
| ccprintf(os, "%%f%d", reg - FP_Base_DepTag); |
| } else { |
| switch (reg - Ctrl_Base_DepTag) { |
| 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 - Ctrl_Base_DepTag); |
| } |
| } |
| } |
| |
| 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)s *xc) |
| { |
| if (FullSystem) { |
| PSTATE pstate = xc->readMiscReg(MISCREG_PSTATE); |
| if (pstate.pef && xc->readMiscReg(MISCREG_FPRS) & 0x4) { |
| return NoFault; |
| } else { |
| return new FpDisabled; |
| } |
| } else { |
| return NoFault; |
| } |
| } |
| }}; |
| |
| |