diff --git a/ps2xRecomp/src/lib/code_generator.cpp b/ps2xRecomp/src/lib/code_generator.cpp index 666a59e7..757ec3f8 100644 --- a/ps2xRecomp/src/lib/code_generator.cpp +++ b/ps2xRecomp/src/lib/code_generator.cpp @@ -1003,6 +1003,12 @@ namespace ps2recomp } } + // Safety fallback: if execution reaches here, the function boundary + // was incomplete (missing jr $ra). Return via $ra to prevent ctx->pc + // from being left in a bad state, which would cascade returns up the + // entire call chain. For correctly-terminated functions this is + // unreachable dead code after the jr $ra return block. + ss << " ctx->pc = GPR_U32(ctx, 31); return;\n"; ss << "}\n"; return ss.str(); } diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index 2ea7d54c..36276d54 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -636,10 +636,22 @@ namespace ps2recomp const Function *containingFunction = findContainingFunction(function.start); uint32_t sliceEndAddress = containingFunction ? containingFunction->end : function.end; + // Only clamp to boundaries OUTSIDE the containing function. + // Sub-functions inside the parent (e.g. Ghidra-detected sub_xxx) + // must not truncate sibling entry slices that share the parent range. + uint32_t parentEnd = sliceEndAddress; auto nextIt = std::upper_bound(boundaryStarts.begin(), boundaryStarts.end(), function.start); - if (nextIt != boundaryStarts.end() && *nextIt < sliceEndAddress) + while (nextIt != boundaryStarts.end() && *nextIt < parentEnd) { + // Skip boundaries inside the containing function — they are + // sub-functions, not real function starts that should truncate us. + if (containingFunction && *nextIt < containingFunction->end) + { + ++nextIt; + continue; + } sliceEndAddress = *nextIt; + break; } if (sliceEndAddress <= function.start)