Repository navigation
Conversation
With --instruction-blocks --region-paced a CPU slice can stop at any instruction. The scheduler then interprets the rest of the routine, and its callers after its return, until the next call bounces. --aot-resume gives each region-paced variant a switch in its prologue (g_aot_resume_table in dispatch_v2.c), so code a slice stopped in goes on compiled: a frame suspended at a deadline is re-entered with its own entry S and return contract, and code the scheduler reached by a jump is entered as a frame one below S. SNESRECOMP_AOT_RESUME=0 at run time turns it off, =2 keeps suspended frames only. Off by default; the flag is part of the generation identity only when on. What keeps the tiers alike: - An instruction compiled code hands back in place (a region-paced fallback: unmodeled, MMIO, a pointer it cannot preview) runs once in the interpreter before resuming there again. - The prologue's stack-window fallback hands back the resume point, not the variant's entry. - A frameless frame's normal return (a bound routine that ran its own RTS/RTL) goes on at the address it popped. - A compiled RTI goes on at the popped PC while the scheduler owns the run (interp_bridge_aot_rti), so interrupt handlers resume too. - The JSL envelope restores PB after a normal return even with an unwind pending. - Each region-paced instruction that stays compiled reports itself to g_interp_bridge_pc_hook (cpu_aot_insn_begin), as the interpreter does. - The beam is synced before compiled code reads PPU, joypad and $42xx registers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
--region-pacedseems to have solved a lot of problems for me. This is an extension, so Evermore can use the native code as often as possible.Summary
With
--instruction-blocks --region-paced, compiled code checks the master-clock deadline before every instruction, so a CPUslice can stop at any instruction. The scheduler then interprets the rest of the routine, and its callers after its return,
until the next call bounces. A host that slices often (DRAM refresh points, vblank edges, single steps around NMI enables)
ends up mostly interpreted: about 13 % of Secret of Evermore's instructions ran compiled.
--aot-resumegives each region-paced variant a resume switch, so code a slice stopped in goes on compiled:Off by default; the flag is part of the generation identity only when on (existing recipe digests do not change).
SNESRECOMP_AOT_RESUME=0at run time turns it off,=2keeps suspended frames only.What keeps the tiers alike:
interpreter before resuming there again.
g_interp_bridge_pc_hook(cpu_aot_insn_begin),as the interpreter does.
$42xxregisters.Related: #276 adds yields at H/V IRQ latches to region-paced code; each such stop would otherwise hand the rest of the
routine to the interpreter, so the two complement each other. The compiled RTI here also covers what #251's Secret of Mana
run needed as a test-only addition (resumable interrupt handlers).
Evidence
Secret of Evermore (USA), two MesenCE 2.2.1 movies from power-on (8,592 and 23,756 NMIs), WRAM, VRAM, CGRAM and OAM
compared at every NMI, nothing copied or masked, on main
80a41241:--instruction-blocks --region-paced): both replays identical; about 13 % of instructions compiled; thesecond replay runs in 93 s.
After (
--aot-resumeadded): both replays identical; 94.05 % / 92.43 % compiled; the second replay runs in 79 s(15 % faster end to end, PPU and APU included). Also identical with resume off at run time and all-interpreted.
pytest tests/v2 tests/test_snes_cycles.py: 638 passed. New:tests/v2/test_aot_resume.py(a resume entry per instruction,off by default, needs region pacing, the fallback hands back the resume point, the resume table keeps the longest variant,
the flag check). The region-pacing harness gets stubs for the two resume helpers and
cpu_aot_insn_begin.Recipe: https://github.com/r-bin/SecretOfEvermoreRecomp/blob//docs/pr/README.md#reproduce
Merge Danger
Door: two-way. Opt-in at generation; off at run time with
SNESRECOMP_AOT_RESUME=0.Blast Radius: small for projects that do not pass
--aot-resume. Two changes reach everyone using region pacing or thebridge: region-paced instructions now call
cpu_aot_insn_begin(a hook call, no timing change), and the beam is syncedbefore compiled register reads.
Who gains: projects that pass
--region-pacedand whose host stops the CPU often (per-line slices, single steps aroundinterrupts, IRQ-latch yields as in #276). With
beam_frame_driver's few stops per field the gain is small; without regionpacing nothing changes.
Limits: measured on one game; the speed gain is one replay's wall time. Not yet run together with #276. Frameless entries
assume a frame one below S; a routine that returns from such an entry in an unusual way goes back to the interpreter at
the popped PC.