diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h index 9a8ea53c89..970e2cd52f 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h @@ -163,6 +163,10 @@ enum Opcode { ShortIdleLoop, // Idle loop seen in homebrew like wii mahjong, // just a branch + // used for MMU, at least until someone + // has a better idea of integrating it + FPExceptionCheckStart, FPExceptionCheckEnd, + ISIException, // "Opcode" representing a register too far away to // reference directly; this is a size optimization Tramp, @@ -389,6 +393,15 @@ public: InstLoc EmitSystemCall(InstLoc pc) { return FoldUOp(SystemCall, pc); } + InstLoc EmitFPExceptionCheckStart(InstLoc pc) { + return EmitUOp(FPExceptionCheckStart, pc); + } + InstLoc EmitFPExceptionCheckEnd(InstLoc pc) { + return EmitUOp(FPExceptionCheckEnd, pc); + } + InstLoc EmitISIException(InstLoc dest) { + return EmitUOp(ISIException, dest); + } InstLoc EmitRFIExit() { return FoldZeroOp(RFIExit, 0); } diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp index 2f1f9b726c..c33feb2614 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/IR_X86.cpp @@ -705,6 +705,9 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak case RFIExit: case InterpreterBranch: case ShortIdleLoop: + case FPExceptionCheckStart: + case FPExceptionCheckEnd: + case ISIException: case Int3: case Tramp: // No liveness effects @@ -1607,10 +1610,9 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak } case SystemCall: { unsigned InstLoc = ibuild->GetImmValue(getOp1(I)); - Jit->Cleanup(); Jit->OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_SYSCALL)); Jit->MOV(32, M(&PC), Imm32(InstLoc + 4)); - Jit->JMP(((JitIL *)jit)->asm_routines.testExceptions, true); + Jit->WriteExceptionExit(); break; } case InterpreterBranch: { @@ -1627,14 +1629,46 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak Jit->AND(32, R(EAX), Imm32(~mask)); Jit->AND(32, R(ECX), Imm32(mask)); Jit->OR(32, R(EAX), R(ECX)); - // MSR &= 0xFFFDFFFF; //TODO: VERIFY - Jit->AND(32, R(EAX), Imm32(0xFFFDFFFF)); + // MSR &= 0xFFFBFFFF; // Mask used to clear the bit MSR[13] + Jit->AND(32, R(EAX), Imm32(0xFFFBFFFF)); Jit->MOV(32, M(&MSR), R(EAX)); // NPC = SRR0; Jit->MOV(32, R(EAX), M(&SRR0)); Jit->WriteRfiExitDestInEAX(); break; } + case FPExceptionCheckStart: { + unsigned InstLoc = ibuild->GetImmValue(getOp1(I)); + //This instruction uses FPU - needs to add FP exception bailout + Jit->TEST(32, M(&PowerPC::ppcState.msr), Imm32(1 << 13)); // Test FP enabled bit + FixupBranch b1 = Jit->J_CC(CC_NZ); + + // If a FPU exception occurs, the exception handler will read + // from PC. Update PC with the latest value in case that happens. + Jit->MOV(32, M(&PC), Imm32(InstLoc)); + Jit->SUB(32, M(&CoreTiming::downcount), Jit->js.downcountAmount > 127 ? Imm32(Jit->js.downcountAmount) : Imm8(Jit->js.downcountAmount)); + Jit->JMP(Jit->asm_routines.fpException, true); + Jit->SetJumpTarget(b1); + break; + } + case FPExceptionCheckEnd: { + unsigned InstLoc = ibuild->GetImmValue(getOp1(I)); + Jit->TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_DSI)); + FixupBranch noMemException = Jit->J_CC(CC_Z); + + // If a memory exception occurs, the exception handler will read + // from PC. Update PC with the latest value in case that happens. + Jit->MOV(32, M(&PC), Imm32(InstLoc)); + Jit->WriteExceptionExit(); + Jit->SetJumpTarget(noMemException); + break; + } + case ISIException: { + unsigned InstLoc = ibuild->GetImmValue(getOp1(I)); + Jit->ABI_CallFunctionC(reinterpret_cast(&Memory::GenerateISIException_JIT), InstLoc); + Jit->WriteExceptionExit(); + break; + } case Int3: { Jit->INT3(); break; diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp index c285efc9c3..f9d7679f23 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.cpp @@ -172,7 +172,11 @@ void JitIL::Init() } else { - jo.enableBlocklink = true; // Speed boost, but not 100% safe + if (!Core::g_CoreStartupParameter.bJITBlockLinking) + jo.enableBlocklink = false; + else + // Speed boost, but not 100% safe + jo.enableBlocklink = !Core::g_CoreStartupParameter.bMMU; } #ifdef _M_X64 @@ -185,6 +189,7 @@ void JitIL::Init() jo.optimizeGatherPipe = true; jo.fastInterrupts = false; jo.accurateSinglePrecision = false; + js.memcheck = Core::g_CoreStartupParameter.bMMU; trampolines.Init(); AllocCodeSpace(CODE_SIZE); @@ -336,11 +341,10 @@ void JitIL::WriteRfiExitDestInEAX() JMP(asm_routines.testExceptions, true); } -void JitIL::WriteExceptionExit(u32 exception) +void JitIL::WriteExceptionExit() { Cleanup(); - OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(exception)); - MOV(32, M(&PC), Imm32(js.compilerPC + 4)); + SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); JMP(asm_routines.testExceptions, true); } @@ -401,6 +405,9 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc { int blockSize = code_buf->GetSize(); + // Memory exception on instruction fetch + bool memory_exception = false; + // A broken block is a block that does not end in a branch bool broken_block = false; @@ -414,7 +421,21 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc if (em_address == 0) PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR); - int size; + if (Core::g_CoreStartupParameter.bMMU && + (em_address >> 28) != 0x0 && + (em_address >> 28) != 0x8 && + (em_address >> 28) != 0x9 && + (em_address >> 28) != 0xC && + (em_address >> 28) != 0xD) + { + if (!Memory::TranslateAddress(em_address, Memory::FLAG_OPCODE)) + { + // Memory exception occurred during instruction fetch + memory_exception = true; + } + } + + int size = 0; js.isLastInstruction = false; js.blockStart = em_address; js.fifoBytesThisBlock = 0; @@ -423,7 +444,12 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc //Analyze the block, collect all instructions it is made of (including inlining, //if that is enabled), reorder instructions for optimal performance, and join joinable instructions. - b->exitAddress[0] = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, broken_block, code_buf, blockSize); + b->exitAddress[0] = em_address; + if (!memory_exception) + { + // If there is a memory exception inside a block (broken_block==true), compile up to that instruction. + b->exitAddress[0] = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, broken_block, code_buf, blockSize); + } PPCAnalyst::CodeOp *ops = code_buf->codebuffer; const u8 *start = AlignCode4(); //TODO: Test if this or AlignCode16 make a difference from GetCodePtr @@ -459,8 +485,7 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc ibuild.Reset(); - js.downcountAmount = js.st.numCycles; - + js.downcountAmount = 0; if (!Core::g_CoreStartupParameter.bEnableDebugging) js.downcountAmount += PatchEngine::GetSpeedhackCycles(em_address); @@ -470,6 +495,9 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc js.compilerPC = ops[i].address; js.op = &ops[i]; js.instructionNumber = i; + const GekkoOPInfo *opinfo = GetOpInfo(ops[i].inst); + js.downcountAmount += (opinfo->numCyclesMinusOne + 1); + if (i == (int)size - 1) { js.isLastInstruction = true; @@ -484,10 +512,25 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc if (!ops[i].skip) { + if (js.memcheck && (opinfo->flags & FL_USE_FPU)) + { + ibuild.EmitFPExceptionCheckStart(ibuild.EmitIntConst(ops[i].address)); + } + JitILTables::CompileInstruction(ops[i]); + + if (js.memcheck && (opinfo->flags & FL_LOADSTORE)) + { + ibuild.EmitFPExceptionCheckEnd(ibuild.EmitIntConst(ops[i].address)); + } } } + if (memory_exception) + { + ibuild.EmitISIException(ibuild.EmitIntConst(em_address)); + } + // Perform actual code generation WriteCode(); diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.h b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.h index f493bc3214..732978bd93 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.h +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL.h @@ -112,7 +112,7 @@ public: void WriteExit(u32 destination, int exit_num); void WriteExitDestInEAX(int exit_num); - void WriteExceptionExit(u32 exception); + void WriteExceptionExit(); void WriteRfiExitDestInEAX(); void WriteCallInterpreter(UGeckoInstruction _inst); void Cleanup(); diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_Integer.cpp b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_Integer.cpp index 10b05703ec..3623b06551 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_Integer.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64IL/JitIL_Integer.cpp @@ -125,6 +125,7 @@ void JitIL::cmpXX(UGeckoInstruction inst) rhs = ibuild.EmitIntConst(inst.SIMM_16); res = ibuild.EmitICmpCRSigned(lhs, rhs); } + js.downcountAmount++; //TODO: should this be somewhere else? ibuild.EmitStoreCR(res, inst.CRFD); }