mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
psxRecClearMem walks down through recBlocks from the block containing the written word to pick up straddlers — blocks that start below it but whose bodies reach into the range being cleared. recBlocks is ordered by startpc, not by end address, so stopping at the first block that ends before the write is unsound: a longer block lower down can jump clean over a short one and still cover the write, and the scan quits before it is ever examined. The straddler is then neither removed from recBlocks nor LUT-reset, so it keeps executing stale code. This is the IOP half of the same blind spot3ac41e05bdfixed on the EE; both stops came from801d71f7f0(2009), which added them to iR3000A and iR5900 in one commit and is still upstream in both. Scan past a non-overlapping neighbour instead of stopping on it, bounded by s_maxBlockBytes — the longest guest extent compiled since the last reset, so the walk still terminates as soon as nothing below could reach. The scan cursor is split from the removal start, so a skipped survivor never becomes the base of the removal range. Unlike the EE, IOP blocks have no 4KB page break (psxRecRecompile ends them only at a branch or an already-compiled head), so the mark has no structural cap beyond the 0xffff-instruction assert. IOP blocks are short in practice; the cost is flat-array reads either way. New gate ClearScansPastNonOverlappingNeighborToStraddlerBelow builds the geometry the bug needs — A long, H compiled mid-A, then B truncated against H's compiled head, which is the only way to get a block that both starts inside A and ends before A does. It pokes a word inside A past B's end and requires A to run the new instruction. Red before this change with the JIT returning the stale 0x11 against the interpreter's 0x22, green after. recompiler_tests 1439/1439.