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,
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue