From a97f66766dcba9bf6d8b2dd4cc6f8b4d30bcda77 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 23 Nov 2012 12:42:35 +0100 Subject: [PATCH] Add "slightly faster interpreter", optimizing the most common instrs. Not as helpful as I thought it would be on Android, really need a proper JIT. --- Core/Config.cpp | 8 +- Core/Config.h | 3 +- Core/CoreParameter.h | 2 +- Core/HLE/sceAudio.cpp | 2 +- Core/MIPS/MIPS.cpp | 87 ++------------- Core/MIPS/MIPSInt.cpp | 2 +- Core/MIPS/MIPSTables.cpp | 217 ++++++++++++++++++++++++++++++------ Core/MIPS/MIPSTables.h | 2 + Core/MemMap.h | 69 +++++++++--- Core/MemMapFunctions.cpp | 13 ++- Windows/EmuThread.cpp | 2 +- Windows/WndMainWindow.cpp | 17 ++- Windows/ppsspp.rc | 1 + Windows/resource.h | 5 +- android/jni/EmuScreen.cpp | 6 +- android/jni/MenuScreens.cpp | 6 + headless/Headless.cpp | 5 +- 17 files changed, 304 insertions(+), 143 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index db16c09b6c..aa8fb39afa 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -45,11 +45,13 @@ void CConfig::Load(const char *iniFileName) general->Get("FirstRun", &bFirstRun, true); general->Get("AutoLoadLast", &bAutoLoadLast, false); general->Get("AutoRun", &bAutoRun, false); - general->Get("Jit", &bJIT, false); general->Get("ConfirmOnQuit", &bConfirmOnQuit, false); general->Get("IgnoreBadMemAccess", &bIgnoreBadMemAccess, true); general->Get("CurrentDirectory", ¤tDirectory, ""); + IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU"); + cpu->Get("Core", &iCpuCore, 0); + IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics"); graphics->Get("ShowFPSCounter", &bShowFPSCounter, false); graphics->Get("DisplayFramebuffer", &bDisplayFramebuffer, false); @@ -74,11 +76,13 @@ void CConfig::Save() general->Set("FirstRun", bFirstRun); general->Set("AutoLoadLast", bAutoLoadLast); general->Set("AutoRun", bAutoRun); - general->Set("Jit", bJIT); general->Set("ConfirmOnQuit", bConfirmOnQuit); general->Set("IgnoreBadMemAccess", bIgnoreBadMemAccess); general->Set("CurrentDirectory", currentDirectory); + IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU"); + cpu->Set("Core", iCpuCore); + IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics"); graphics->Set("ShowFPSCounter", bShowFPSCounter); graphics->Set("DisplayFramebuffer", bDisplayFramebuffer); diff --git a/Core/Config.h b/Core/Config.h index 31c2c4c080..f7add3be19 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -39,7 +39,6 @@ public: bool bFirstRun; bool bAutoRun; bool bSpeedLimit; - bool bJIT; bool bConfirmOnQuit; bool bIgnoreBadMemAccess; bool bDisplayFramebuffer; @@ -49,7 +48,7 @@ public: bool bShowFPSCounter; bool bShowDebugStats; int iWindowZoom; // for Windows - + int iCpuCore; std::string currentDirectory; diff --git a/Core/CoreParameter.h b/Core/CoreParameter.h index bd9b219e62..29ec6d4ddb 100644 --- a/Core/CoreParameter.h +++ b/Core/CoreParameter.h @@ -22,6 +22,7 @@ enum CPUCore { CPU_INTERPRETER, + CPU_FASTINTERPRETER, // unsafe, a bit faster than INTERPRETER CPU_JIT, }; @@ -31,7 +32,6 @@ enum GPUCore { GPU_SOFTWARE, }; - struct CoreParameter { // 0 = Interpreter diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index 97b11cb618..ceff1d67b8 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -315,7 +315,7 @@ void sceAudioOutput2OutputBlocking() u32 sceAudioOutput2ChangeLength(u32 sampleCount) { - WARN_LOG(HLE,"sceAudioOutput2ChangeLength(%i)"); + WARN_LOG(HLE,"sceAudioOutput2ChangeLength(%i)", sampleCount); chans[0].sampleCount = sampleCount; return 0; } diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index a9ac49aec4..4ec868aed9 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -21,7 +21,6 @@ #include "MIPSDebugInterface.h" #include "MIPSVFPUUtils.h" #include "../System.h" -#include "../Debugger/Breakpoints.h" #include "../HLE/sceDisplay.h" #if defined(ANDROID) || defined(BLACKBERRY) @@ -42,6 +41,7 @@ MIPSDebugInterface *currentDebugMIPS = &debugr4k; MIPSState::MIPSState() { cpuType = CPUTYPE_ALLEGREX; + MIPSComp::jit = 0; } MIPSState::~MIPSState() @@ -109,85 +109,18 @@ void MIPSState::SingleStep() // returns 1 if reached ticks limit int MIPSState::RunLoopUntil(u64 globalTicks) { - if (PSP_CoreParameter().cpuCore == CPU_JIT) + switch (PSP_CoreParameter().cpuCore) { + case CPU_JIT: MIPSComp::jit->RunLoopUntil(globalTicks); - } - else - { + break; + + case CPU_FASTINTERPRETER: // For jit-less platforms. Crashier than INTERPRETER. + return MIPSInterpret_RunFastUntil(globalTicks); + + case CPU_INTERPRETER: // INFO_LOG(CPU, "Entering run loop for %i ticks, pc=%08x", (int)globalTicks, mipsr4k.pc); - while (coreState == CORE_RUNNING) - { - // NEVER stop in a delay slot! - while (CoreTiming::downcount >= 0 && coreState == CORE_RUNNING) - { - // int cycles = 0; - { - again: - u32 op = Memory::ReadUnchecked_U32(mipsr4k.pc); - //u32 op = Memory::Read_Opcode_JIT(mipsr4k.pc); - /* - // Choke on VFPU - u32 info = MIPSGetInfo(op); - if (info & IS_VFPU) - { - if (!Core_IsStepping() && !GetAsyncKeyState(VK_LSHIFT)) - { - Core_EnableStepping(true); - return; - } - }*/ - - //2: check for breakpoint (VERY SLOW) -#if defined(_DEBUG) - if (CBreakPoints::IsAddressBreakPoint(pc)) - { - Core_EnableStepping(true); - if (CBreakPoints::IsTempBreakPoint(pc)) - CBreakPoints::RemoveBreakPoint(pc); - break; - } -#endif - if (inDelaySlot) - { - MIPSInterpret(op); - if (inDelaySlot) - { - pc = nextPC; - inDelaySlot = false; - } - } - else - { - MIPSInterpret(op); - } - - /* - if (!Memory::IsValidAddress(pc)) - { - pc = pc; - } - if (r[MIPS_REG_RA] != 0 && !Memory::IsValidAddress(r[MIPS_REG_RA])) - { - // pc = pc; - }*/ - if (inDelaySlot) - { - CoreTiming::downcount -= 1; - goto again; - } - } - - CoreTiming::downcount -= 1; - if (CoreTiming::GetTicks() > globalTicks) - { - // DEBUG_LOG(CPU, "Hit the max ticks, bailing 1 : %llu, %llu", globalTicks, CoreTiming::GetTicks()); - return 1; - } - } - - CoreTiming::Advance(); - } + return MIPSInterpret_RunUntil(globalTicks); } return 1; } diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index 7a3ffb72ba..6fcc01e56b 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -66,7 +66,7 @@ float round_ieee_754(float d) { return i + 1.0f; } -void DelayBranchTo(u32 where) +static inline void DelayBranchTo(u32 where) { PC += 4; mipsr4k.nextPC = where; diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index ed54a09df7..9b117e023f 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -23,6 +23,8 @@ #include "MIPSInt.h" #include "MIPSIntVFPU.h" #include "MIPSCodeUtils.h" +#include "../../Core/CoreTiming.h" +#include "../Debugger/Breakpoints.h" #if defined(ANDROID) || defined(BLACKBERRY) #include "ARM/Jit.h" @@ -939,44 +941,197 @@ void MIPSInterpret(u32 op) //only for those rare ones #define _RS ((op>>21) & 0x1F) #define _RT ((op>>16) & 0x1F) #define _RD ((op>>11) & 0x1F) -#define R(i) (currentMIPS->r[i]) +#define R(i) (curMips->r[i]) -void MIPSInterpret_Fast(u32 op) + +int MIPSInterpret_RunUntil(u64 globalTicks) { - switch (op >> 29) + MIPSState *curMips = currentMIPS; + while (coreState == CORE_RUNNING) { - case 0x1: + // NEVER stop in a delay slot! + while (CoreTiming::downcount >= 0 && coreState == CORE_RUNNING) { - s32 simm = (s32)(s16)(op & 0xFFFF); - u32 uimm = (u32)(u16)(op & 0xFFFF); - u32 suimm = (u32)simm; - - int rt = _RT; - int rs = _RS; - - if (rt == 0) //destination register is zero register - return; //nop - - switch (op>>26) + // int cycles = 0; { - case 8: R(rt) = R(rs) + simm; break; //addi - case 9: R(rt) = R(rs) + simm; break; //addiu - case 10: R(rt) = (s32)R(rs) < simm; break; //slti - case 11: R(rt) = R(rs) < suimm; break; //sltiu - case 12: R(rt) = R(rs) & uimm; break; //andi - case 13: R(rt) = R(rs) | uimm; break; //ori - case 14: R(rt) = R(rs) ^ uimm; break; //xori - case 15: R(rt) = uimm << 16; break; //lui - default: - break; - } - currentMIPS->pc += 4; - } - break; + again: + u32 op = Memory::ReadUnchecked_U32(curMips->pc); + //u32 op = Memory::Read_Opcode_JIT(mipsr4k.pc); + /* + // Choke on VFPU + u32 info = MIPSGetInfo(op); + if (info & IS_VFPU) + { + if (!Core_IsStepping() && !GetAsyncKeyState(VK_LSHIFT)) + { + Core_EnableStepping(true); + return; + } + }*/ - default: - MIPSInterpret(op); + //2: check for breakpoint (VERY SLOW) +#if defined(_DEBUG) + if (CBreakPoints::IsAddressBreakPoint(curMips->pc)) + { + Core_EnableStepping(true); + if (CBreakPoints::IsTempBreakPoint(curMips->pc)) + CBreakPoints::RemoveBreakPoint(curMips->pc); + break; + } +#endif + + bool wasInDelaySlot = curMips->inDelaySlot; + + MIPSInterpret(op); + + if (curMips->inDelaySlot) + { + // The reason we have to check this is the delay slot hack in Int_Syscall. + if (wasInDelaySlot) + { + curMips->pc = curMips->nextPC; + curMips->inDelaySlot = false; + } + CoreTiming::downcount -= 1; + goto again; + } + } + + if (CoreTiming::GetTicks() > globalTicks) + { + // DEBUG_LOG(CPU, "Hit the max ticks, bailing 1 : %llu, %llu", globalTicks, CoreTiming::GetTicks()); + return 1; + } + } + + CoreTiming::Advance(); } + + return 1; +} + +static inline void DelayBranchTo(MIPSState *curMips, u32 where) +{ + curMips->pc += 4; + curMips->nextPC = where; + curMips->inDelaySlot = true; +} + +// Optimized interpreter loop that shortcuts the most common instructions. +// For slow platforms without JITs. +#define SIMM16 (s32)(s16)(op & 0xFFFF) +#define UIMM16 (u32)(u16)(op & 0xFFFF) +#define SUIMM16 (u32)(s32)(s16)(op & 0xFFFF) +int MIPSInterpret_RunFastUntil(u64 globalTicks) +{ + MIPSState *curMips = currentMIPS; + while (coreState == CORE_RUNNING) + { + while (CoreTiming::downcount >= 0 && coreState == CORE_RUNNING) // TODO: Try to get rid of the latter check + { + again: + bool wasInDelaySlot = curMips->inDelaySlot; + u32 op = Memory::ReadUnchecked_U32(curMips->pc); + switch (op >> 29) + { + case 0x0: + { + int imm = (s16)(op&0xFFFF) << 2; + int rs = _RS; + int rt = _RT; + u32 addr = curMips->pc + imm + 4; + switch (op >> 26) + { + case 4: if (R(rt) == R(rs)) DelayBranchTo(curMips, addr); else curMips->pc += 4; break; //beq + case 5: if (R(rt) != R(rs)) DelayBranchTo(curMips, addr); else curMips->pc += 4; break; //bne + case 6: if ((s32)R(rs) <= 0) DelayBranchTo(curMips, addr); else curMips->pc += 4; break; //blez + case 7: if ((s32)R(rs) > 0) DelayBranchTo(curMips, addr); else curMips->pc += 4; break; //bgtz + default: + goto interpret; + } + } + break; + + case 0x1: + { + int rt = _RT; + int rs = _RS; + switch (op >> 26) + { + case 8: R(rt) = R(rs) + SIMM16; break; //addi + case 9: R(rt) = R(rs) + SIMM16; break; //addiu + case 10: R(rt) = (s32)R(rs) < SIMM16; break; //slti + case 11: R(rt) = R(rs) < SUIMM16; break; //sltiu + case 12: R(rt) = R(rs) & UIMM16; break; //andi + case 13: R(rt) = R(rs) | UIMM16; break; //ori + case 14: R(rt) = R(rs) ^ UIMM16; break; //xori + case 15: R(rt) = UIMM16 << 16; break; //lui + default: + goto interpret; + } + currentMIPS->pc += 4; + } + break; + + case 0x4: + { + int rt = _RT; + int rs = _RS; + int imm = (s16)(op & 0xFFFF); + u32 addr = R(rs) + imm; + switch (op >> 26) + { + case 32: R(rt) = (u32)(s32)(s8) Memory::ReadUnchecked_U8(addr); break; //lb + case 33: R(rt) = (u32)(s32)(s16)Memory::ReadUnchecked_U16(addr); break; //lh + case 35: R(rt) = Memory::ReadUnchecked_U32(addr); break; //lw + case 36: R(rt) = Memory::ReadUnchecked_U8(addr); break; //lbu + case 37: R(rt) = Memory::ReadUnchecked_U16(addr); break; //lhu + default: + goto interpret; + } + currentMIPS->pc += 4; + } + break; + + case 0x5: + { + int rt = _RT; + int rs = _RS; + int imm = (s16)(op & 0xFFFF); + u32 addr = R(rs) + imm; + switch (op >> 26) + { + case 40: Memory::WriteUnchecked_U8(R(rt), addr); break; //sb + case 41: Memory::WriteUnchecked_U16(R(rt), addr); break; //sh + case 43: Memory::WriteUnchecked_U32(R(rt), addr); break; //sw + default: + goto interpret; + } + currentMIPS->pc += 4; + } + break; + + default: + interpret: + MIPSInterpret(op); + } + + if (curMips->inDelaySlot) + { + // The reason we have to check this is the delay slot hack in Int_Syscall. + if (wasInDelaySlot) + { + curMips->pc = curMips->nextPC; + curMips->inDelaySlot = false; + } + CoreTiming::downcount -= 1; + goto again; + } + } + + CoreTiming::Advance(); + } + return 1; } diff --git a/Core/MIPS/MIPSTables.h b/Core/MIPS/MIPSTables.h index f76575cb7b..7f71735236 100644 --- a/Core/MIPS/MIPSTables.h +++ b/Core/MIPS/MIPSTables.h @@ -56,6 +56,8 @@ void MIPSCompileOp(u32 op); void MIPSDisAsm(u32 op, u32 pc, char *out); u32 MIPSGetInfo(u32 op); void MIPSInterpret(u32 op); //only for those rare ones +int MIPSInterpret_RunFastUntil(u64 globalTicks); +int MIPSInterpret_RunUntil(u64 globalTicks); MIPSInterpretFunc MIPSGetInterpretFunc(u32 op); int MIPSGetInstructionCycleEstimate(u32 op); diff --git a/Core/MemMap.h b/Core/MemMap.h index 6afd44e118..7d34d61abf 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -34,7 +34,7 @@ // If safe memory is enabled and JIT is disabled, all memory access will go through the proper // memory access functions, and thus won't crash the emu when they go out of bounds. #if defined(_DEBUG) -#define SAFE_MEMORY +//#define SAFE_MEMORY #endif @@ -114,13 +114,6 @@ void DoState(PointerWrap &p); void Clear(); bool AreMemoryBreakpointsActivated(); -// ONLY for use by GUI -u8 ReadUnchecked_U8(const u32 _Address); -u16 ReadUnchecked_U16(const u32 _Address); - -void WriteUnchecked_U8(const u8 _Data, const u32 _Address); -void WriteUnchecked_U32(const u32 _Data, const u32 _Address); - inline u8* GetMainRAMPtr() {return m_pRAM;} // used by interpreter to read instructions, uses iCache @@ -146,19 +139,69 @@ u16 Read_U16(const u32 _Address); u32 Read_U32(const u32 _Address); u64 Read_U64(const u32 _Address); +#if (defined(ARM) || defined(_ARM)) && !defined(_M_ARM) +#define _M_ARM +#endif + #ifdef SAFE_MEMORY u32 ReadUnchecked_U32(const u32 _Address); +// ONLY for use by GUI and fast interpreter +u8 ReadUnchecked_U8(const u32 _Address); +u16 ReadUnchecked_U16(const u32 _Address); +void WriteUnchecked_U8(const u8 _Data, const u32 _Address); +void WriteUnchecked_U16(const u16 _Data, const u32 _Address); +void WriteUnchecked_U32(const u32 _Data, const u32 _Address); #else -inline u32 ReadUnchecked_U32(const u32 _Address) { + +inline u32 ReadUnchecked_U32(const u32 address) { #if defined(_M_IX86) || defined(_M_ARM32) - return (*(u32 *)(base + (_Address & MEMVIEW32_MASK))); // ReadUnchecked_U32(_Address); -#elif defined(_M_X64) - return (*(u32 *)(base + _Address)); + return (*(u32 *)(base + (address & MEMVIEW32_MASK))); #else - return (*(u32 *)(base + _Address)); + return (*(u32 *)(base + address)); #endif } + +inline u16 ReadUnchecked_U16(const u32 address) { +#if defined(_M_IX86) || defined(_M_ARM32) + return (*(u16 *)(base + (address & MEMVIEW32_MASK))); +#else + return (*(u16 *)(base + address)); +#endif +} + +inline u8 ReadUnchecked_U8(const u32 address) { +#if defined(_M_IX86) || defined(_M_ARM32) + return (*(u8 *)(base + (address & MEMVIEW32_MASK))); +#else + return (*(u8 *)(base + address)); +#endif +} + +inline void WriteUnchecked_U32(u32 data, u32 address) { +#if defined(_M_IX86) || defined(_M_ARM32) + (*(u32 *)(base + (address & MEMVIEW32_MASK))) = data; +#else + (*(u32 *)(base + address)) = data; +#endif +} + +inline void WriteUnchecked_U16(u16 data, u32 address) { +#if defined(_M_IX86) || defined(_M_ARM32) + (*(u16 *)(base + (address & MEMVIEW32_MASK))) = data; +#else + (*(u16 *)(base + address)) = data; +#endif +} + +inline void WriteUnchecked_U8(u8 data, u32 address) { +#if defined(_M_IX86) || defined(_M_ARM32) + (*(u8 *)(base + (address & MEMVIEW32_MASK))) = data; +#else + (*(u8 *)(base + address)) = data; +#endif +} + #endif diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index 561e7fb93e..e6bb8d1e48 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -275,6 +275,8 @@ void Write_U64(const u64 _Data, const u32 _Address) WriteToHardware(_Address, _Data); } +#ifdef SAFE_MEMORY + u8 ReadUnchecked_U8(const u32 _Address) { u8 _var = 0; @@ -289,8 +291,6 @@ u16 ReadUnchecked_U16(const u32 _Address) return _var; } -#ifdef SAFE_MEMORY - u32 ReadUnchecked_U32(const u32 _Address) { u32 _var = 0; @@ -298,16 +298,21 @@ u32 ReadUnchecked_U32(const u32 _Address) return _var; } -#endif - void WriteUnchecked_U8(const u8 _iValue, const u32 _Address) { WriteToHardware(_Address, _iValue); } +void WriteUnchecked_U8(const u16 _iValue, const u32 _Address) +{ + WriteToHardware(_Address, _iValue); +} + void WriteUnchecked_U32(const u32 _iValue, const u32 _Address) { WriteToHardware(_Address, _iValue); } +#endif + } // namespace Memory diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index 8b318a19cf..4815a26296 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -67,7 +67,7 @@ DWORD TheThread(LPVOID x) coreParameter.fileToStart = fileToStart; coreParameter.enableSound = true; coreParameter.gpuCore = GPU_GLES; - coreParameter.cpuCore = g_Config.bJIT ? CPU_JIT : CPU_INTERPRETER; + coreParameter.cpuCore = (CPUCore)g_Config.iCpuCore; coreParameter.enableDebugging = true; coreParameter.printfEmuLog = false; coreParameter.headLess = false; diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index 22f9c48795..bb9938205a 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -147,7 +147,7 @@ namespace MainWindow info.cyMax = 0; info.dwStyle = MNS_CHECKORBMP; info.fMask = MIM_STYLE; - for (int i=0; i 1 ? argv[1] : 0; @@ -100,6 +101,8 @@ int main(int argc, const char* argv[]) fullLog = true; else if (!strcmp(argv[i], "-j")) useJit = true; + else if (!strcmp(argv[i], "-f")) + fastInterpreter = true; else if (!strcmp(argv[i], "-c")) autoCompare = true; } @@ -129,7 +132,7 @@ int main(int argc, const char* argv[]) coreParameter.fileToStart = bootFilename; coreParameter.mountIso = mountIso ? mountIso : ""; coreParameter.startPaused = false; - coreParameter.cpuCore = useJit ? CPU_JIT : CPU_INTERPRETER; + coreParameter.cpuCore = useJit ? CPU_JIT : (fastInterpreter ? CPU_FASTINTERPRETER : CPU_INTERPRETER); coreParameter.gpuCore = GPU_NULL; coreParameter.enableSound = false; coreParameter.headLess = true;