From 21a8457ed8126baf2987f9b4300ff8c8991b60c2 Mon Sep 17 00:00:00 2001 From: tetrisgm <30949000+tetrisgm@users.noreply.github.com> Date: Wed, 19 Aug 2026 11:45:58 -0700 Subject: [PATCH] recompiler: IRQ-redirect guard must CONSUME the benign same-site resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first guard shape (6ead85c) surfaced redirects but left cpu->pc nonzero when the exception epilogue benignly resumed at the site's own PC (the offline mode-1 restore path publishes cpu->pc = real_epc and never zeroes it). At a goto-type check site the stale pc then survived into the NEXT check site, where it read as a foreign redirect and spuriously unwound the frame mid-function — measured live on WipEout 3: the top-level PC=0 exit moved earlier (frame ~639, exit ra=0) instead of dying at the original site. The trampoline's own return-boundary helper already consumes the same-site publish (pc==stop -> pc=0); the guard now does the same: surface a foreign resume, consume a same-site one, fall through untouched only when pc==0. Co-Authored-By: Claude Fable 5 --- recompiler/src/code_generator.cpp | 4 ++-- recompiler/src/full_function_emitter.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/recompiler/src/code_generator.cpp b/recompiler/src/code_generator.cpp index 75a38d59..7a1b173b 100644 --- a/recompiler/src/code_generator.cpp +++ b/recompiler/src/code_generator.cpp @@ -157,7 +157,7 @@ std::string CodeGenerator::emit_interrupt_check(uint32_t resume_pc, return std::string("#ifdef PSX_ENABLE_BLOCK_CYCLES\n") + indent + "psx_cyc_bb_defer_flush();\n#endif\n" + indent + fmt::format("psx_check_interrupts_at(cpu, 0x{:08X}u);\n", resume_pc) + indent + - fmt::format("if (cpu->pc != 0u && ((cpu->pc ^ 0x{:08X}u) & 0x1FFFFFFFu)) return; /* IRQ redirect */\n", + fmt::format("if (cpu->pc != 0u) {{ if ((cpu->pc ^ 0x{:08X}u) & 0x1FFFFFFFu) return; cpu->pc = 0u; }} /* IRQ redirect / consume same-site resume */\n", resume_pc); } @@ -166,7 +166,7 @@ std::string CodeGenerator::emit_interrupt_check_expr(const std::string& resume_p return std::string("#ifdef PSX_ENABLE_BLOCK_CYCLES\n") + indent + "psx_cyc_bb_defer_flush();\n#endif\n" + indent + fmt::format("psx_check_interrupts_at(cpu, {});\n", resume_pc_expr) + indent + - fmt::format("if (cpu->pc != 0u && ((cpu->pc ^ ({})) & 0x1FFFFFFFu)) return; /* IRQ redirect */\n", + fmt::format("if (cpu->pc != 0u) {{ if ((cpu->pc ^ ({})) & 0x1FFFFFFFu) return; cpu->pc = 0u; }} /* IRQ redirect / consume same-site resume */\n", resume_pc_expr); } diff --git a/recompiler/src/full_function_emitter.cpp b/recompiler/src/full_function_emitter.cpp index efea2674..7bcad55f 100644 --- a/recompiler/src/full_function_emitter.cpp +++ b/recompiler/src/full_function_emitter.cpp @@ -181,14 +181,14 @@ bool FullFunctionEmitter::emit_function( return std::string("#ifdef PSX_ENABLE_BLOCK_CYCLES\n") + indent + "psx_cyc_bb_defer_flush();\n#endif\n" + indent + fmt::format("psx_check_interrupts_at(cpu, 0x{:08X}u);\n", rt) + indent + - fmt::format("if (cpu->pc != 0u && ((cpu->pc ^ 0x{:08X}u) & 0x1FFFFFFFu)) return; /* IRQ redirect */\n", rt); + fmt::format("if (cpu->pc != 0u) {{ if ((cpu->pc ^ 0x{:08X}u) & 0x1FFFFFFFu) return; cpu->pc = 0u; }} /* IRQ redirect / consume same-site resume */\n", rt); }; auto emit_irq_check_expr = [](const std::string& resume_pc_expr, const std::string& indent = " ") { return std::string("#ifdef PSX_ENABLE_BLOCK_CYCLES\n") + indent + "psx_cyc_bb_defer_flush();\n#endif\n" + indent + fmt::format("psx_check_interrupts_at(cpu, {});\n", resume_pc_expr) + indent + - fmt::format("if (cpu->pc != 0u && ((cpu->pc ^ ({})) & 0x1FFFFFFFu)) return; /* IRQ redirect */\n", + fmt::format("if (cpu->pc != 0u) {{ if ((cpu->pc ^ ({})) & 0x1FFFFFFFu) return; cpu->pc = 0u; }} /* IRQ redirect / consume same-site resume */\n", resume_pc_expr); }; auto emit_cosim_instr = [](uint32_t pc, const std::string& indent = " ") { @@ -1210,7 +1210,7 @@ bool FullFunctionEmitter::emit_function( " * back here as a registered continuation target. */\n" " if (cpu->read_word(0x{:08X}u) != 0u) {{\n" " psx_check_interrupts_at(cpu, 0x{:08X}u);\n" - " if (cpu->pc != 0u && ((cpu->pc ^ 0x{:08X}u) & 0x1FFFFFFFu)) return; /* IRQ redirect */\n" + " if (cpu->pc != 0u) {{ if ((cpu->pc ^ 0x{:08X}u) & 0x1FFFFFFFu) return; cpu->pc = 0u; }} /* IRQ redirect / consume */\n" " cpu->pc = 0x{:08X}u; return;\n" " }}\n", addr, ram_pc, ram_pc + 0x10u, ram_pc, ram_pc, ram_pc, ram_pc);