recompiler: IRQ-redirect guard must CONSUME the benign same-site resume

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 <noreply@anthropic.com>
This commit is contained in:
tetrisgm
2026-08-19 11:45:58 -07:00
parent 6ead85c9da
commit 21a8457ed8
2 changed files with 5 additions and 5 deletions
+2 -2
View File
@@ -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);
}
+3 -3
View File
@@ -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);