Skip to content
Open
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
9 changes: 8 additions & 1 deletion ps2xRecomp/src/lib/control_flow_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,14 @@ namespace ps2recomp
}
}
}
if (!foundTable)
// Only an unresolved computed *jump* can land on an arbitrary
// instruction of this function and therefore force every address to
// become an entry point. JALR is a call: it transfers control to
// another function and comes back to the instruction after the delay
// slot, which is already queued as a resume target above. Treating a
// call like a jump here promotes the whole function for what is
// usually just a function pointer or virtual dispatch.
if (!foundTable && jrInst->function != SPECIAL_JALR)
{
needsIndirectFallback = true;
}
Comment on lines +395 to 398
Expand Down
19 changes: 15 additions & 4 deletions ps2xTest/src/code_generator_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ void register_code_generator_tests()
"unresolved JR should not pretend it has a resolved local jump table");
});

tc.Run("unresolved JALR marks internal labels as indirect fallback resume entries", [](TestCase &t) {
tc.Run("unresolved JALR resumes after the call without promoting the function", [](TestCase &t) {
Function func;
func.name = "unresolved_jalr_fallback";
func.start = 0x3200;
Expand All @@ -548,10 +548,21 @@ void register_code_generator_tests()
CodeGenerator gen({}, {});
CodeGenerator::AnalysisResult analysis = gen.collectInternalBranchTargets(func, instructions);

t.IsTrue(analysis.indirectFallbackEntryPoints.contains(0x320Cu),
"unresolved JALR should register internal labels as resumable entries for the owning function");
// JALR is a call: it returns past the delay slot, so 0x320C is the only
// address in this function that has to be reachable from outside.
t.IsTrue(analysis.resumeEntryPoints.contains(0x320Cu),
"unresolved JALR should mark its return pc as resumable");
t.IsTrue(analysis.entryPoints.contains(0x320Cu),
"unresolved JALR fallback targets should still emit labels in the owner");
"unresolved JALR resume pc should still emit a label in the owner");

// Both sets feed the same owner resume-target list, so the return pc is
// registered either way; what must not happen is the whole-function
// promotion reserved for jumps that could land anywhere.
t.IsFalse(analysis.indirectFallbackEntryPoints.contains(0x3210u),
"an indirect call must not promote unrelated instructions to entry points");
t.IsFalse(analysis.indirectFallbackEntryPoints.contains(0x3200u),
"an indirect call must not promote the function start to a fallback entry");

t.IsFalse(analysis.jumpTableTargets.contains(0x3204u),
"unresolved JALR should not pretend it has a resolved local jump table");
});
Expand Down