Skip to content

fix(recomp): register resume points for standalone entry_* functions - #271

Open
drakolordx7 wants to merge 1 commit into
ran-j:mainfrom
drakolordx7:fix/recomp-resume-points-standalone-entry
Open

drakolordx7 wants to merge 1 commit into
ran-j:mainfrom
drakolordx7:fix/recomp-resume-points-standalone-entry

Conversation

@drakolordx7

Copy link
Copy Markdown

discoverAdditionalEntryPoints() skipped every entry_* function when collecting resume targets, assuming they are synthetic wrappers nested in a real function. Ghidra also exports standalone code blocks (static initializers, thunks) as entry_<addr>. Those got no resume points, so a thread switched out inside a call made from one could never be resumed.

Now only entry_* functions inside a real function's range are skipped (sorted interval lookup, cheap for tens of thousands of blocks). Games with many standalone entry_* blocks get a few more resume cases in the generated output.

The new test builds an ELF with a standalone entry_* block containing a JAL and checks that the return address is registered; it fails without the change. ps2x_tests 437/437.

Made by drakolord and assisted with Claude Code.

discoverAdditionalEntryPoints() skipped every function whose name starts
with "entry_" when collecting resume targets, on the assumption that they
are synthetic wrappers that live inside a real function (the ones
DiscoverAdditionalEntryPoints() itself creates for mid-function targets).
Their owner then supplies the resume points.

That does not hold for entry_* labels that come from a symbol or function
map: Ghidra exports standalone code blocks (static initializers, thunks,
code the analyzer did not attach to a function) as entry_<addr>, and they
are not inside any other function. Those were skipped with nothing to
cover them, so a thread switched out inside a call made from such a block
(JAL return address, loop head, ...) had no registered resume point and
could never be resumed at it.

Skip an entry_* function only when it lies inside the range of a
non-entry function; standalone ones now collect their own resume targets
like any other function. The nested check uses a sorted interval lookup,
so it stays cheap for maps with tens of thousands of entry_* blocks.

Found in a game whose map names ~65k standalone blocks entry_*; a static
constructor block (entry_004186f0) switched out inside a call never came
back. The new recompiler test generates a tiny ELF with a standalone
entry_* block containing a JAL and checks that the return address is
registered; it fails without the change.

Made by drakolord and assisted with Claude Code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant