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:
j4ck.fr0st 2010-07-30 17:51:05 +00:00
parent 1d2f4283d1
commit 6ed3357066
5 changed files with 104 additions and 13 deletions

View File

@ -163,6 +163,10 @@ enum Opcode {
ShortIdleLoop, // Idle loop seen in homebrew like wii mahjong, ShortIdleLoop, // Idle loop seen in homebrew like wii mahjong,
// just a branch // 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 // "Opcode" representing a register too far away to
// reference directly; this is a size optimization // reference directly; this is a size optimization
Tramp, Tramp,
@ -389,6 +393,15 @@ public:
InstLoc EmitSystemCall(InstLoc pc) { InstLoc EmitSystemCall(InstLoc pc) {
return FoldUOp(SystemCall, 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() { InstLoc EmitRFIExit() {
return FoldZeroOp(RFIExit, 0); return FoldZeroOp(RFIExit, 0);
} }

View File

@ -705,6 +705,9 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak
case RFIExit: case RFIExit:
case InterpreterBranch: case InterpreterBranch:
case ShortIdleLoop: case ShortIdleLoop:
case FPExceptionCheckStart:
case FPExceptionCheckEnd:
case ISIException:
case Int3: case Int3:
case Tramp: case Tramp:
// No liveness effects // No liveness effects
@ -1607,10 +1610,9 @@ static void DoWriteCode(IRBuilder* ibuild, JitIL* Jit, bool UseProfile, bool Mak
} }
case SystemCall: { case SystemCall: {
unsigned InstLoc = ibuild->GetImmValue(getOp1(I)); unsigned InstLoc = ibuild->GetImmValue(getOp1(I));
Jit->Cleanup();
Jit->OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_SYSCALL)); Jit->OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_SYSCALL));
Jit->MOV(32, M(&PC), Imm32(InstLoc + 4)); Jit->MOV(32, M(&PC), Imm32(InstLoc + 4));
Jit->JMP(((JitIL *)jit)->asm_routines.testExceptions, true); Jit->WriteExceptionExit();
break; break;
} }
case InterpreterBranch: { 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(EAX), Imm32(~mask));
Jit->AND(32, R(ECX), Imm32(mask)); Jit->AND(32, R(ECX), Imm32(mask));
Jit->OR(32, R(EAX), R(ECX)); Jit->OR(32, R(EAX), R(ECX));
// MSR &= 0xFFFDFFFF; //TODO: VERIFY // MSR &= 0xFFFBFFFF; // Mask used to clear the bit MSR[13]
Jit->AND(32, R(EAX), Imm32(0xFFFDFFFF)); Jit->AND(32, R(EAX), Imm32(0xFFFBFFFF));
Jit->MOV(32, M(&MSR), R(EAX)); Jit->MOV(32, M(&MSR), R(EAX));
// NPC = SRR0; // NPC = SRR0;
Jit->MOV(32, R(EAX), M(&SRR0)); Jit->MOV(32, R(EAX), M(&SRR0));
Jit->WriteRfiExitDestInEAX(); Jit->WriteRfiExitDestInEAX();
break; 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: { case Int3: {
Jit->INT3(); Jit->INT3();
break; break;

View File

@ -172,7 +172,11 @@ void JitIL::Init()
} }
else 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 #ifdef _M_X64
@ -185,6 +189,7 @@ void JitIL::Init()
jo.optimizeGatherPipe = true; jo.optimizeGatherPipe = true;
jo.fastInterrupts = false; jo.fastInterrupts = false;
jo.accurateSinglePrecision = false; jo.accurateSinglePrecision = false;
js.memcheck = Core::g_CoreStartupParameter.bMMU;
trampolines.Init(); trampolines.Init();
AllocCodeSpace(CODE_SIZE); AllocCodeSpace(CODE_SIZE);
@ -336,11 +341,10 @@ void JitIL::WriteRfiExitDestInEAX()
JMP(asm_routines.testExceptions, true); JMP(asm_routines.testExceptions, true);
} }
void JitIL::WriteExceptionExit(u32 exception) void JitIL::WriteExceptionExit()
{ {
Cleanup(); Cleanup();
OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(exception)); SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
MOV(32, M(&PC), Imm32(js.compilerPC + 4));
JMP(asm_routines.testExceptions, true); 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(); 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 // A broken block is a block that does not end in a branch
bool broken_block = false; bool broken_block = false;
@ -414,7 +421,21 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
if (em_address == 0) if (em_address == 0)
PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR); 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.isLastInstruction = false;
js.blockStart = em_address; js.blockStart = em_address;
js.fifoBytesThisBlock = 0; 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, //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. //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); 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; PPCAnalyst::CodeOp *ops = code_buf->codebuffer;
const u8 *start = AlignCode4(); //TODO: Test if this or AlignCode16 make a difference from GetCodePtr 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(); ibuild.Reset();
js.downcountAmount = js.st.numCycles; js.downcountAmount = 0;
if (!Core::g_CoreStartupParameter.bEnableDebugging) if (!Core::g_CoreStartupParameter.bEnableDebugging)
js.downcountAmount += PatchEngine::GetSpeedhackCycles(em_address); 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.compilerPC = ops[i].address;
js.op = &ops[i]; js.op = &ops[i];
js.instructionNumber = i; js.instructionNumber = i;
const GekkoOPInfo *opinfo = GetOpInfo(ops[i].inst);
js.downcountAmount += (opinfo->numCyclesMinusOne + 1);
if (i == (int)size - 1) if (i == (int)size - 1)
{ {
js.isLastInstruction = true; js.isLastInstruction = true;
@ -484,8 +512,23 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
if (!ops[i].skip) 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 // Perform actual code generation

View File

@ -112,7 +112,7 @@ public:
void WriteExit(u32 destination, int exit_num); void WriteExit(u32 destination, int exit_num);
void WriteExitDestInEAX(int exit_num); void WriteExitDestInEAX(int exit_num);
void WriteExceptionExit(u32 exception); void WriteExceptionExit();
void WriteRfiExitDestInEAX(); void WriteRfiExitDestInEAX();
void WriteCallInterpreter(UGeckoInstruction _inst); void WriteCallInterpreter(UGeckoInstruction _inst);
void Cleanup(); void Cleanup();

View File

@ -125,6 +125,7 @@ void JitIL::cmpXX(UGeckoInstruction inst)
rhs = ibuild.EmitIntConst(inst.SIMM_16); rhs = ibuild.EmitIntConst(inst.SIMM_16);
res = ibuild.EmitICmpCRSigned(lhs, rhs); res = ibuild.EmitICmpCRSigned(lhs, rhs);
} }
js.downcountAmount++; //TODO: should this be somewhere else?
ibuild.EmitStoreCR(res, inst.CRFD); ibuild.EmitStoreCR(res, inst.CRFD);
} }