setjmp was executed once per block dispatch to guard against longjmp
from MMU faults and panics inside compiled blocks. One setjmp per
rk65c02_run_jit call is equivalent: emu_stop_error sets a terminal
state and clears in_jit_run before jumping, so the fault path lands
before the loop, processes any write events the faulting block had
recorded (shared jit_process_pending_writes helper), and the loop
condition exits. disable_jit_for_run is volatile per setjmp rules.
Result: functional ROM JIT speedup 2.40x -> 2.61x, extended 3.03x ->
3.28x (~8% less JIT time), fault paths still green (mmu fault tests,
mmu_pae).
Instrumentation added for dispatch-cost attribution:
- insns_executed in rk65c02_jit_stats (blocks record num_insns);
bench_functional prints avg insns/block and emulated MIPS
- bench_functional takes an optional tick-interval argument
- JIT_PAGE_INVALIDATION_THRESHOLD / JIT_MAX_INVALIDATIONS_PER_RUN
overridable via -D for experiments (defaults unchanged)
- msbasic host: RK65C02_JIT=0 disables JIT, RK65C02_JIT_STATS=1 dumps
stats to stderr; bench.bas benchmark program for scripted runs
Attribution findings (medians, this machine):
- avg insns/block: 4.9 (Dormann functional), 3.5 (MS BASIC) - per-
dispatch overhead is the dominant JIT-mode cost; block chaining is
the next big lever
- tick at interval 0 costs ~10% in BOTH modes (harness artifact,
speedup ratio unchanged)
- disabling page demotion makes things WORSE (functional drops to
0.97x: unbounded invalidation churn until the run budget disables
JIT) - demotion is a net win, not a cost
- MS BASIC: 2.5x under JIT (1.02s vs 2.55s); write_events=4,
pages_demoted=1 - CHRGET-style zero-page SMC gets its page demoted
at startup, so BASIC's hottest routine runs interpreted all session;
dynamic-operand recompilation (Tier 4) is the targeted fix
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>