Add a codegen tool to generate a fast switch-tree interpreter dispatcher

MIPSTables.cpp has walked a tree of tables on every single interpreted
instruction since forever, with a standing TODO asking for exactly this:
"generate smart dispatcher functions from above tables instead of this
slow method." GenerateInterpreterDispatch() does that - it walks the
same tables MIPSGetInstruction() walks at runtime, but resolves the
walk into a nested switch tree once, at generation time, with each
leaf calling straight into the existing MIPSInt::Int_* handlers and
returning that instruction's fixed cycle count. Anything not covered
(invalid opcodes, and the handful of instructions with no interpreter
implemented at all, e.g. tge/tlt/teq) falls back to the existing
MIPSInterpret()/MIPSGetInstructionCycleEstimate() slow path, so the
result is total over all 32-bit inputs, same as the table-walking path.

Wired up via a new headless --generate-interpreter-dispatch flag,
which prints the generated Core/MIPS/InterpreterDispatch.cpp source to
stdout and exits.

Also widens CmdLine.cpp's --help column formatting, which silently
truncated any option name longer than 24 characters - the new option's
name was the first to hit it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019SKhm9wKEQzRUsx9mTrrtQ
This commit is contained in:
Henrik Rydgård
2026-08-12 10:24:01 +02:00
co-authored by Claude Sonnet 5
parent d718bda178
commit 65495c76c0
5 changed files with 162 additions and 4 deletions
+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) {