From 745046ca43c7e8717f2f8eb1f6d11216952e35a2 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Tue, 28 Apr 2026 21:52:32 -0700 Subject: [PATCH] analysis: don't extend max_reached past last visited code in jtbl handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit discover_function_bounds was extending max_reached to cover the jump table's bytes themselves (jtbl_end - 4). This was wrong: jtbl entries are DATA, not code. Including their bytes in the function's words range made recompile_function try to interpret intervening data words as MIPS instructions, which usually fails (encountering 'pref', 'INVALID', branches to outside the function, etc.). The fix: stop extending max_reached for jtbl_end. The recompiler's analyze_function reads jtbl entries directly from context.rom — they don't need to be part of func.words. Function bounds = max-reached- instruction + 4 (delay slot), nothing more. Effect on Stadium's 0x8FF00000 pattern activation: Before this fix: 1 recompile error (`func_8FF00020__rom_FE000020` at instr 8243 — 'pref' instruction emitted at vram 0x8FF080B8, followed by INVALID instructions). The function had been sized large enough to contain unrelated data after the case-arms region. After this fix: 0 errors. All 219 pattern-synthesized sections recompile cleanly. Stadium boot impact (pattern activated, 30-second run): - 1048 audio hits (15x improvement; was ~70 with static fragment78). - 31 fragments registered (3.4x improvement; was ~9). - 17 different fragment78-family variants streamed through link vram 0x8FF00000, each correctly content-hash-dispatched. - Stadium reaches a NEW failure mode much later: `lookup miss at 0x810001D0` — a different fragment slot (link bucket 0x10) needs similar treatment. Tracked separately. The pattern is now ACTIVE in Stadium's game.toml. game.toml updated to document the active configuration. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/analysis.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/analysis.cpp b/src/analysis.cpp index d7bbe23..333ba8f 100644 --- a/src/analysis.cpp +++ b/src/analysis.cpp @@ -526,21 +526,22 @@ bool N64Recomp::discover_function_bounds( cursor, jtbl.vram); return false; } - // Add each target to BFS. Also extend max_reached past - // the table itself so we count its bytes as part of - // the function. + // Add each target to BFS so its arm gets walked and + // its instructions count toward max_reached. Do NOT + // extend max_reached to cover the jump table's bytes + // themselves: jtbl entries are DATA, not code, and + // including them in the function's `words` makes + // recompile_function try to interpret intervening + // data words as MIPS instructions (which usually + // fails because the data isn't valid code). The + // recompiler's analyze_function reads jtbl entries + // directly from context.rom — they don't need to + // be inside func.words. for (size_t t : jtbl_targets) { if (!visited.contains(t)) { worklist.push_back(t); } } - size_t jtbl_end = (jtbl.vram - vram_base) + - collected * 4; - if (jtbl_end > 0) { - if (jtbl_end - 4 > max_reached) { - max_reached = jtbl_end - 4; - } - } break; // block ends after the jr's delay slot }