Merge pull request #22087 from hrydgard/interpreter-stuff

Interpreter: Use a generated switch-tree dispatcher
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2026-08-12 14:01:32 +02:00
commit ba4c3d93f3
29 files changed
+1193 -111

No files matched your search

+20 -6
View File
@@ -180,12 +180,26 @@ small examples to copy from). A module is a `const HLEFunction <name>[]` table o
the `// add new modules here.` comment near the end of that function) - not inserted alphabetically/logically among
the existing `Register_*()` calls. Module registration order affects numeric IDs used in savestates, so inserting a
new module earlier in that list would break save-state compatibility for saves made with older builds.
- Remember to add any new `.cpp`/`.c` file to **five** places: `Core/CMakeLists.txt`, `Core/Core.vcxproj`,
`Core/Core.vcxproj.filters`, `android/jni/Android.mk`, and `libretro/Makefile.common`. New `.h` files only need the
first three (`Android.mk`/`Makefile.common` are plain compiled-source lists so headers
don't go in them). Only the CMakeLists.txt change can be verified from a Linux/Mac build - the rest can't be
build-tested here, so double check them by hand against how an existing neighboring file (e.g. `sceVaudio.cpp`) is
listed in each. Note: New files in the unittest project have to be updated in the unittest part in android/jni/Android.mk.
- Remember to add any new `.cpp`/`.c` file to **seven** places: `Core/CMakeLists.txt`, `Core/Core.vcxproj`,
`Core/Core.vcxproj.filters`, `UWP/CoreUWP/CoreUWP.vcxproj`, `UWP/CoreUWP/CoreUWP.vcxproj.filters`,
`android/jni/Android.mk`, and `libretro/Makefile.common`. New `.h` files need the first five (everything except
`Android.mk`/`Makefile.common`, which are plain compiled-source lists so headers don't go in them). Double check
each by hand against how an existing neighboring file (e.g. `sceVaudio.cpp`) is listed. Forgetting the UWP entries
is easy to miss - the CMake and MSBuild (`Core.vcxproj`) builds both succeed silently, and it only surfaces as a
UWP-only build failure (this has happened for real: `Core/MIPS/InterpreterDispatch.cpp` landed without its UWP
entries, and the omission wasn't caught until someone actually built the UWP project). Note: New files in the
unittest project have to be updated in the unittest part in android/jni/Android.mk.
Both the CMakeLists.txt change (via a Linux/Mac build) and the `Core.vcxproj`/UWP changes (via MSBuild on Windows)
can actually be build-tested, not just eyeballed - see "Build and Validation" above for the main Windows solution,
and for UWP specifically:
```powershell
$installPath = & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath
$msbuild = "$installPath\MSBuild\Current\Bin\MSBuild.exe"
& $msbuild "UWP\PPSSPP_UWP.sln" /t:CoreUWP /p:Configuration=Debug /p:Platform=x64 /m
```
(only `android/jni/Android.mk` and `libretro/Makefile.common` genuinely can't be build-tested here - see their
respective sections above for what verification is possible for those.)
## WebSocket debugger
+6 -4
View File
@@ -618,10 +618,12 @@ add_library(Core STATIC
MIPS/MIPSDis.h
MIPS/MIPSDisVFPU.cpp
MIPS/MIPSDisVFPU.h
MIPS/MIPSInt.cpp
MIPS/MIPSInt.h
MIPS/MIPSIntVFPU.cpp
MIPS/MIPSIntVFPU.h
MIPS/Interpreter.cpp
MIPS/Interpreter.h
MIPS/InterpreterDispatch.cpp
MIPS/InterpreterDispatch.h
MIPS/InterpreterVFPU.cpp
MIPS/InterpreterVFPU.h
MIPS/MIPSStackWalk.cpp
MIPS/MIPSStackWalk.h
MIPS/MIPSTables.cpp
+8 -3
View File
@@ -1,3 +1,4 @@
#include <algorithm>
#include <cstdlib>
#include "Core/Config.h"
@@ -188,6 +189,7 @@ static const CommandLineParam g_autoParams[] = {
{POFF(maxScreenshotError), CmdParamType::Double, "max-mse", '\0', "Maximum allowed MSE error for screenshot comparison", CmdLineMode::Headless},
{POFF(mountIso), CmdParamType::String, "mount", 'm', "Mount ISO/CSO on umd1:", CmdLineMode::Headless},
{POFF(odsLog), CmdParamType::Bool, "odslog", 'o', "Also log through OutputDebugString (Windows)", CmdLineMode::Headless},
{POFF(generateInterpreterDispatch), CmdParamType::Bool, "generate-interpreter-dispatch", '\0', "Generate C++ interpreter dispatch code (ExecInstruction) to stdout and exit", CmdLineMode::Headless},
{POFF(resolutionScale), CmdParamType::Int, "resolution-scale", '\0', "Set the resolution scale factor"},
{POFF(debuggerPort), CmdParamType::Int, "debugger", '\0', "Enable the WebSocket debugger on this port (0 = pick automatically); see docs/WebSocketDebugger.md"},
{POFF(bootVSH), CmdParamType::Bool, "vsh", '\0', "Boot the VSH (requires files dumped from a PSP in the flash0 directory)"},
@@ -257,12 +259,15 @@ int CommandLineOptions::PrintUsage(const char *progname, const char *situationTe
// Skip mode-irrelevant parameters in help.
continue;
}
char key[25]{};
char key[64]{};
snprintf(key, ARRAY_SIZE(key), " --%s%s%s", param.longName,
param.shortName ? ", -" : "",
param.shortName ? std::string(1, param.shortName).c_str() : "");
// Fill key with spacing.
for (size_t j = strlen(key); j < ARRAY_SIZE(key) - 1; ++j) {
// Fill key with spacing, keeping at least one space before the doc string
// even if the name itself ran past the normal column width.
size_t padTo = std::max(strlen(key) + 1, (size_t)24);
padTo = std::min(padTo, ARRAY_SIZE(key) - 1);
for (size_t j = strlen(key); j < padTo; ++j) {
key[j] = ' ';
}
if (param.type == CmdParamType::Enum) {
+2
View File
@@ -107,6 +107,8 @@ struct CommandLineOptions {
std::optional<double> maxScreenshotError;
// Headless: test names to skip. May be specified more than once.
std::vector<std::string> ignoredTests;
// Headless: generate C++ interpreter dispatch code to stdout and exit.
std::optional<bool> generateInterpreterDispatch;
// SDL only.
std::optional<int> xres;
+6 -4
View File
@@ -844,8 +844,9 @@
<ClCompile Include="MIPS\MIPSDebugInterface.cpp" />
<ClCompile Include="MIPS\MIPSDis.cpp" />
<ClCompile Include="MIPS\MIPSDisVFPU.cpp" />
<ClCompile Include="MIPS\MIPSInt.cpp" />
<ClCompile Include="MIPS\MIPSIntVFPU.cpp" />
<ClCompile Include="MIPS\Interpreter.cpp" />
<ClCompile Include="MIPS\InterpreterDispatch.cpp" />
<ClCompile Include="MIPS\InterpreterVFPU.cpp" />
<ClCompile Include="MIPS\MIPSTables.cpp" />
<ClCompile Include="MIPS\MIPSVFPUUtils.cpp" />
<ClCompile Include="MIPS\MIPSTracer.cpp" />
@@ -1229,8 +1230,9 @@
<ClInclude Include="MIPS\MIPSDebugInterface.h" />
<ClInclude Include="MIPS\MIPSDis.h" />
<ClInclude Include="MIPS\MIPSDisVFPU.h" />
<ClInclude Include="MIPS\MIPSInt.h" />
<ClInclude Include="MIPS\MIPSIntVFPU.h" />
<ClInclude Include="MIPS\Interpreter.h" />
<ClInclude Include="MIPS\InterpreterDispatch.h" />
<ClInclude Include="MIPS\InterpreterVFPU.h" />
<ClInclude Include="MIPS\MIPSTables.h" />
<ClInclude Include="MIPS\MIPSVFPUUtils.h" />
<ClInclude Include="MIPS\MIPSTracer.h" />
+10 -4
View File
@@ -111,10 +111,13 @@
<ClCompile Include="MIPS\MIPSDisVFPU.cpp">
<Filter>MIPS</Filter>
</ClCompile>
<ClCompile Include="MIPS\MIPSInt.cpp">
<ClCompile Include="MIPS\Interpreter.cpp">
<Filter>MIPS</Filter>
</ClCompile>
<ClCompile Include="MIPS\MIPSIntVFPU.cpp">
<ClCompile Include="MIPS\InterpreterDispatch.cpp">
<Filter>MIPS</Filter>
</ClCompile>
<ClCompile Include="MIPS\InterpreterVFPU.cpp">
<Filter>MIPS</Filter>
</ClCompile>
<ClCompile Include="MIPS\MIPSTables.cpp">
@@ -1416,10 +1419,13 @@
<ClInclude Include="MIPS\MIPSDisVFPU.h">
<Filter>MIPS</Filter>
</ClInclude>
<ClInclude Include="MIPS\MIPSInt.h">
<ClInclude Include="MIPS\Interpreter.h">
<Filter>MIPS</Filter>
</ClInclude>
<ClInclude Include="MIPS\MIPSIntVFPU.h">
<ClInclude Include="MIPS\InterpreterDispatch.h">
<Filter>MIPS</Filter>
</ClInclude>
<ClInclude Include="MIPS\InterpreterVFPU.h">
<Filter>MIPS</Filter>
</ClInclude>
<ClInclude Include="MIPS\MIPSTables.h">
+1 -1
View File
@@ -28,7 +28,7 @@
#include "Core/HLE/ErrorCodes.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "Core/FileSystems/FileSystem.h"
+1 -1
View File
@@ -35,7 +35,7 @@
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/MIPS/ARM/ArmRegCache.h"
+1 -1
View File
@@ -37,7 +37,7 @@
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/HLE/ReplaceTables.h"
#include "Core/MIPS/ARM64/Arm64RegCache.h"
+1 -1
View File
@@ -1216,7 +1216,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) {
case IROp::Interpret: // SLOW fallback. Can be made faster. Ideally should be removed but may be useful for debugging.
{
MIPSOpcode op(inst->constant);
MIPSInterpret(op);
MIPSInterpret(mips, op);
break;
}
+1 -1
View File
@@ -33,7 +33,7 @@
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/IR/IRRegCache.h"
#include "Core/MIPS/IR/IRInterpreter.h"
+1 -1
View File
@@ -149,7 +149,7 @@ void IRNativeBackend::DoMIPSInst(uint32_t value) {
if constexpr (enableDebugStats)
debugSeenNotCompiled[MIPSGetName(op)]++;
MIPSInterpret(op);
MIPSInterpret(currentMIPS, op);
}
// This is called from IR->JIT implementation to fall back to the IR interpreter for missing ops.
@@ -28,7 +28,7 @@
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/Reporting.h"
#include "Core/HLE/HLE.h"
@@ -75,20 +75,20 @@ static inline void SkipLikely() {
}
}
int MIPS_SingleStep() {
if (!Memory::IsValid4AlignedAddress(mipsr4k.pc)) {
Core_ExecException(mipsr4k.pc, mipsr4k.pc, ExecExceptionType::JUMP);
int MIPS_SingleStep(MIPSState *mips) {
if (!Memory::IsValid4AlignedAddress(mips->pc)) {
Core_ExecException(mips->pc, mips-> pc, ExecExceptionType::JUMP);
return 0;
}
MIPSOpcode op = Memory::Read_Opcode_JIT(mipsr4k.pc); // now unchecked
if (mipsr4k.inDelaySlot) {
MIPSInterpret(op);
if (mipsr4k.inDelaySlot) {
mipsr4k.pc = mipsr4k.nextPC;
mipsr4k.inDelaySlot = false;
MIPSOpcode op = Memory::Read_Opcode_JIT(mips->pc); // now unchecked
if (mips->inDelaySlot) {
MIPSInterpret(mips, op);
if (mips->inDelaySlot) {
mips->pc = mips->nextPC;
mips->inDelaySlot = false;
}
} else {
MIPSInterpret(op);
MIPSInterpret(mips, op);
}
return 1;
}
@@ -1122,7 +1122,7 @@ namespace MIPSInt
if (entry->flags & (REPFLAG_HOOKENTER | REPFLAG_HOOKEXIT)) {
// Interpret the original instruction under the hook.
MIPSInterpret(Memory::Read_Instruction(PC, true));
MIPSInterpret(currentMIPS, Memory::Read_Instruction(PC, true));
} else if (cycles < 0) {
// Leave PC unchanged, call the replacement again (assumes args are modified.)
currentMIPS->downcount += cycles;
@@ -1135,7 +1135,7 @@ namespace MIPSInt
ERROR_LOG(Log::CPU, "Bad replacement function index %i", index);
}
// Interpret the original instruction under it.
MIPSInterpret(Memory::Read_Instruction(PC, true));
MIPSInterpret(currentMIPS, Memory::Read_Instruction(PC, true));
}
}
}
@@ -20,7 +20,7 @@
#include "Common/CommonTypes.h"
#include "Core/MIPS/MIPS.h"
int MIPS_SingleStep();
int MIPS_SingleStep(MIPSState *mips);
namespace MIPSInt
{
+736
View File
@@ -0,0 +1,736 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
// AUTO-GENERATED by `PPSSPPHeadless --generate-interpreter-dispatch` from the
// tables in MIPSTables.cpp - do not hand-edit, regenerate instead.
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/InterpreterVFPU.h"
// Returns the cycle count consumed, or -1 if op isn't a recognized instruction -
// callers must fall back to MIPSInterpret() themselves in that case.
int ExecInstruction(MIPSOpcode op) {
switch ((op.encoding >> 26) & 0x3f) {
case 0:
{
switch ((op.encoding >> 0) & 0x3f) {
// sll, srl, sra, sllv, srlv, srav
case 0:
case 2:
case 3:
case 4:
case 6:
case 7:
MIPSInt::Int_ShiftType(op);
return 1;
// jr, jalr
case 8:
case 9:
MIPSInt::Int_JumpRegType(op);
return 1;
// movz, movn, add, addu, sub, subu, and, or, xor, nor, slt, sltu, max, min
case 10:
case 11:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
case 42:
case 43:
case 44:
case 45:
MIPSInt::Int_RType3(op);
return 1;
// syscall
case 12:
MIPSInt::Int_Syscall(op);
return 1;
// break
case 13:
MIPSInt::Int_Break(op);
return 1;
// sync
case 15:
MIPSInt::Int_Sync(op);
return 1;
// mfhi, mthi, mflo, mtlo, mult, multu, div, divu, madd, maddu, msub, msubu
case 16:
case 17:
case 18:
case 19:
case 24:
case 25:
case 26:
case 27:
case 28:
case 29:
case 46:
case 47:
MIPSInt::Int_MulDivType(op);
return 1;
// clz, clo
case 22:
case 23:
MIPSInt::Int_RType2(op);
return 1;
default:
return -1;
}
}
case 1:
{
switch ((op.encoding >> 16) & 0x1f) {
// bltz, bgez, bltzl, bgezl, bltzal, bgezal, bltzall, bgezall
case 0:
case 1:
case 2:
case 3:
case 16:
case 17:
case 18:
case 19:
MIPSInt::Int_RelBranchRI(op);
return 1;
default:
return -1;
}
}
// j, jal
case 2:
case 3:
MIPSInt::Int_JumpType(op);
return 1;
// beq, bne, blez, bgtz, beql, bnel, blezl, bgtzl
case 4:
case 5:
case 6:
case 7:
case 20:
case 21:
case 22:
case 23:
MIPSInt::Int_RelBranch(op);
return 1;
// addi, addiu, slti, sltiu, andi, ori, xori, lui
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
MIPSInt::Int_IType(op);
return 1;
case 16:
{
switch ((op.encoding >> 21) & 0x1f) {
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
{
return -1;
}
default:
return -1;
}
}
case 17:
{
switch ((op.encoding >> 21) & 0x1f) {
// mfc1, cfc1, mtc1, ctc1
case 0:
case 2:
case 4:
case 6:
MIPSInt::Int_mxc1(op);
return 1;
case 8:
{
switch ((op.encoding >> 16) & 0x1f) {
// bc1f, bc1t, bc1fl, bc1tl
case 0:
case 1:
case 2:
case 3:
MIPSInt::Int_FPUBranch(op);
return 1;
default:
return -1;
}
}
case 16:
{
switch ((op.encoding >> 0) & 0x3f) {
// add.s, sub.s, mul.s
case 0:
case 1:
case 2:
MIPSInt::Int_FPU3op(op);
return 1;
// div.s
case 3:
MIPSInt::Int_FPU3op(op);
return 29;
// sqrt.s, abs.s, mov.s, neg.s, round.w.s, trunc.w.s, ceil.w.s, floor.w.s, cvt.w.s
case 4:
case 5:
case 6:
case 7:
case 12:
case 13:
case 14:
case 15:
case 36:
MIPSInt::Int_FPU2op(op);
return 1;
// dis.int
case 38:
MIPSInt::Int_Interrupt(op);
return 1;
// c.f.s, c.un.s, c.eq.s, c.ueq.s, c.olt.s, c.ult.s, c.ole.s, c.ule.s, c.sf.s, c.ngle.s, c.seq.s, c.ngl.s, c.lt.s, c.nge.s, c.le.s, c.ngt.s
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
case 58:
case 59:
case 60:
case 61:
case 62:
case 63:
MIPSInt::Int_FPUComp(op);
return 1;
default:
return -1;
}
}
case 20:
{
switch ((op.encoding >> 0) & 0x3f) {
// cvt.s.w
case 32:
MIPSInt::Int_FPU2op(op);
return 1;
default:
return -1;
}
}
default:
return -1;
}
}
case 18:
{
switch ((op.encoding >> 21) & 0x1f) {
// mfv, mtv
case 3:
case 7:
MIPSInt::Int_Mftv(op);
return 2;
case 8:
{
switch ((op.encoding >> 16) & 0x3) {
// bvf, bvt, bvfl, bvtl
case 0:
case 1:
case 2:
case 3:
MIPSInt::Int_VBranch(op);
return 2;
default:
return -1;
}
}
default:
return -1;
}
}
case 24:
{
switch ((op.encoding >> 23) & 0x7) {
// vadd, vsub, vdiv
case 0:
case 1:
case 7:
MIPSInt::Int_VecDo3(op);
return 2;
// vsbn
case 2:
MIPSInt::Int_Vsbn(op);
return 2;
default:
return -1;
}
}
case 25:
{
switch ((op.encoding >> 23) & 0x7) {
// vmul
case 0:
MIPSInt::Int_VecDo3(op);
return 2;
// vdot
case 1:
MIPSInt::Int_VDot(op);
return 2;
// vscl
case 2:
MIPSInt::Int_VScl(op);
return 2;
// vhdp
case 4:
MIPSInt::Int_VHdp(op);
return 2;
// vcrs
case 5:
MIPSInt::Int_Vcrs(op);
return 2;
// vdet
case 6:
MIPSInt::Int_Vdet(op);
return 2;
default:
return -1;
}
}
case 26:
{
switch ((op.encoding >> 24) & 0x3) {
// RUNBLOCK
case 0:
MIPSInt::Int_Emuhack(op);
return 2;
// RetKrnl, CallRepl
case 1:
case 2:
MIPSInt::Int_Emuhack(op);
return 1;
default:
return -1;
}
}
case 27:
{
switch ((op.encoding >> 23) & 0x7) {
// vcmp
case 0:
MIPSInt::Int_Vcmp(op);
return 2;
// vmin, vmax
case 2:
case 3:
MIPSInt::Int_Vminmax(op);
return 2;
// vscmp
case 5:
MIPSInt::Int_Vscmp(op);
return 2;
// vsge
case 6:
MIPSInt::Int_Vsge(op);
return 2;
// vslt
case 7:
MIPSInt::Int_Vslt(op);
return 2;
default:
return -1;
}
}
case 28:
{
switch ((op.encoding >> 0) & 0x3f) {
// mfic, mtic
case 36:
case 38:
MIPSInt::Int_Special2(op);
return 1;
default:
return -1;
}
}
case 31:
{
switch ((op.encoding >> 0) & 0x3f) {
// ext, ins
case 0:
case 4:
MIPSInt::Int_Special3(op);
return 1;
case 24:
case 32:
{
switch ((op.encoding >> 6) & 0x1f) {
// wsbh, wsbw
case 2:
case 3:
MIPSInt::Int_Allegrex2(op);
return 1;
// seb, bitrev, seh
case 16:
case 20:
case 24:
MIPSInt::Int_Allegrex(op);
return 1;
default:
return -1;
}
}
default:
return -1;
}
}
// lb, lh, lwl, lw, lbu, lhu, lwr, sb, sh, swl, sw, swr
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 40:
case 41:
case 42:
case 43:
case 46:
MIPSInt::Int_ITypeMem(op);
return 1;
// cache
case 47:
MIPSInt::Int_Cache(op);
return 1;
// ll, sc
case 48:
case 56:
MIPSInt::Int_StoreSync(op);
return 1;
// lwc1, swc1
case 49:
case 57:
MIPSInt::Int_FPULS(op);
return 1;
// lv.s, sv.s
case 50:
case 58:
MIPSInt::Int_SV(op);
return 2;
case 52:
{
switch ((op.encoding >> 21) & 0x1f) {
case 0:
{
switch ((op.encoding >> 16) & 0x1f) {
// vmov, vabs, vneg, vsat0, vsat1, vrcp, vrsq, vsin, vcos, vexp2, vlog2, vsqrt, vasin, vnrcp, vnsin, vrexp2
case 0:
case 1:
case 2:
case 4:
case 5:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
case 26:
case 28:
MIPSInt::Int_VV2Op(op);
return 2;
// vidt
case 3:
MIPSInt::Int_Vidt(op);
return 2;
// vzero, vone
case 6:
case 7:
MIPSInt::Int_VVectorInit(op);
return 2;
default:
return -1;
}
}
case 1:
{
switch ((op.encoding >> 16) & 0x1f) {
// vrnds
case 0:
MIPSInt::Int_Vrnds(op);
return 2;
// vrndi, vrndf1, vrndf2
case 1:
case 2:
case 3:
MIPSInt::Int_VrndX(op);
return 2;
// vf2h
case 18:
MIPSInt::Int_Vf2h(op);
return 2;
// vh2f
case 19:
MIPSInt::Int_Vh2f(op);
return 2;
// vsbz
case 22:
MIPSInt::Int_Vsbz(op);
return 2;
// vlgb
case 23:
MIPSInt::Int_Vlgb(op);
return 2;
// vuc2ifs, vc2i, vus2i, vs2i
case 24:
case 25:
case 26:
case 27:
MIPSInt::Int_Vx2i(op);
return 2;
// vi2uc, vi2c, vi2us, vi2s
case 28:
case 29:
case 30:
case 31:
MIPSInt::Int_Vi2x(op);
return 2;
default:
return -1;
}
}
case 2:
{
switch ((op.encoding >> 16) & 0x1f) {
// vsrt1
case 0:
MIPSInt::Int_Vsrt1(op);
return 2;
// vsrt2
case 1:
MIPSInt::Int_Vsrt2(op);
return 2;
// vbfy1, vbfy2
case 2:
case 3:
MIPSInt::Int_Vbfy(op);
return 2;
// vocp
case 4:
MIPSInt::Int_Vocp(op);
return 2;
// vsocp
case 5:
MIPSInt::Int_Vsocp(op);
return 2;
// vfad
case 6:
MIPSInt::Int_Vfad(op);
return 2;
// vavg
case 7:
MIPSInt::Int_Vavg(op);
return 2;
// vsrt3
case 8:
MIPSInt::Int_Vsrt3(op);
return 2;
// vsrt4
case 9:
MIPSInt::Int_Vsrt4(op);
return 2;
// vsgn
case 10:
MIPSInt::Int_Vsgn(op);
return 2;
// vmfvc
case 16:
MIPSInt::Int_Vmfvc(op);
return 2;
// vmtvc
case 17:
MIPSInt::Int_Vmtvc(op);
return 2;
// vt4444, vt5551, vt5650
case 25:
case 26:
case 27:
MIPSInt::Int_ColorConv(op);
return 2;
default:
return -1;
}
}
// vcst
case 3:
MIPSInt::Int_Vcst(op);
return 2;
// vf2in, vf2iz, vf2iu, vf2id
case 16:
case 17:
case 18:
case 19:
MIPSInt::Int_Vf2i(op);
return 2;
// vi2f
case 20:
MIPSInt::Int_Vi2f(op);
return 2;
// vcmov
case 21:
MIPSInt::Int_Vcmov(op);
return 2;
// vwbn, vwbn, vwbn, vwbn, vwbn, vwbn, vwbn, vwbn
case 24:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
MIPSInt::Int_Vwbn(op);
return 2;
default:
return -1;
}
}
// lv, lv.q, sv, sv.q
case 53:
case 54:
case 61:
case 62:
MIPSInt::Int_SVQ(op);
return 2;
case 55:
{
switch ((op.encoding >> 23) & 0x7) {
// vpfxs, vpfxs, vpfxt, vpfxt, vpfxd, vpfxd
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
MIPSInt::Int_VPFX(op);
return 2;
// viim.s, vfim.s
case 6:
case 7:
MIPSInt::Int_Viim(op);
return 2;
default:
return -1;
}
}
case 60:
{
switch ((op.encoding >> 21) & 0x1f) {
// vmmul, vmmul, vmmul, vmmul
case 0:
case 1:
case 2:
case 3:
MIPSInt::Int_Vmmul(op);
return 2;
// v(h)tfm2, v(h)tfm2, v(h)tfm2, v(h)tfm2, v(h)tfm3, v(h)tfm3, v(h)tfm3, v(h)tfm3, v(h)tfm4, v(h)tfm4, v(h)tfm4, v(h)tfm4
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
MIPSInt::Int_Vtfm(op);
return 2;
// vmscl, vmscl, vmscl, vmscl
case 16:
case 17:
case 18:
case 19:
MIPSInt::Int_Vmscl(op);
return 2;
// vcrsp.t/vqmul.q, vcrsp.t/vqmul.q, vcrsp.t/vqmul.q, vcrsp.t/vqmul.q
case 20:
case 21:
case 22:
case 23:
MIPSInt::Int_CrossQuat(op);
return 2;
case 28:
{
switch ((op.encoding >> 16) & 0xf) {
// vmmov
case 0:
MIPSInt::Int_Vmmov(op);
return 2;
// vmidt, vmzero, vmone
case 3:
case 6:
case 7:
MIPSInt::Int_VMatrixInit(op);
return 2;
default:
return -1;
}
}
// vrot
case 29:
MIPSInt::Int_Vrot(op);
return 2;
default:
return -1;
}
}
// vflush
case 63:
MIPSInt::Int_Vflush(op);
return 2;
default:
return -1;
}
}
+30
View File
@@ -0,0 +1,30 @@
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#pragma once
#include "Core/MIPS/MIPS.h"
// Fast switch-tree interpreter dispatcher, generated into InterpreterDispatch.cpp by
// GenerateInterpreterDispatch() in MIPSTables.cpp. Executes op on the global currentMIPS
// (same convention as the MIPSInt::Int_* handlers it calls into) and returns the number of
// cycles it consumed - or -1 if op isn't a recognized instruction (an invalid encoding, or
// one of the handful of real instructions with no interpreter implementation, e.g.
// tge/tlt/teq/...). Not total by design: the caller must fall back to MIPSInterpret() on a
// negative return, since deciding what "unhandled" means is a caller policy, not something
// a mechanically generated dispatch tree should embed.
int ExecInstruction(MIPSOpcode op);
@@ -31,7 +31,7 @@
#include "Core/System.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/MIPSVFPUUtils.h"
File renamed without changes.
+4 -4
View File
@@ -26,7 +26,7 @@
#include "Common/Serialize/SerializeFuncs.h"
#include "Core/ConfigValues.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/MIPS/MIPSDebugInterface.h"
#include "Core/MIPS/MIPSVFPUUtils.h"
@@ -323,8 +323,8 @@ void MIPSState::DoState(PointerWrap &p) {
}
void MIPSState::SingleStep() {
int cycles = MIPS_SingleStep();
currentMIPS->downcount -= cycles;
int cycles = MIPS_SingleStep(this);
downcount -= cycles;
CoreTiming::Advance();
}
@@ -347,7 +347,7 @@ int MIPSState::RunLoopUntil(u64 globalTicks) {
break;
case CPUCore::INTERPRETER:
return MIPSInterpret_RunUntil(globalTicks);
return MIPSInterpret_RunUntil(this, globalTicks);
}
return 1;
}
+194 -44
View File
@@ -15,6 +15,8 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <vector>
#include "Common/StringUtils.h"
#include "Core/Core.h"
@@ -22,8 +24,9 @@
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSDis.h"
#include "Core/MIPS/MIPSDisVFPU.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/MIPSIntVFPU.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/InterpreterDispatch.h"
#include "Core/MIPS/InterpreterVFPU.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/CoreTiming.h"
@@ -72,6 +75,7 @@ struct MIPSInstruction {
MIPSComp::MIPSCompileFunc compile;
MIPSDisFunc disasm;
MIPSInterpretFunc interpret;
const char *interpretName;
//MIPSInstructionInfo information;
MIPSInfo flags;
};
@@ -80,7 +84,7 @@ struct MIPSInstruction {
#define INVALID_X_8 INVALID,INVALID,INVALID,INVALID,INVALID,INVALID,INVALID,INVALID
#define ENCODING(a) {a}
#define INSTR(name, comp, dis, inter, flags) {Instruc, name, comp, dis, inter, MIPSInfo(flags)}
#define INSTR(name, comp, dis, inter, flags) {Instruc, name, comp, dis, inter, #inter, MIPSInfo(flags)}
#define JITFUNC(f) (&MIPSFrontendInterface::f)
@@ -899,6 +903,145 @@ const MIPSInstruction *MIPSGetInstruction(MIPSOpcode op) {
return instr;
}
// Emits one level of the dispatch tree as a switch statement, recursing into nested
// switches for ENCODING() redirects. Mirrors the traversal MIPSGetInstruction() does at
// runtime, but resolves it into source text once instead of walking it on every call.
static void EmitDispatchLevel(std::string &out, MipsEncoding encoding, int indent) {
const MIPSInstruction *table = mipsTables[encoding];
const EncodingBitsInfo &bits = encodingBits[encoding];
_dbg_assert_(table != nullptr);
std::string ind(indent * 4, ' ');
std::string ind2((indent + 1) * 4, ' ');
// Group indices that share identical behavior (same leaf interpret function, or same
// redirect target) so they can share one case block instead of duplicating it.
u32 size = bits.mask + 1;
std::vector<bool> handled(size, false);
bool anyCases = false;
for (u32 i = 0; i < size; i++) {
const MIPSInstruction &probe = table[i];
bool isInvalid = probe.altEncoding == Inval || (probe.altEncoding == Instruc && probe.interpret == nullptr);
if (!isInvalid) {
anyCases = true;
break;
}
}
if (!anyCases) {
// Every slot in this level is invalid (or unimplemented) - no point emitting an
// empty switch (MSVC warns C4065 on a switch with only a default label).
out += ind + "return -1;\n";
return;
}
out += ind + StringFromFormat("switch ((op.encoding >> %d) & 0x%x) {\n", bits.shift, bits.mask);
for (u32 i = 0; i < size; i++) {
if (handled[i])
continue;
const MIPSInstruction &instr = table[i];
if (instr.altEncoding == Inval)
continue; // Falls through to default (slow path).
if (instr.altEncoding == Instruc && instr.interpret == nullptr)
continue; // No interpreter implemented for this one (e.g. tge/tlt/teq/...) - slow path.
handled[i] = true;
std::vector<u32> group{ i };
for (u32 j = i + 1; j < size; j++) {
if (handled[j])
continue;
const MIPSInstruction &other = table[j];
bool same;
if (instr.altEncoding == Instruc && other.altEncoding == Instruc)
// Cycle count must match too - it's baked into the generated "return N;",
// so two leaves sharing a handler but not a cycle count can't be merged.
same = instr.interpret == other.interpret && instr.flags.cycles == other.flags.cycles;
else if (instr.altEncoding != Instruc && other.altEncoding != Instruc)
same = instr.altEncoding == other.altEncoding;
else
same = false;
if (same) {
group.push_back(j);
handled[j] = true;
}
}
if (instr.altEncoding == Instruc) {
std::string names;
for (u32 idx : group) {
if (!names.empty())
names += ", ";
names += table[idx].name;
}
out += ind + StringFromFormat("// %s\n", names.c_str());
for (u32 idx : group)
out += ind + StringFromFormat("case %d:\n", idx);
out += ind2 + StringFromFormat("MIPSInt::%s(op);\n", instr.interpretName);
out += ind2 + StringFromFormat("return %d;\n", (int)instr.flags.cycles);
} else {
for (u32 idx : group)
out += ind + StringFromFormat("case %d:\n", idx);
out += ind + "{\n";
EmitDispatchLevel(out, instr.altEncoding, indent + 1);
out += ind + "}\n";
// No trailing return here - the nested switch above always either returns
// a value directly from a case, or falls to its own default's "goto slow_path;".
}
}
out += ind + "default:\n";
out += ind2 + "return -1;\n";
out += ind + "}\n";
}
// Generates a full, compilable Core/MIPS/InterpreterDispatch.cpp: a fast
// int ExecInstruction(MIPSOpcode op) dispatcher, built by resolving the tables above into
// a nested switch tree at generation time, so each real instruction is reached by a direct
// call instead of MIPSGetInstruction()'s per-instruction table walk plus indirect call
// through instr->interpret. Leaves call straight into the existing MIPSInt::Int_* handlers
// - the tables only record which handler an opcode maps to, not the handler's behavior, so
// that's the only thing there is to call - and return that instruction's fixed cycle count
// (baked in at generation time, same value MIPSGetInstructionCycleEstimate() would have
// returned) so the caller can still track downcount. Operates on the global currentMIPS,
// same as the Int_* handlers do.
//
// The generated function is deliberately not total: most of the 32-bit opcode space doesn't
// decode to anything (either a genuinely invalid encoding, or a real-but-uninterpreted
// instruction like tge/tlt/teq/...), and any such case returns -1 instead of embedding a
// fallback. The generator has no more information about those cases than "not one of the
// ~305 known leaves" - what to actually do about that (log it, disassemble it, whatever) is
// a policy decision that belongs to the caller, not to a mechanically generated dispatch
// tree. Callers must check for a negative return and fall back to MIPSInterpret() themselves.
//
// Regenerate by running `PPSSPPHeadless --generate-interpreter-dispatch > Core/MIPS/InterpreterDispatch.cpp`
// after the tables above change. Do not hand-edit the generated file.
std::string GenerateInterpreterDispatch() {
std::string out;
out += "// Copyright (c) 2012- PPSSPP Project.\n\n";
out += "// This program is free software: you can redistribute it and/or modify\n";
out += "// it under the terms of the GNU General Public License as published by\n";
out += "// the Free Software Foundation, version 2.0 or later versions.\n\n";
out += "// This program is distributed in the hope that it will be useful,\n";
out += "// but WITHOUT ANY WARRANTY; without even the implied warranty of\n";
out += "// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n";
out += "// GNU General Public License 2.0 for more details.\n\n";
out += "// A copy of the GPL 2.0 should have been included with the program.\n";
out += "// If not, see http://www.gnu.org/licenses/\n\n";
out += "// Official git repository and contact information can be found at\n";
out += "// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.\n\n";
out += "// AUTO-GENERATED by `PPSSPPHeadless --generate-interpreter-dispatch` from the\n";
out += "// tables in MIPSTables.cpp - do not hand-edit, regenerate instead.\n\n";
out += "#include \"Core/MIPS/MIPS.h\"\n";
out += "#include \"Core/MIPS/Interpreter.h\"\n";
out += "#include \"Core/MIPS/InterpreterVFPU.h\"\n\n";
out += "// Returns the cycle count consumed, or -1 if op isn't a recognized instruction -\n";
out += "// callers must fall back to MIPSInterpret() themselves in that case.\n";
out += "int ExecInstruction(MIPSOpcode op) {\n";
EmitDispatchLevel(out, Imme, 1);
out += "}\n";
return out;
}
void MIPSCompileOp(MIPSOpcode op, MIPSComp::MIPSFrontendInterface *jit) {
if (op == 0)
return;
@@ -939,16 +1082,24 @@ void MIPSDisAsm(MIPSOpcode op, u32 pc, char *out, size_t outSize, bool tabsToSpa
}
}
static inline void Interpret(const MIPSInstruction *instr, MIPSOpcode op) {
// Shared by both Interpret() below (the MIPSGetInstruction()-based slow path, still used by
// RunUntilWithChecks) and RunUntilFast()'s -1 handling (ExecInstruction() is generated from
// the exact same tables MIPSGetInstruction() walks, so it has no better information to offer
// here - no point re-walking those tables just to rediscover "no interpreter for this op").
static void HandleUnknownInstruction(MIPSState *mips, MIPSOpcode op) {
ERROR_LOG_REPORT(Log::CPU, "Unknown instruction %08x at %08x", op.encoding, mips->pc);
// Try to disassemble it
char disasm[256];
MIPSDisAsm(op, mips->pc, disasm, sizeof(disasm));
_dbg_assert_msg_(0, "%s", disasm);
mips->pc += 4;
}
static inline void Interpret(MIPSState *mips, const MIPSInstruction *instr, MIPSOpcode op) {
if (instr && instr->interpret) {
instr->interpret(op);
} else {
ERROR_LOG_REPORT(Log::CPU, "Unknown instruction %08x at %08x", op.encoding, currentMIPS->pc);
// Try to disassemble it
char disasm[256];
MIPSDisAsm(op, currentMIPS->pc, disasm, sizeof(disasm));
_dbg_assert_msg_(0, "%s", disasm);
currentMIPS->pc += 4;
HandleUnknownInstruction(mips, op);
}
}
@@ -958,18 +1109,12 @@ inline int GetInstructionCycleEstimate(const MIPSInstruction *instr) {
return 1;
}
void MIPSInterpret(MIPSOpcode op) {
void MIPSInterpret(MIPSState *mips, MIPSOpcode op) {
const MIPSInstruction *instr = MIPSGetInstruction(op);
Interpret(instr, op);
Interpret(mips, instr, op);
}
#define _RS ((op>>21) & 0x1F)
#define _RT ((op>>16) & 0x1F)
#define _RD ((op>>11) & 0x1F)
#define R(i) (curMips->r[i])
static inline void RunUntilFast() {
MIPSState *curMips = currentMIPS;
static inline void RunUntilFast(MIPSState *curMips) {
// NEVER stop in a delay slot!
while (curMips->downcount >= 0 && coreState == CORE_RUNNING_CPU) {
do {
@@ -980,9 +1125,17 @@ static inline void RunUntilFast() {
MIPSOpcode op = MIPSOpcode(Memory::ReadUnchecked_U32(curMips->pc));
bool wasInDelaySlot = curMips->inDelaySlot;
const MIPSInstruction *instr = MIPSGetInstruction(op);
Interpret(instr, op);
curMips->downcount -= GetInstructionCycleEstimate(instr);
int cycles = ExecInstruction(op);
if (cycles < 0) {
// Not a recognized instruction (invalid encoding, or a known instruction
// with no interpreter implementation, e.g. tge/tlt/teq/...). No point
// calling MIPSGetInstruction() here - see HandleUnknownInstruction()'s
// comment. Every such case has cycles == 1 today (same as
// GetInstructionCycleEstimate()'s default for a null/flagless instr).
HandleUnknownInstruction(curMips, op);
cycles = 1;
}
curMips->downcount -= cycles;
// The reason we have to check this is the delay slot hack in Int_Syscall.
if (curMips->inDelaySlot && wasInDelaySlot) {
@@ -993,8 +1146,8 @@ static inline void RunUntilFast() {
}
}
static void RunUntilWithChecks(u64 globalTicks) {
MIPSState *curMips = currentMIPS;
#define _RS(op) ((op>>21) & 0x1F)
static void RunUntilWithChecks(MIPSState *curMips, u64 globalTicks) {
// NEVER stop in a delay slot!
bool hasBPs = g_breakpoints.HasBreakPoints();
bool hasMCs = g_breakpoints.HasMemChecks();
@@ -1010,7 +1163,7 @@ static void RunUntilWithChecks(u64 globalTicks) {
// Check for breakpoint
if (hasBPs && g_breakpoints.IsAddressBreakPoint(curMips->pc) && g_breakpoints.CheckSkipFirst() != curMips->pc) {
auto cond = g_breakpoints.GetBreakPointCondition(currentMIPS->pc);
auto cond = g_breakpoints.GetBreakPointCondition(curMips->pc);
if (!cond || cond->Evaluate()) {
Core_Break(BreakReason::CpuBreakpoint, curMips->pc);
if (g_breakpoints.IsTempBreakPoint(curMips->pc))
@@ -1021,7 +1174,7 @@ static void RunUntilWithChecks(u64 globalTicks) {
if (hasMCs && (instr->flags & (IN_MEM | OUT_MEM)) != 0 && g_breakpoints.CheckSkipFirst() != curMips->pc && instr->interpret != &Int_Syscall) {
// This is common for all IN_MEM/OUT_MEM funcs.
int offset = (instr->flags & IS_VFPU) != 0 ? SignExtend16ToS32(op & 0xFFFC) : SignExtend16ToS32(op);
u32 addr = (R(_RS) + offset) & 0xFFFFFFFC;
u32 addr = (curMips->r[_RS(op)] + offset) & 0xFFFFFFFC;
int sz = MIPSGetMemoryAccessSize(op);
if ((instr->flags & IN_MEM) != 0)
@@ -1035,7 +1188,7 @@ static void RunUntilWithChecks(u64 globalTicks) {
}
bool wasInDelaySlot = curMips->inDelaySlot;
Interpret(instr, op);
Interpret(curMips, instr, op);
curMips->downcount -= GetInstructionCycleEstimate(instr);
// The reason we have to check this is the delay slot hack in Int_Syscall.
@@ -1049,17 +1202,17 @@ static void RunUntilWithChecks(u64 globalTicks) {
return;
}
}
#undef _RS
int MIPSInterpret_RunUntil(u64 globalTicks) {
MIPSState *curMips = currentMIPS;
int MIPSInterpret_RunUntil(MIPSState *curMips, u64 globalTicks) {
while (coreState == CORE_RUNNING_CPU) {
CoreTiming::Advance();
uint64_t ticksLeft = globalTicks - CoreTiming::GetTicks();
if (g_breakpoints.HasBreakPoints() || g_breakpoints.HasMemChecks() || ticksLeft <= curMips->downcount)
RunUntilWithChecks(globalTicks);
RunUntilWithChecks(curMips, globalTicks);
else
RunUntilFast();
RunUntilFast(curMips);
if (CoreTiming::GetTicks() > globalTicks) {
// DEBUG_LOG(Log::CPU, "Hit the max ticks, bailing 1 : %llu, %llu", globalTicks, CoreTiming::GetTicks());
@@ -1070,28 +1223,26 @@ int MIPSInterpret_RunUntil(u64 globalTicks) {
return 1;
}
const char *MIPSGetName(MIPSOpcode op)
{
const char *MIPSGetName(MIPSOpcode op) {
static const char * const noname = "unk";
const MIPSInstruction *instr = MIPSGetInstruction(op);
if (!instr)
if (!instr) {
return noname;
else
} else {
return instr->name;
}
}
MIPSInfo MIPSGetInfo(MIPSOpcode op)
{
// int crunch = CRUNCH_MIPS_OP(op);
MIPSInfo MIPSGetInfo(MIPSOpcode op) {
const MIPSInstruction *instr = MIPSGetInstruction(op);
if (instr)
if (instr) {
return instr->flags;
else
} else {
return MIPSInfo(BAD_INSTRUCTION);
}
}
MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op)
{
MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op) {
const MIPSInstruction *instr = MIPSGetInstruction(op);
if (instr->interpret)
return instr->interpret;
@@ -1100,8 +1251,7 @@ MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op)
}
// TODO: Do something that makes sense here.
int MIPSGetInstructionCycleEstimate(MIPSOpcode op)
{
int MIPSGetInstructionCycleEstimate(MIPSOpcode op) {
const MIPSInstruction *instr = MIPSGetInstruction(op);
return GetInstructionCycleEstimate(instr);
}
+7 -2
View File
@@ -131,11 +131,16 @@ namespace MIPSComp {
void MIPSCompileOp(MIPSOpcode op, MIPSComp::MIPSFrontendInterface *jit);
void MIPSDisAsm(MIPSOpcode op, u32 pc, char *out, size_t outSize, bool tabsToSpaces = false);
MIPSInfo MIPSGetInfo(MIPSOpcode op);
void MIPSInterpret(MIPSOpcode op); //only for those rare ones
int MIPSInterpret_RunUntil(u64 globalTicks);
void MIPSInterpret(MIPSState *mips, MIPSOpcode op); //only for those rare ones
int MIPSInterpret_RunUntil(MIPSState *mips, u64 globalTicks);
MIPSInterpretFunc MIPSGetInterpretFunc(MIPSOpcode op);
int MIPSGetInstructionCycleEstimate(MIPSOpcode op);
int MIPSGetMemoryAccessSize(MIPSOpcode op);
const char *MIPSGetName(MIPSOpcode op);
std::string MIPSDisasmAt(u32 compilerPC);
// Generates the full contents of Core/MIPS/InterpreterDispatch.cpp: a fast switch-tree
// int ExecInstruction(MIPSOpcode op) dispatcher, derived from the tables in MIPSTables.cpp.
// See GenerateInterpreterDispatch()'s comment (in the .cpp) for details.
std::string GenerateInterpreterDispatch();
+2 -2
View File
@@ -26,7 +26,7 @@
#include "Core/MemMap.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/HLE/ReplaceTables.h"
@@ -129,7 +129,7 @@ void FakeJit::Compile(u32 em_address) {
}
void FakeJit::RunLoopUntil(u64 globalticks) {
MIPSInterpret_RunUntil(globalticks);
MIPSInterpret_RunUntil(currentMIPS, globalticks);
}
void FakeJit::DoJit(u32 em_address, JitBlock *b) {
+1 -1
View File
@@ -36,7 +36,7 @@
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSAnalyst.h"
#include "Core/MIPS/MIPSCodeUtils.h"
#include "Core/MIPS/MIPSInt.h"
#include "Core/MIPS/Interpreter.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/HLE/ReplaceTables.h"
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python
# Regenerates Core/MIPS/InterpreterDispatch.cpp (the ExecInstruction switch-tree
# interpreter dispatcher) from the tables in Core/MIPS/MIPSTables.cpp.
#
# Run this after changing anything that affects those tables - new instructions,
# retimed cycle counts, encoding changes, etc. The generated file is checked into
# git like any other source file, not built on the fly, so it needs to be
# regenerated and the diff committed whenever the tables change.
#
# This script only needs an already-built PPSSPPHeadless binary (see
# docs/pspautotests.md for how to build one); it doesn't build anything itself.
# After it finishes, just build normally - Core/MIPS/InterpreterDispatch.cpp is
# already wired into every build system, so no further steps are needed.
import os
import subprocess
import sys
# Same search list test.py uses to find a headless build.
CANDIDATES = [
"Windows/x64/Debug/PPSSPPHeadless.exe",
"Windows/x64/Release/PPSSPPHeadless.exe",
"Windows/Debug/PPSSPPHeadless.exe",
"Windows/Release/PPSSPPHeadless.exe",
"build/Debug/PPSSPPHeadless",
"build/Release/PPSSPPHeadless",
"build/RelWithDebInfo/PPSSPPHeadless",
"build/MinSizeRel/PPSSPPHeadless",
"build/PPSSPPHeadless",
"./PPSSPPHeadless",
"./PPSSPPHeadless.exe",
]
OUT_PATH = os.path.join("Core", "MIPS", "InterpreterDispatch.cpp")
def find_headless():
for candidate in CANDIDATES:
if os.path.isfile(candidate):
return candidate
return None
def main():
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
os.chdir(repo_root)
headless = find_headless()
if headless is None:
sys.stderr.write("error: couldn't find a built PPSSPPHeadless binary. Checked:\n")
for candidate in CANDIDATES:
sys.stderr.write(" %s\n" % candidate)
sys.stderr.write("Build PPSSPPHeadless first (see docs/pspautotests.md), then re-run this script.\n")
return 1
print("Using %s" % headless)
# subprocess/CreateProcess on Windows can fail to resolve a relative,
# forward-slash path as the executable itself - use an absolute, normalized path.
headless_abs = os.path.normpath(os.path.abspath(headless))
try:
result = subprocess.run([headless_abs, "--generate-interpreter-dispatch"], capture_output=True)
except OSError as e:
sys.stderr.write("error: couldn't run %s: %s\n" % (headless, e))
return 1
if result.returncode != 0:
sys.stderr.buffer.write(result.stderr)
sys.stderr.write("error: %s exited with code %d\n" % (headless, result.returncode))
return 1
generated = result.stdout
# Sanity check before clobbering the tracked file - a stale/broken binary should
# fail loudly here rather than silently truncating InterpreterDispatch.cpp.
if b"int ExecInstruction(MIPSOpcode op)" not in generated:
sys.stderr.write("error: generated output doesn't look right (missing ExecInstruction) - not overwriting %s\n" % OUT_PATH)
return 1
with open(OUT_PATH, "wb") as f:
f.write(generated)
print("Regenerated %s" % OUT_PATH)
return 0
if __name__ == "__main__":
sys.exit(main())
+6 -4
View File
@@ -290,8 +290,9 @@
<ClInclude Include="..\..\Core\MIPS\MIPSDebugInterface.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSDis.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSDisVFPU.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSInt.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSIntVFPU.h" />
<ClInclude Include="..\..\Core\MIPS\Interpreter.h" />
<ClInclude Include="..\..\Core\MIPS\InterpreterDispatch.h" />
<ClInclude Include="..\..\Core\MIPS\InterpreterVFPU.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSStackWalk.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSTables.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSVFPUFallbacks.h" />
@@ -586,8 +587,9 @@
<ClCompile Include="..\..\Core\MIPS\MIPSDebugInterface.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSDis.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSDisVFPU.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSInt.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSIntVFPU.cpp" />
<ClCompile Include="..\..\Core\MIPS\Interpreter.cpp" />
<ClCompile Include="..\..\Core\MIPS\InterpreterDispatch.cpp" />
<ClCompile Include="..\..\Core\MIPS\InterpreterVFPU.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSStackWalk.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSTables.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSVFPUFallbacks.cpp" />
+6 -4
View File
@@ -225,8 +225,9 @@
<ClCompile Include="..\..\Core\MIPS\MIPSDebugInterface.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSDis.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSDisVFPU.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSInt.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSIntVFPU.cpp" />
<ClCompile Include="..\..\Core\MIPS\Interpreter.cpp" />
<ClCompile Include="..\..\Core\MIPS\InterpreterDispatch.cpp" />
<ClCompile Include="..\..\Core\MIPS\InterpreterVFPU.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSStackWalk.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSTables.cpp" />
<ClCompile Include="..\..\Core\MIPS\MIPSVFPUFallbacks.cpp" />
@@ -618,8 +619,9 @@
<ClInclude Include="..\..\Core\MIPS\MIPSDebugInterface.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSDis.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSDisVFPU.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSInt.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSIntVFPU.h" />
<ClInclude Include="..\..\Core\MIPS\Interpreter.h" />
<ClInclude Include="..\..\Core\MIPS\InterpreterDispatch.h" />
<ClInclude Include="..\..\Core\MIPS\InterpreterVFPU.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSStackWalk.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSTables.h" />
<ClInclude Include="..\..\Core\MIPS\MIPSVFPUFallbacks.h" />
+3 -2
View File
@@ -514,8 +514,9 @@ EXEC_AND_LIB_FILES := \
$(SRC)/Core/MIPS/MIPSDis.cpp \
$(SRC)/Core/MIPS/MIPSDisVFPU.cpp \
$(SRC)/Core/MIPS/MIPSAsm.cpp \
$(SRC)/Core/MIPS/MIPSInt.cpp.arm \
$(SRC)/Core/MIPS/MIPSIntVFPU.cpp.arm \
$(SRC)/Core/MIPS/Interpreter.cpp.arm \
$(SRC)/Core/MIPS/InterpreterDispatch.cpp \
$(SRC)/Core/MIPS/InterpreterVFPU.cpp.arm \
$(SRC)/Core/MIPS/MIPSStackWalk.cpp \
$(SRC)/Core/MIPS/MIPSTables.cpp \
$(SRC)/Core/MIPS/MIPSVFPUUtils.cpp.arm \
+38 -4
View File
@@ -54,6 +54,7 @@
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/EmuThread.h"
#include "Core/MIPS/MIPSTables.h"
#include "Core/System.h"
#include "Core/WebServer.h"
#include "Core/HLE/sceUtility.h"
@@ -521,6 +522,35 @@ public:
}
};
// Has a parameter because we might start using this from the main build.
void SetupCRT(bool headless) {
#if defined(_MSC_VER)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
if (headless) {
// Suppress abort dialogs and similar.
// 1. Redirect CRT Assertions/Errors/Warnings to stdout/stderr
const _HFILE reportTarget = _CRTDBG_FILE_STDERR;
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ASSERT, reportTarget);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_ERROR, reportTarget);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, reportTarget);
// 2. Suppress the abort() message box & crash reporting dialogs
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
// 3. Suppress Windows OS-level "Program has stopped working" modal dialogs
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
}
#endif
}
int main(int argc, const char* argv[]) {
PROFILE_INIT();
TimeInit();
@@ -535,9 +565,7 @@ int main(int argc, const char* argv[]) {
}
#endif
#if defined(_DEBUG) && defined(_MSC_VER)
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
SetupCRT(true);
CommandLineOptions cmdLineOptions;
CommandLineParseResult parseResult = cmdLineOptions.Parse(argc, argv, CmdLineMode::Headless);
@@ -550,6 +578,12 @@ int main(int argc, const char* argv[]) {
break;
}
if (cmdLineOptions.generateInterpreterDispatch.value_or(false)) {
std::string code = GenerateInterpreterDispatch();
fwrite(code.data(), 1, code.size(), stdout);
return 0;
}
// Needed before any sockets can be used (WSAStartup on Windows) - without this, the
// WebSocket debugger silently fails to listen. Only done when requested since headless
// otherwise has no use for networking.
@@ -709,7 +743,7 @@ int main(int argc, const char* argv[]) {
return 1;
#else
// TODO: Will we need a larger window for higher resolutions? Well, not if we use buffered rendering.
window = CreateHiddenWindow(480, 272, cmdLineOptions.gpuBackend.value(), &windowDesc);
window = CreateHiddenWindow(480, 272, cmdLineOptions.gpuBackend.value_or(GPUBackend::OPENGL), &windowDesc);
if (!windowDesc.Valid()) {
fprintf(stderr, "Failed to create a window for graphics context");
return 1;
+3 -2
View File
@@ -871,8 +871,9 @@ SOURCES_CXX += \
$(COREDIR)/MIPS/MIPSDebugInterface.cpp \
$(COREDIR)/MIPS/MIPSDis.cpp \
$(COREDIR)/MIPS/MIPSDisVFPU.cpp \
$(COREDIR)/MIPS/MIPSInt.cpp \
$(COREDIR)/MIPS/MIPSIntVFPU.cpp \
$(COREDIR)/MIPS/Interpreter.cpp \
$(COREDIR)/MIPS/InterpreterDispatch.cpp \
$(COREDIR)/MIPS/InterpreterVFPU.cpp \
$(COREDIR)/MIPS/MIPSTables.cpp \
$(COREDIR)/MIPS/MIPSStackWalk.cpp \
$(COREDIR)/MIPS/MIPSVFPUUtils.cpp \