Experimental: Implement skid's MMU Demand Paging in JitIL.
This one needs a lot of testing, since I don't have any games that need it, except Rogue Leader (GSWP64) - and I didn't do more "testing" than watching the stormtroopers dance due to speed reasons (altho it seems twice as fast as JIT for me; it does spit out a lot more warnings too) btw, ITS OVER 6000!!111 git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6006 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
1d2f4283d1
commit
6ed3357066
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<void *>(&Memory::GenerateISIException_JIT), InstLoc);
|
||||
Jit->WriteExceptionExit();
|
||||
break;
|
||||
}
|
||||
case Int3: {
|
||||
Jit->INT3();
|
||||
break;
|
||||
|
|
|
@ -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] = 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,8 +512,23 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
|
|||
|
||||
if (!ops[i].skip)
|
||||
{
|
||||
JitILTables::CompileInstruction(ops[i]);
|
||||
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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue