From bf97d218fcb688dd91072df6d38c18846295d379 Mon Sep 17 00:00:00 2001 From: James Sandri <7078671+jlsandri@users.noreply.github.com> Date: Sat, 28 Mar 2026 14:46:33 +1100 Subject: [PATCH] Emit lookupFunction calls for external TOML jump table targets When a [[jump_tables.table]] entry has targets outside the function boundary, emit runtime lookupFunction dispatch calls instead of filtering them out. Internal targets still use goto labels. Added externalJumpTableTargets to AnalysisResult to distinguish internal gotos from external calls during codegen. --- ps2xRecomp/include/ps2recomp/code_generator.h | 4 ++ ps2xRecomp/src/lib/code_generator.cpp | 55 ++++++++++++++++--- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/ps2xRecomp/include/ps2recomp/code_generator.h b/ps2xRecomp/include/ps2recomp/code_generator.h index 04adfdea..d3221e62 100644 --- a/ps2xRecomp/include/ps2recomp/code_generator.h +++ b/ps2xRecomp/include/ps2recomp/code_generator.h @@ -38,6 +38,10 @@ namespace ps2recomp struct AnalysisResult { std::unordered_set entryPoints; std::unordered_map> jumpTableTargets; + // Targets from TOML-configured jump tables that are OUTSIDE the + // function boundary. Codegen emits lookupFunction calls for these + // instead of internal goto labels. + std::unordered_set externalJumpTableTargets; }; std::string generateFunction(const Function &function, const std::vector &instructions, const bool &useHeaders); diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index 666a59e7..6b03c61a 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -432,7 +432,22 @@ namespace ps2recomp ss << " switch (jumpTarget) {\n"; for (uint32_t t : sortedInternalTargets) { - ss << fmt::format(" case 0x{:X}u: goto label_{:x};\n", t, t); + if (analysisResult.externalJumpTableTargets.contains(t)) + { + // External target from TOML config: emit lookupFunction + // call. The target is in a different compiled function + // (e.g., a jump table case block in another CSV entry). + // After the call, chain if pc stays in the external range. + ss << fmt::format(" case 0x{:X}u: {{\n", t); + ss << " auto __extFn = runtime->lookupFunction(jumpTarget);\n"; + ss << " if (__extFn) __extFn(rdram, ctx, runtime);\n"; + ss << " return;\n"; + ss << " }\n"; + } + else + { + ss << fmt::format(" case 0x{:X}u: goto label_{:x};\n", t, t); + } } ss << " default: break;\n"; ss << " }\n"; @@ -781,28 +796,50 @@ namespace ps2recomp const auto configuredTableIt = m_configJumpTableTargetsByAddress.find(tableAddress); if (configuredTableIt != m_configJumpTableTargetsByAddress.end()) { - std::vector jrTargets; - jrTargets.reserve(configuredTableIt->second.size()); + std::vector internalTargets; + std::vector externalTargets; for (uint32_t target : configuredTableIt->second) { if (target >= function.start && target < function.end && instructionAddresses.contains(target)) { - jrTargets.push_back(target); + internalTargets.push_back(target); + } + else + { + externalTargets.push_back(target); } } - if (!jrTargets.empty()) + // Internal targets: handled as goto labels (existing path) + if (!internalTargets.empty()) { - std::sort(jrTargets.begin(), jrTargets.end()); - jrTargets.erase(std::unique(jrTargets.begin(), jrTargets.end()), jrTargets.end()); - result.jumpTableTargets[jrInst->address] = jrTargets; - for (uint32_t target : jrTargets) + std::sort(internalTargets.begin(), internalTargets.end()); + internalTargets.erase(std::unique(internalTargets.begin(), internalTargets.end()), internalTargets.end()); + result.jumpTableTargets[jrInst->address] = internalTargets; + for (uint32_t target : internalTargets) { result.entryPoints.insert(target); } foundTable = true; } + + // External targets: will be emitted as function calls + // in the jr dispatch switch. Store them alongside internal + // targets so the codegen can emit both gotos and calls. + if (!externalTargets.empty()) + { + auto &targets = result.jumpTableTargets[jrInst->address]; + for (uint32_t target : externalTargets) + { + targets.push_back(target); + } + std::sort(targets.begin(), targets.end()); + targets.erase(std::unique(targets.begin(), targets.end()), targets.end()); + // Mark external targets so codegen emits calls not gotos + result.externalJumpTableTargets.insert(externalTargets.begin(), externalTargets.end()); + foundTable = true; + } } uint32_t unshiftedIndexReg = 0;