void Jit::Comp_Generic(MIPSOpcode op) { FlushAll(); MIPSInterpretFunc func = MIPSGetInterpretFunc(op); _dbg_assert_msg_(JIT, (MIPSGetInfo(op) & DELAYSLOT) == 0, "Cannot use interpreter for branch ops."); if (func) { MOV(32, M(&mips_->pc), Imm32(js.compilerPC)); if (USE_JIT_MISSMAP) ABI_CallFunctionC(&JitLogMiss, op.encoding); else ABI_CallFunctionC(func, op.encoding); } else ERROR_LOG_REPORT(JIT, "Trying to compile instruction %08x that can't be interpreted", op.encoding); const MIPSInfo info = MIPSGetInfo(op); if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0) { // If it does eat them, it'll happen in MIPSCompileOp(). if ((info & OUT_EAT_PREFIX) == 0) js.PrefixUnknown(); } }
bool IsRegisterUsed(u32 reg, u32 addr) { while (true) { u32 op = Memory::Read_Instruction(addr); u32 info = MIPSGetInfo(op); if ( ((info & IN_RS) || (info & IN_RS_SHIFT) || (info & IN_RS_ADDR)) && (MIPS_GET_RS(op) == reg) ) return true; if ((info & IN_RT) && (MIPS_GET_RT(op) == reg)) return true; if ((info & IS_CONDBRANCH)) return true; // could also follow both paths if ((info & IS_JUMP)) return true; // could also follow the path if ((info & OUT_RT) && (MIPS_GET_RT(op) == reg)) return false; //the reg got clobbed! yay! if ((info & OUT_RD) && (MIPS_GET_RD(op) == reg)) return false; //the reg got clobbed! yay! if ((info & OUT_RA) && (reg == MIPS_REG_RA)) return false; //the reg got clobbed! yay! addr+=4; } return true; }
std::vector<int> GetInputRegs(u32 op) { std::vector<int> vec; u32 info = MIPSGetInfo(op); if ((info & IS_VFPU) == 0) { if (info & IN_RS) vec.push_back(MIPS_GET_RS(op)); if (info & IN_RT) vec.push_back(MIPS_GET_RT(op)); } return vec; }
int GetOutReg(u32 op) { u32 opinfo = MIPSGetInfo(op); if (opinfo & OUT_RT) return MIPS_GET_RT(op); if (opinfo & OUT_RD) return MIPS_GET_RD(op); if (opinfo & OUT_RA) return MIPS_REG_RA; return -1; }
void Jit::EatInstruction(u32 op) { u32 info = MIPSGetInfo(op); _dbg_assert_msg_(JIT, !(info & DELAYSLOT), "Never eat a branch op."); _dbg_assert_msg_(JIT, !js.inDelaySlot, "Never eat an instruction inside a delayslot."); CheckJitBreakpoint(js.compilerPC + 4, 0); js.numInstructions++; js.compilerPC += 4; js.downcountAmount += MIPSGetInstructionCycleEstimate(op); }
std::vector<int> GetOutputRegs(u32 op) { std::vector<int> vec; u32 info = MIPSGetInfo(op); if ((info & IS_VFPU) == 0) { if (info & OUT_RD) vec.push_back(MIPS_GET_RD(op)); if (info & OUT_RT) vec.push_back(MIPS_GET_RT(op)); if (info & OUT_RA) vec.push_back(MIPS_REG_RA); } return vec; }
void Jit::Comp_Generic(u32 op) { FlushAll(); MIPSInterpretFunc func = MIPSGetInterpretFunc(op); _dbg_assert_msg_(JIT, (MIPSGetInfo(op) & DELAYSLOT) == 0, "Cannot use interpreter for branch ops."); if (func) { MOV(32, M(&mips_->pc), Imm32(js.compilerPC)); if (USE_JIT_MISSMAP) ABI_CallFunctionC((void *)&JitLogMiss, op); else ABI_CallFunctionC((void *)func, op); } else _dbg_assert_msg_(JIT, 0, "Trying to compile instruction that can't be interpreted"); // Might have eaten prefixes, hard to tell... if ((MIPSGetInfo(op) & IS_VFPU) != 0) js.PrefixStart(); }
void MipsJit::EatInstruction(MIPSOpcode op) { MIPSInfo info = MIPSGetInfo(op); if (info & DELAYSLOT) { ERROR_LOG_REPORT_ONCE(ateDelaySlot, JIT, "Ate a branch op."); } if (js.inDelaySlot) { ERROR_LOG_REPORT_ONCE(ateInDelaySlot, JIT, "Ate an instruction inside a delay slot."); } js.numInstructions++; js.compilerPC += 4; js.downcountAmount += MIPSGetInstructionCycleEstimate(op); }
u32 GetSureBranchTarget(u32 addr) { MIPSOpcode op = Memory::Read_Instruction(addr, true); if (op != 0) { MIPSInfo info = MIPSGetInfo(op); if ((info & IS_CONDBRANCH) && !(info & (IN_FPUFLAG | IS_VFPU))) { bool sure; bool takeBranch; switch (info & CONDTYPE_MASK) { case CONDTYPE_EQ: sure = _RS == _RT; takeBranch = true; break; case CONDTYPE_NE: sure = _RS == _RT; takeBranch = false; break; case CONDTYPE_LEZ: case CONDTYPE_GEZ: sure = _RS == 0; takeBranch = true; break; case CONDTYPE_LTZ: case CONDTYPE_GTZ: sure = _RS == 0; takeBranch = false; break; default: sure = false; } if (sure && takeBranch) return addr + 4 + ((signed short)(op&0xFFFF)<<2); else if (sure && !takeBranch) return addr + 8; else return INVALIDTARGET; } else return INVALIDTARGET; } else return INVALIDTARGET; }
bool IsDelaySlotNice(u32 branch, u32 delayslot) { int outReg = GetOutReg(delayslot); if (outReg != -1) { if (ReadsFromReg(branch, outReg)) { return false; //evil :( } else { return false; //aggh this should be true but doesn't work } } else { // Check for FPU flag if ((MIPSGetInfo(delayslot) & OUT_FPUFLAG) && (MIPSGetInfo(branch) & IN_FPUFLAG)) return false; return true; //nice :) } }
u32 GetBranchTargetNoRA(u32 addr, MIPSOpcode op) { if (op != 0) { MIPSInfo info = MIPSGetInfo(op); if ((info & IS_CONDBRANCH) && !(info & OUT_RA)) { return addr + 4 + ((signed short)(op&0xFFFF)<<2); } else return INVALIDTARGET; } else return INVALIDTARGET; }
bool ReadsFromReg(u32 op, u32 reg) { u32 opinfo = MIPSGetInfo(op); if (opinfo & IN_RT) { if (MIPS_GET_RT(opinfo) == reg) return true; } if (opinfo & (IN_RS | IN_RS_ADDR | IN_RS_SHIFT)) { if (MIPS_GET_RS(opinfo) == reg) return true; } return false; //TODO: there are more cases! }
u32 GetBranchTarget(u32 addr) { u32 op = Memory::Read_Instruction(addr); if (op) { u32 info = MIPSGetInfo(op); if (info & IS_CONDBRANCH) { return addr + ((signed short)(op&0xFFFF)<<2); } else return INVALIDTARGET; } else return INVALIDTARGET; }
u32 GetJumpTarget(u32 addr) { MIPSOpcode op = Memory::Read_Instruction(addr, true); if (op != 0) { MIPSInfo info = MIPSGetInfo(op); if ((info & IS_JUMP) && (info & IN_IMM26)) { u32 target = (addr & 0xF0000000) | ((op&0x03FFFFFF) << 2); return target; } else return INVALIDTARGET; } else return INVALIDTARGET; }
void FakeJit::Comp_Generic(MIPSOpcode op) { FlushAll(); MIPSInterpretFunc func = MIPSGetInterpretFunc(op); if (func) { SaveDowncount(); RestoreDowncount(); } const MIPSInfo info = MIPSGetInfo(op); if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0) { // If it does eat them, it'll happen in MIPSCompileOp(). if ((info & OUT_EAT_PREFIX) == 0) js.PrefixUnknown(); } }
u32 GetSureBranchTarget(u32 addr) { u32 op = Memory::Read_Instruction(addr); if (op) { u32 info = MIPSGetInfo(op); if (info & IS_CONDBRANCH) { //TODO: safer check if ((op & 0xFFFF0000) == 0x10000000) return addr + ((signed short)(op&0xFFFF)<<2); else return INVALIDTARGET; } else return INVALIDTARGET; } else return INVALIDTARGET; }
void HashFunctions() { for (vector<Function>::iterator iter = functions.begin(); iter!=functions.end(); iter++) { Function &f=*iter; u32 hash = 0x1337babe; for (u32 addr = f.start; addr <= f.end; addr++) { u32 validbits = 0xFFFFFFFF; u32 instr = Memory::Read_Instruction(addr); u32 flags = MIPSGetInfo(instr); if (flags & IN_IMM16) validbits&=~0xFFFF; if (flags & IN_IMM26) validbits&=~0x3FFFFFF; hash = _rotl(hash,13); hash ^= (instr&validbits); } f.hash=hash; f.hasHash=true; } }
void MipsJit::Comp_Generic(MIPSOpcode op) { FlushAll(); MIPSInterpretFunc func = MIPSGetInterpretFunc(op); if (func) { //SaveDowncount(); RestoreRoundingMode(); // Move Imm32(js.compilerPC) in to M(&mips_->pc) QuickCallFunction(V1, (void *)func); ApplyRoundingMode(); //RestoreDowncount(); } const MIPSInfo info = MIPSGetInfo(op); if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0) { // If it does eat them, it'll happen in MIPSCompileOp(). if ((info & OUT_EAT_PREFIX) == 0) js.PrefixUnknown(); } }
bool IsBranch(MIPSOpcode op) { return (MIPSGetInfo(op) & IS_CONDBRANCH) == IS_CONDBRANCH; }
void Analyze(u32 address) { //set everything to -1 (FF) memset(regAnal, 255, sizeof(AnalysisResults)*32); for (int i=0; i<32; i++) { regAnal[i].used=false; regAnal[i].readCount=0; regAnal[i].writeCount=0; regAnal[i].readAsAddrCount=0; } u32 addr = address; bool exitFlag = false; while (true) { u32 op = Memory::Read_Instruction(addr); u32 info = MIPSGetInfo(op); for (int reg=0; reg < 32; reg++) { int rs = MIPS_GET_RS(op); int rt = MIPS_GET_RT(op); int rd = MIPS_GET_RD(op); if ( ((info & IN_RS) && (rs == reg)) || ((info & IN_RS_SHIFT) && (rs == reg)) || ((info & IN_RT) && (rt == reg))) { if (regAnal[reg].firstRead == -1) regAnal[reg].firstRead = addr; regAnal[reg].lastRead = addr; regAnal[reg].readCount++; regAnal[reg].used=true; } if ( ((info & IN_RS_ADDR) && (rs == reg)) ) { if (regAnal[reg].firstReadAsAddr == -1) regAnal[reg].firstReadAsAddr = addr; regAnal[reg].lastReadAsAddr = addr; regAnal[reg].readAsAddrCount++; regAnal[reg].used=true; } if ( ((info & OUT_RT) && (rt == reg)) || ((info & OUT_RD) && (rd == reg)) || ((info & OUT_RA) && (reg == MIPS_REG_RA)) ) { if (regAnal[reg].firstWrite == -1) regAnal[reg].firstWrite = addr; regAnal[reg].lastWrite = addr; regAnal[reg].writeCount++; regAnal[reg].used=true; } } if (exitFlag) //delay slot done, let's quit! break; if ((info & IS_JUMP) || (info & IS_CONDBRANCH)) { exitFlag = true; // now do the delay slot } addr += 4; } int numUsedRegs=0; static int totalUsedRegs=0; static int numAnalyzings=0; for (int i=0; i<32; i++) { if (regAnal[i].used) numUsedRegs++; } totalUsedRegs+=numUsedRegs; numAnalyzings++; DEBUG_LOG(CPU,"[ %08x ] Used regs: %i Average: %f",address,numUsedRegs,(float)totalUsedRegs/(float)numAnalyzings); }
bool IsVFPUBranch(MIPSOpcode op) { return (MIPSGetInfo(op) & (IS_VFPU | IS_CONDBRANCH)) == (IS_VFPU | IS_CONDBRANCH); }
void DisassemblyFunction::load() { generateBranchLines(); // gather all branch targets std::set<u32> branchTargets; for (size_t i = 0; i < lines.size(); i++) { switch (lines[i].type) { case LINE_DOWN: branchTargets.insert(lines[i].second); break; case LINE_UP: branchTargets.insert(lines[i].first); break; default: break; } } DebugInterface* cpu = DisassemblyManager::getCpu(); u32 funcPos = address; u32 funcEnd = address+size; u32 nextData = symbolMap.GetNextSymbolAddress(funcPos-1,ST_DATA); u32 opcodeSequenceStart = funcPos; while (funcPos < funcEnd) { if (funcPos == nextData) { if (opcodeSequenceStart != funcPos) addOpcodeSequence(opcodeSequenceStart,funcPos); DisassemblyData* data = new DisassemblyData(funcPos,symbolMap.GetDataSize(funcPos),symbolMap.GetDataType(funcPos)); entries[funcPos] = data; lineAddresses.push_back(funcPos); funcPos += data->getTotalSize(); nextData = symbolMap.GetNextSymbolAddress(funcPos-1,ST_DATA); opcodeSequenceStart = funcPos; continue; } // force align if (funcPos % 4) { u32 nextPos = (funcPos+3) & ~3; DisassemblyComment* comment = new DisassemblyComment(funcPos,nextPos-funcPos,".align","4"); entries[funcPos] = comment; lineAddresses.push_back(funcPos); funcPos = nextPos; opcodeSequenceStart = funcPos; continue; } MIPSAnalyst::MipsOpcodeInfo opInfo = MIPSAnalyst::GetOpcodeInfo(cpu,funcPos); u32 opAddress = funcPos; funcPos += 4; // skip branches and their delay slots if (opInfo.isBranch) { funcPos += 4; continue; } // lui if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData) { MIPSOpcode next = Memory::Read_Instruction(funcPos); MIPSInfo nextInfo = MIPSGetInfo(next); u32 immediate = ((opInfo.encodedOpcode & 0xFFFF) << 16) + (s16)(next.encoding & 0xFFFF); int rt = MIPS_GET_RT(opInfo.encodedOpcode); int nextRs = MIPS_GET_RS(next.encoding); int nextRt = MIPS_GET_RT(next.encoding); // both rs and rt of the second op have to match rt of the first, // otherwise there may be hidden consequences if the macro is displayed. // also, don't create a macro if something branches into the middle of it if (nextRs == rt && nextRt == rt && branchTargets.find(funcPos) == branchTargets.end()) { DisassemblyMacro* macro = NULL; switch (MIPS_GET_OP(next.encoding)) { case 0x09: // addiu macro = new DisassemblyMacro(opAddress); macro->setMacroLi(immediate,rt); funcPos += 4; break; case 0x20: // lb case 0x21: // lh case 0x23: // lw case 0x24: // lbu case 0x25: // lhu case 0x28: // sb case 0x29: // sh case 0x2B: // sw macro = new DisassemblyMacro(opAddress); int dataSize; switch (nextInfo & MEMTYPE_MASK) { case MEMTYPE_BYTE: dataSize = 1; break; case MEMTYPE_HWORD: dataSize = 2; break; case MEMTYPE_WORD: case MEMTYPE_FLOAT: dataSize = 4; break; case MEMTYPE_VQUAD: dataSize = 16; break; default: return; } macro->setMacroMemory(MIPSGetName(next),immediate,rt,dataSize); funcPos += 4; break; } if (macro != NULL) { if (opcodeSequenceStart != opAddress) addOpcodeSequence(opcodeSequenceStart,opAddress); entries[opAddress] = macro; for (int i = 0; i < macro->getNumLines(); i++) { lineAddresses.push_back(macro->getLineAddress(i)); } opcodeSequenceStart = funcPos; continue; } } } // just a normal opcode } if (opcodeSequenceStart != funcPos) addOpcodeSequence(opcodeSequenceStart,funcPos); }