|
| 1 | +# VM JIT roadmap |
| 2 | + |
| 3 | +Goal: a JIT for the register VM with **no gap** between the JIT, the interpreter, |
| 4 | +and the compiled (Rust-lowering) backend. Built verification-first. |
| 5 | + |
| 6 | +## Invariants (hold at every tier) |
| 7 | + |
| 8 | +1. **Single source of semantic truth.** The JIT reuses the interpreter's value |
| 9 | + representation (`VmValue`) and runtime intrinsics. It generates code for hot |
| 10 | + control-flow / arithmetic and *calls back* into the shared runtime for |
| 11 | + everything else — it never reimplements semantics. |
| 12 | +2. **Fallback = correctness floor.** Anything the JIT does not fully support |
| 13 | + runs on the interpreter (`run_frame`). Unsupported features cannot create a |
| 14 | + gap; only what the JIT *does* compile can have bugs — and that is exactly what |
| 15 | + the differential targets. |
| 16 | +3. **N-way differential as the gate.** `interp ≡ jit ≡ compiled` on the curated |
| 17 | + corpus and on generated programs (`tests/common/differential.rs`, |
| 18 | + `tests/backend_differential.rs`). Every tier must keep it green. |
| 19 | +4. **Converging coverage.** What the JIT covers vs falls back is reported |
| 20 | + (`RegVmExecutable::jit_plan`); unsupported instructions must be a documented, |
| 21 | + shrinking set, never a silent divergence. |
| 22 | +5. **Determinism.** Generated programs exclude/seed all nondeterminism (float |
| 23 | + NaN/ordering, map iteration order, time/random/env, scheduling, div-by-zero) |
| 24 | + so divergence is always a real bug. |
| 25 | + |
| 26 | +## Status |
| 27 | + |
| 28 | +- **Phase 0 — N-way differential framework.** Done. `Backend` trait |
| 29 | + (interp/jit/compiled), `assert_backends_agree`, generative differential. |
| 30 | +- **Phase 1 — tier-0 JIT (seam + correctness floor).** Done. |
| 31 | + `reg_vm_eval_source_main_jit` runs per-function eligibility analysis and |
| 32 | + executes through the shared interpreter; `JitPlan` reports eligible vs |
| 33 | + fallback. No native code yet. |
| 34 | + |
| 35 | +## Phase 2 — native baseline codegen |
| 36 | + |
| 37 | +A native tier must live in a **separate crate** (e.g. `vm-jit`): the `rsscript` |
| 38 | +crate is `#![forbid(unsafe_code)]`, and executing generated machine code + |
| 39 | +calling function pointers requires `unsafe` (mirror the `native-abi` `host` |
| 40 | +feature pattern). |
| 41 | + |
| 42 | +- **Codegen:** Cranelift (`cranelift-jit`/`-frontend`/`-codegen`) — mature, fast |
| 43 | + compile, Rust-native. (LLVM is heavier; a copy-and-patch/template JIT is an |
| 44 | + even lighter first step.) |
| 45 | +- **Bytecode ABI:** expose the `RegFunction`/`RegInstr` surface (currently |
| 46 | + private) as a stable, versioned IR the JIT crate consumes. |
| 47 | +- **Value glue:** `VmValue` is `Rc`-heavy and tagged, so only `Int`/`Float`/ |
| 48 | + `Bool` unbox cleanly into registers. The JIT keeps locals as `VmValue` in the |
| 49 | + register file and emits `extern "C"` calls to shared runtime helpers for |
| 50 | + construction/inspection and all non-numeric ops. Pure numeric/control regions |
| 51 | + run native; the rest are calls — still removes interpreter dispatch overhead. |
| 52 | +- **Per-function dispatch:** compile `jit_plan().eligible_functions`; everything |
| 53 | + else falls back (invariant 2). |
| 54 | +- **Gate:** add the real `Jit` backend, force-JIT CI mode, run the full corpus + |
| 55 | + generative differential three-way. |
| 56 | + |
| 57 | +## Phase 3 — tiering, deopt, optimization |
| 58 | + |
| 59 | +- **Tiering / OSR:** hot-loop counters trigger tier-up; on-stack replacement to |
| 60 | + enter/leave compiled code mid-loop. |
| 61 | +- **Deopt:** type guards that bail to the interpreter with exact state |
| 62 | + reconstruction. The highest-risk part — test by **deopting at every guard** and |
| 63 | + comparing to the interpreter (`{jit-off, tier0, tier1, force-deopt}` must all |
| 64 | + agree). |
| 65 | +- **Optimizations:** constant folding, intrinsic inlining, register allocation — |
| 66 | + one at a time, each behind the differential gate. |
| 67 | +- **Coverage-guided fuzz:** a `seed(bytes) -> program` decoder + mutators with |
| 68 | + coverage feedback (Fuzzilli-style), run nightly, to reach deep tiering/deopt |
| 69 | + paths. Generators should deliberately emit hot loops, megamorphic calls, |
| 70 | + guard-failing inputs, and high register pressure. |
| 71 | + |
| 72 | +## Don't |
| 73 | + |
| 74 | +- Don't reimplement instruction semantics in the JIT (breaks invariant 1). |
| 75 | +- Don't add an optimization without the N-way differential + generative fuzz |
| 76 | + passing. |
| 77 | +- Don't widen the JIT-supported instruction set without extending the generator |
| 78 | + to exercise it. |
0 commit comments