Skip to content

codegen: force continuation on JAL/JALR resume-PC mismatch instead of aborting caller - #117

Closed
jlsandri wants to merge 5 commits into
ran-j:mainfrom
jlsandri:pr/codegen-jal-jalr-resume-pc
Closed

codegen: force continuation on JAL/JALR resume-PC mismatch instead of aborting caller#117
jlsandri wants to merge 5 commits into
ran-j:mainfrom
jlsandri:pr/codegen-jal-jalr-resume-pc

Conversation

@jlsandri

@jlsandri jlsandri commented Apr 6, 2026

Copy link
Copy Markdown

Stacked on #115 (codegen: handle continuation PCs and nested callee yields). Please merge #115 first — once it lands, this PR's diff shrinks to just the two commits below.

Problem

After a JAL/JALR call + continuation re-entry loop, if ctx->pc didn't match the expected fallthrough address, the previous codegen logged [PC_MISMATCH] and aborted the caller with return.

That abort cascaded up the entire call stack whenever a nested callee legitimately left ctx->pc at an internal resume target — common with overlay functions, nested JALR chains, and any function that sets ctx->pc as part of its own resume-PC mechanism. The caller's stack frame and register context were still valid; the codegen was throwing them away on a false signal.

Fix

Replace the hard return with:

  1. A throttled [JALR_FIXUP] / [JAL_FIXUP] diagnostic — 5 warnings per call site, so the signal isn't lost.
  2. ctx->pc = fallthroughPc; — force continuation at the caller's expected resume point.

Two commits:

  1. JALR resume-PC fixup at the single indirect-call site (depends on codegen: handle continuation PCs and nested callee yields #115 for the re-entry loop structure it edits).
  2. JAL resume-PC fixup extending the same treatment to all three direct-call codegen paths for consistency:
    • Direct JAL via findFunctionByAddress (called 0x%x pattern)
    • Direct JAL via lookupFunction (called 0x{literal} pattern)
    • Reloc JAL (called reloc pattern)

Why this is stacked on #115

The JALR hunk rewrites the PC_MISMATCH branch that #115 introduces as part of the re-entry loop. Without #115 merged first, there is no PC_MISMATCH to rewrite.

Scope

  • One file: ps2xRecomp/src/lib/code_generator.cpp
  • +21 / -7

Rationale

The old behaviour was conservative — any resume-PC anomaly aborted the whole chain — and that's reasonable as a first line of defence. But in practice the only trigger is legitimate internal resume-PC use by the callee, which the caller should tolerate. Demoting the abort to a throttled warning + forced continuation keeps the diagnostic signal while eliminating the cascade-failure class entirely.

jlsandri added 5 commits April 6, 2026 18:55
When a callee yields at an internal continuation PC, the caller now
re-enters it in a while loop until it exits to the real fallthrough.
Prevents nested continuations from leaking as false PC_MISMATCH.
After a JALR call + re-entry loop, if ctx->pc doesn't match the
expected fallthrough address, the previous codegen printed
[PC_MISMATCH] and aborted the caller with `return`. That cascaded
up the entire call stack whenever a nested callee legitimately left
ctx->pc at an internal resume target (overlay functions, nested
JALR chains, and functions that set ctx->pc for their own resume-PC
mechanism).

Replace the hard abort with a throttled [JALR_FIXUP] diagnostic
(5 warnings per call site) and force ctx->pc = fallthroughPc so the
caller's still-valid stack frame and register state can continue
executing.

Stacks on the continuation-PC handling PR — this hunk replaces the
PC_MISMATCH branch that PR introduced.
Extends the JALR_FIXUP to cover all three JAL codegen paths:
1. Direct JAL via findFunctionByAddress (called 0x%x pattern)
2. Direct JAL via lookupFunction (called 0x{literal} pattern)
3. Reloc JAL (called reloc pattern)

All paths now force ctx->pc = fallthroughPc on mismatch instead of
aborting. This removes the cascade failure where a nested callee's
PC_MISMATCH would abort every function up the call stack.
@jlsandri
jlsandri force-pushed the pr/codegen-jal-jalr-resume-pc branch from a4153e8 to bc83180 Compare April 6, 2026 08:56
@jlsandri

jlsandri commented Apr 6, 2026

Copy link
Copy Markdown
Author

Closing as part of a batch cleanup after #107 landed. The runtime ecosystem refactor in #107 substantially reworked the files this PR touched, and I would like to re-audit the underlying fix against the new code structure before putting it back in front of you. If the fix is still needed after that re-audit, I will re-open as a focused PR rebased onto current main. Thanks for your patience.

@jlsandri jlsandri closed this Apr 6, 2026
@jlsandri
jlsandri deleted the pr/codegen-jal-jalr-resume-pc branch April 6, 2026 09:17
LuisFellp added a commit to LuisFellp/PS2Recomp that referenced this pull request Jul 22, 2026
- Generalize dispatchGuestBranch's mid-function resume handling in
  ps2_runtime.cpp so it correctly re-drives whichever recompiled
  function ctx->pc lands in after a call, not just the original
  callee -- fixes cases where a callee tail-calls into another
  function that itself needs resuming (mirrors upstream's unmerged
  PR ran-j#115/ran-j#117 intent).
- Fix a delay-slot-boundary truncation bug in FUN_0018c018 that
  dropped the last instruction before its jr $ra, causing the game
  to exit immediately after boot.
- Fix a jal-mistranslated-as-internal-goto codegen bug in
  FUN_00194f30 that produced a broken inline duplicate instead of a
  proper cross-function call, causing an infinite dispatch spin.
- Fix six SIF/IOP synchronous-RPC hangs (CreateSema+submit+WaitSema
  idiom) by signaling the semaphore inline once the real IOP-side
  async completion isn't otherwise emulated.
- Add a bounded spin guard to the flag-poll loop in
  FUN_001655E0/label_16560c: it called func_1D3930 with a constant
  argument (address never advances between iterations, unlike the
  legitimate ~65k-entry loop next to it), so nothing could ever
  clear the awaited flag -- forces the loop to exit after 2000
  spins instead of exhausting dispatchGuestBranch's resume guard and
  silently stalling the whole process.
- Keep the [DIAG:*] instrumentation added while chasing these (entry
  callfail/badtarget tracing, ctor-call/loopcheck, per-thread status
  dump in run()) for whoever picks this back up.

Net effect: boot no longer stalls at the previous entry-callfail
point (tick ~14640); a marathon run now reaches tick ~81000+ with no
new dispatch failures. Still no confirmed GS/GIF draw traffic in that
span (gif transfer counter stays at its initial boot value), so the
game has not yet been observed producing real video output.
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