Files
ppsspp/Tools/update-dispatcher.py
T
Henrik RydgårdandClaude Sonnet 5 ffad0acea1 Plumb an explicit MIPSState *mips through the interpreter's Int_* handlers
All ~82 MIPSInt::Int_* functions (Interpreter.cpp/.h,
InterpreterVFPU.cpp/.h) now take an explicit MIPSState *mips instead
of reaching for the global currentMIPS internally, along with their
file-local helpers (DelayBranchTo, SkipLikely, ApplySwizzleS/T,
ApplyPrefixD/ST, RetainInvalidSwizzleST, EatPrefixes). MIPSInterpretFunc,
Interpret(), ExecInstruction()/InterpreterDispatch.cpp (regenerated),
and RunUntilFast() all thread mips through accordingly.

Deliberately left on currentMIPS for now: MIPSVFPUUtils.cpp's
ReadVector/WriteVector/ReadMatrix/WriteMatrix/VFPURewritePrefix -
these are shared with every JIT backend's compile-time VFPU code, so
parameterizing them would balloon this into a JIT-wide refactor. This
is a partial refactor; that's the next boundary to push on.

Several JIT backends (x86 Jit.cpp, ARM/ArmJit.cpp, ARM64/Arm64Jit.cpp,
x86/X64IRJit.cpp, RiscV/RiscVJit.cpp, LoongArch64/LoongArch64Jit.cpp,
ARM64/Arm64IRJit.cpp) bake the raw interpreter function pointer
directly into JIT-generated machine code as their "fall back to the
interpreter for this one op" mechanism, with only a single argument
register set up for the call. Rather than hand-editing register
allocation across four architectures that can't be build-tested here,
added MIPSInterpretTrampoline(MIPSOpcode op) - a 1-arg wrapper around
MIPSInterpret(currentMIPS, op) - and pointed all 7 such call sites at
it instead, leaving that codegen untouched. Two other call sites
(JitLogMiss, JitBranchLog) were plain C++ calls and just got the
extra argument directly.

Verified (Windows x64): PPSSPPWindows/PPSSPPHeadless/UnitTest all
build clean, 49/49 unit tests pass. `test.py -g --graphics=software`:
interpreter 312/314 (cpu/fpu/fpu is the pre-existing, unrelated
interpreter-vs-JIT denormal difference; gpu/rendertarget/copy passes
standalone, so was cross-test state bleed in the batch run, not a
regression), default JIT 314/314, jit-ir 313/314 (gpu/vertices/morph
is an expected difference from the vertex decoder taking a different
mode with this core change, not a bug).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
2026-08-12 14:02:19 +02:00

91 lines
3.0 KiB
Python

#!/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(MIPSState *mips, 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())