From d31953fe6bb8750ab30602587f2b461cec79b330 Mon Sep 17 00:00:00 2001 From: tetrisgm <30949000+tetrisgm@users.noreply.github.com> Date: Wed, 19 Aug 2026 00:30:29 -0700 Subject: [PATCH] Fix overlay autocompile: missing cull-widen ABI symbols; PGXP hooks swallowed by #endif Two bugs that together kept every mod-patched text page interpreted forever (the WipEout 3 framerate mod's 74 divergent pages -> ~0.5M interpreted block dispatches/sec in play): - The [[widescreen.cull.widen]] emitter writes psx_ws_cull_slt_widen / psx_ws_cull_slti_lower at configured sites, including in overlay-resident code, but the overlay dispatch preamble never defined them: every shard containing a widen site failed to link (undefined reference) and the autocompile pipeline reported compile-error for the whole region. Mirror both helpers into the preamble following the documented self-contained, byte-identical convention. - append_pgxp_hooks joins the hook onto the translated statement with a space; the block-cycles MULT/DIV/MFLO/MFHI translations end in `#endif`, and the preprocessor discards tokens after #endif (the "extra tokens" warning class) - so those hooks silently never ran. Give the hook its own line when the translation ends in a directive. Co-Authored-By: Claude Fable 5 --- recompiler/src/pgxp_hook_emitter.cpp | 7 +++++++ runtime/include/overlay_dispatch_preamble.c.inc | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/recompiler/src/pgxp_hook_emitter.cpp b/recompiler/src/pgxp_hook_emitter.cpp index 1455983c..5c54b8f5 100644 --- a/recompiler/src/pgxp_hook_emitter.cpp +++ b/recompiler/src/pgxp_hook_emitter.cpp @@ -30,6 +30,13 @@ inline int16_t get_imm16(uint32_t i) { return (int16_t)(i & 0xFFFF); } * return early above translate_instruction's main dispatch and are likewise * unhooked (they are cull compares, not vertex moves; validation covers). */ void append_pgxp_hooks(uint32_t instr, std::string& code) { + /* A translation ending in a preprocessor directive (the block-cycles + * muldiv wrappers end in `#endif`) would swallow an appended hook: the + * preprocessor discards tokens after #endif (the "extra tokens at end of + * #endif" warning class), so the MULT/DIV/MFLO/MFHI hooks silently never + * ran. Give the hook its own line. */ + if (code.size() >= 6 && code.compare(code.size() - 6, 6, "#endif") == 0) + code += "\n "; const uint32_t opcode = (instr >> 26) & 0x3F; const uint32_t rs = get_rs(instr); const uint32_t rt = get_rt(instr); diff --git a/runtime/include/overlay_dispatch_preamble.c.inc b/runtime/include/overlay_dispatch_preamble.c.inc index c2acb8e7..eee589b7 100644 --- a/runtime/include/overlay_dispatch_preamble.c.inc +++ b/runtime/include/overlay_dispatch_preamble.c.inc @@ -211,6 +211,23 @@ int psx_ws_cull_vxrange(uint32_t x, uint32_t imm) { return (((x + (uint32_t)margin) & 0xFFFFu) < (bound + 2u * (uint32_t)margin)) ? 1 : 0; } +/* [[widescreen.cull.widen]] SLT-family helpers. The emitter writes these at + * explicitly configured cull sites, INCLUDING in overlay-resident code, so + * the DLL must define them or the shard fails to link (undefined reference -> + * no native shard -> that mod-patched page runs interpreted forever). Same + * contract as the funnel helpers above: self-contained on psx_ws_x_margin(), + * identity at margin 0 (4:3), MUST stay byte-for-byte identical to the gpu.c + * implementations. */ +int psx_ws_cull_slti_lower(uint32_t sx, uint32_t imm) { + int32_t bound = (int32_t)(int16_t)(uint16_t)imm; + return ((int32_t)sx < bound - psx_ws_x_margin()) ? 1 : 0; +} +int psx_ws_cull_slt_widen(uint32_t rs, uint32_t rt, int bound_is_rt) { + const int32_t m = psx_ws_x_margin(); + const int32_t a = (int32_t)rs, b = (int32_t)rt; + return bound_is_rt ? ((a < b + m) ? 1 : 0) + : ((a + m < b) ? 1 : 0); +} void psx_ws_sprite_tag(CPUState *cpu) { if (g_cbs.ws_sprite_tag) g_cbs.ws_sprite_tag(cpu); }