Files
ARMSX2/tests
Brian Degenhardtandpstef fd1fa89e10 IOP: route JIT RAM stores through out-of-line fast-path stubs
After the removal of the per-store block search, the remaining per-store cost
was the C call chain itself: iopMemWrite* region dispatch + WLUT load, the
indirect psxCpu->Clear call, and the recClearIOP/psxRecClearMem frames --
~2.9% of battle EE-thread samples in pstef's profile (iopMemWrite32 1.13%,
iopMemWrite16 0.28%, recClearIOP 1.44%).

Emit three per-width stubs (_DynGen_StoreStub) alongside the dispatchers
instead: gate, IsC check, direct store into Main[addr & (ExposedIopRam-1)],
and an inline g_iopCodeCov granule probe that only calls into the clear
machinery (iopStoreClearHit -> recClearIOP) when a live block overlaps the
store. Store sites emit a single BL, exactly the size of the old C call.
Hw/unmapped targets tail-jump from the stub to iopMemWrite*, which returns
straight to the store site; compile-time-known hw addresses keep calling C
directly.

The condition is (addr & 0x1f800000) == 0, not a bit-28 test: iopMemWrite*
masks addresses to 0x1fffffff, and within that space pages 0x00-0x7f are the
only WLUT-mapped write target reachable this way (plain RAM; the parallel port
at 0x1f00 is the other mapped page and the mask excludes it). Everything else
-- 0x1f80/0x1f40 hw, SIF, DEV9, SPU2, ROM, and the unmapped
0x00800000-0x0fffffff range that bit 28 alone misclassifies as RAM -- must
take the C path. Bits 23-28 survive the phys mask, so the test is exact for
every KUSEG/KSEG0/KSEG1 mirror, and matches iopMemReset's `for (i < 0x0080)`
mapping loop. The RAM mask equals the recLUT_SetPage mirror collapse, so the
masked offset doubles as the coverage probe index. IsC (Status bit 16)
swallows the store with no clear, matching the C RAM branch's
p != NULL && !IsC guard. Stubs are re-emitted on every recResetIOP, so the
baked mask and RAM base track extra-memory-mode flips (which discard all
blocks). RPSXSTATE is x21, callee-saved, so it survives both the BL and the
slow-path tail jump.

Outlining is deliberate: pstef measured a fully inline fast path (~15
insns/site) first, and its per-site code growth cost +10% L1I misses/frame,
eating the win (3D net zero, FMV only -1.08% cycles). The shared stubs keep
sites baseline-sized and also displace the C functions' own icache footprint.

Documented at the stub, not fixed here: the inline probe is mirror-collapsed
(addr & (ExposedIopRam-1)) while iopCovAdjust and psxRecClearMem key the same
array by HWADDR, which strips the KSEG base but not the RAM mirrors. Under a
2MB configuration a block executed from a mirror page registers coverage at a
different granule than a store to its physical alias probes. That is exactly
the pre-existing blindness of keying recBlocks by HWADDR -- not a regression
-- but the two domains are a trap for anyone making either side finer-grained.

Six new IopSmc cases: KSEG1-mirror, const-address, SB, and SH stores over a
pre-compiled victim block (compiled first, then stored into via RunResume, so
a stale-cache leak is actually observable), unmapped-low-region drop, and
IsC-swallow. Verified not inert: a bit-28-only gate turns
JitStoreToUnmappedLowRegionIsDropped red, and defeating the IsC check turns
six cases red including JitStoreWithCacheIsolatedIsSwallowed. Full suite
1432/1432.

Taken from https://github.com/yaps2/yaps2/pull/11 by pstef
(https://github.com/pstef).

Co-authored-by: pstef <3462925+pstef@users.noreply.github.com>
2026-07-24 21:02:50 -07:00
..