diff --git a/Core/HLE/ReplaceTables.cpp b/Core/HLE/ReplaceTables.cpp index 76f3eb687b..1bd4804e4a 100644 --- a/Core/HLE/ReplaceTables.cpp +++ b/Core/HLE/ReplaceTables.cpp @@ -52,6 +52,7 @@ static int Replace_atan2f() { RETURNF(atan2f(f1, f2)); return 120; // guess number of cycles } + // Should probably do JIT versions of this, possibly ones that only delegate // large copies to a C function. static int Replace_memcpy() { @@ -151,12 +152,11 @@ static const ReplacementTableEntry entries[] = { { "memcpy", &Replace_memcpy, 0, 0}, { "memmove", &Replace_memmove, 0, 0}, { "memset", &Replace_memset, 0, 0}, - { "strlen", &Replace_strlen, 0, 0}, { "strcpy", &Replace_strcpy, 0, 0}, - { "fabsf", 0, &MIPSComp::Jit::Replace_fabsf, REPFLAG_ALLOWINLINE}, { "strcmp", &Replace_strcmp, 0, 0}, { "strncmp", &Replace_strncmp, 0, 0}, + { "fabsf", 0, &MIPSComp::Jit::Replace_fabsf, REPFLAG_ALLOWINLINE}, // { "vmmul_q_transp", &Replace_vmmul_q_transp, 0, 0}, {} }; @@ -208,11 +208,16 @@ void WriteReplaceInstruction(u32 address, u64 hash, int size) { } bool GetReplacedOpAt(u32 address, u32 *op) { - auto iter = replacedInstructions.find(address); - if (iter != replacedInstructions.end()) { - *op = iter->second; - return true; - } else { - return false; + u32 instr = Memory::Read_U32(address); + if (MIPS_IS_REPLACEMENT(instr)) { + auto iter = replacedInstructions.find(address); + if (iter != replacedInstructions.end()) { + *op = iter->second; + return true; + } else { + return false; + } } + *op = instr; + return true; } diff --git a/Core/MIPS/MIPSDis.cpp b/Core/MIPS/MIPSDis.cpp index 83a80da535..1df4fce89f 100644 --- a/Core/MIPS/MIPSDis.cpp +++ b/Core/MIPS/MIPSDis.cpp @@ -334,6 +334,7 @@ namespace MIPSDis const char *name = MIPSGetName(op); sprintf(out, "%s\t->$%08x",name,addr); } + void Dis_JumpRegType(MIPSOpcode op, char *out) { int rs = _RS; @@ -363,11 +364,18 @@ namespace MIPSDis void Dis_Emuhack(MIPSOpcode op, char *out) { - //const char *name = MIPSGetName(op); - //sprintf(out,"%s\t-",name); - out[0]='*'; - out[1]=0; - // MIPSDisAsm(MIPSComp::GetOriginalOp(op), currentDebugMIPS->GetPC(), out+1); + switch (op.encoding >> 24) { + case 0x68: + strcpy(out, "* jitblock"); + break; + case 0x6a: + strcpy(out, "* replacement"); + break; + default: + out[0]='*'; + out[1]=0; + break; + } } diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index d2ae447c34..6fbf2ea062 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -552,6 +552,10 @@ void Jit::Comp_Jump(MIPSOpcode op) break; case 3: //jal + // Special case for branches to "replace functions": + // if (ReplaceJalTo(targetAddr)) + // return; + // Save return address - might be overwritten by delay slot. gpr.SetImm(MIPS_REG_RA, js.compilerPC + 8); CompileDelaySlot(DELAYSLOT_NICE); diff --git a/Core/MIPS/x86/CompReplace.cpp b/Core/MIPS/x86/CompReplace.cpp index dc4c5d4990..068ba294e6 100644 --- a/Core/MIPS/x86/CompReplace.cpp +++ b/Core/MIPS/x86/CompReplace.cpp @@ -27,6 +27,7 @@ int Jit::Replace_fabsf() { fpr.MapReg(0, MAP_DIRTY | MAP_NOINIT); MOVSS(fpr.RX(0), fpr.R(12)); ANDPS(fpr.RX(0), M((void *)&ssNoSignMask)); + fpr.ReleaseSpillLocks(); return 4; // Number of instructions in the MIPS function } diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index f29c7236e1..f5d9da71b2 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -229,7 +229,7 @@ void Jit::CompileDelaySlot(int flags, RegCacheState *state) SAVE_FLAGS; // preserve flag around the delay slot! js.inDelaySlot = true; - MIPSOpcode op = Memory::Read_Instruction(addr); + MIPSOpcode op = Memory::Read_Opcode_JIT(addr); MIPSCompileOp(op); js.inDelaySlot = false; @@ -323,7 +323,7 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b) // Jit breakpoints are quite fast, so let's do them in release too. CheckJitBreakpoint(js.compilerPC, 0); - MIPSOpcode inst = Memory::Read_Instruction(js.compilerPC); + MIPSOpcode inst = Memory::Read_Opcode_JIT(js.compilerPC); js.downcountAmount += MIPSGetInstructionCycleEstimate(inst); MIPSCompileOp(inst); @@ -372,6 +372,35 @@ void Jit::Comp_RunBlock(MIPSOpcode op) ERROR_LOG(JIT, "Comp_RunBlock"); } +bool Jit::ReplaceJalTo(u32 dest) { + MIPSOpcode op(Memory::Read_U32(dest)); + if (!MIPS_IS_REPLACEMENT(dest)) + return false; + + int index = op.encoding & MIPS_EMUHACK_VALUE_MASK; + const ReplacementTableEntry *entry = GetReplacementFunc(index); + if (!entry) { + ERROR_LOG(HLE, "ReplaceJalTo: Invalid replacement op %08x at %08x", op.encoding, dest); + return false; + } + + // Warning - this might be bad if the code at the destination changes... + if (entry->flags & REPFLAG_ALLOWINLINE) { + // Jackpot! Just do it, no flushing. The code will be entirely inlined. + + // First, compile the delay slot. It's unconditional so no issues. + CompileDelaySlot(DELAYSLOT_NICE); + // Technically, we should write the unused return address to RA, but meh. + MIPSReplaceFunc repl = entry->jitReplaceFunc; + int cycles = (this->*repl)(); + js.downcountAmount += cycles; + // No writing exits, keep going! + return true; + } else { + return false; + } +} + void Jit::Comp_ReplacementFunc(MIPSOpcode op) { // We get here if we execute the first instruction of a replaced function. This means diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index 26c0346dc9..e1b98f088e 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -167,12 +167,14 @@ public: void ClearCache(); void ClearCacheAt(u32 em_address, int length = 4); + private: void GetStateAndFlushAll(RegCacheState &state); void RestoreState(const RegCacheState state); void FlushAll(); void FlushPrefixV(); void WriteDowncount(int offset = 0); + bool ReplaceJalTo(u32 dest); // See CompileDelaySlotFlags for flags. void CompileDelaySlot(int flags, RegCacheState *state = NULL); diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index 1bf39963cf..dfeeedaf6f 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -171,15 +171,22 @@ void Clear() memset(m_pVRAM, 0, VRAM_SIZE); } + + Opcode Read_Instruction(u32 address) { Opcode inst = Opcode(Read_U32(address)); - if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) - { + if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) { JitBlockCache *bc = MIPSComp::jit->GetBlockCache(); int block_num = bc->GetBlockNumberFromEmuHackOp(inst, true); if (block_num >= 0) { - return bc->GetOriginalFirstOp(block_num); + inst = bc->GetOriginalFirstOp(block_num); + /* + u32 op; + if (GetReplacedOpAt(address, &op)) { + return Opcode(op); + }*/ + return inst; } else { return inst; } @@ -197,7 +204,18 @@ Opcode Read_Instruction(u32 address) Opcode Read_Opcode_JIT(u32 address) { - return Read_Instruction(address); + Opcode inst = Opcode(Read_U32(address)); + if (MIPS_IS_RUNBLOCK(inst.encoding) && MIPSComp::jit) { + JitBlockCache *bc = MIPSComp::jit->GetBlockCache(); + int block_num = bc->GetBlockNumberFromEmuHackOp(inst, true); + if (block_num >= 0) { + return bc->GetOriginalFirstOp(block_num); + } else { + return inst; + } + } else { + return inst; + } } // WARNING! No checks! diff --git a/Core/MemMap.h b/Core/MemMap.h index 60d2da3175..1007b1f747 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -133,17 +133,14 @@ struct Opcode { u32 encoding; }; -// used by JIT to read instructions +// used by JIT to read instructions. Does not resolve replacements. Opcode Read_Opcode_JIT(const u32 _Address); -// used by JIT. uses iCacheJIT. Reads in the "Locked cache" mode +// used by JIT. Reads in the "Locked cache" mode void Write_Opcode_JIT(const u32 _Address, const Opcode _Value); -// this is used by Debugger a lot. -// For now, just reads from memory! + +// Should be used by analyzers, disassemblers etc. Does resolve replacements. Opcode Read_Instruction(const u32 _Address); - -// For use by emulator - u8 Read_U8(const u32 _Address); u16 Read_U16(const u32 _Address); u32 Read_U32(const u32 _Address);