Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ps2xRecomp/include/ps2recomp/code_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ namespace ps2recomp
struct AnalysisResult {
std::unordered_set<uint32_t> entryPoints;
std::unordered_map<uint32_t, std::vector<uint32_t>> 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<uint32_t> externalJumpTableTargets;
};

std::string generateFunction(const Function &function, const std::vector<Instruction> &instructions, const bool &useHeaders);
Expand Down
55 changes: 46 additions & 9 deletions ps2xRecomp/src/lib/code_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -781,28 +796,50 @@ namespace ps2recomp
const auto configuredTableIt = m_configJumpTableTargetsByAddress.find(tableAddress);
if (configuredTableIt != m_configJumpTableTargetsByAddress.end())
{
std::vector<uint32_t> jrTargets;
jrTargets.reserve(configuredTableIt->second.size());
std::vector<uint32_t> internalTargets;
std::vector<uint32_t> 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;
Expand Down