Skip to content

Commit d7b4d15

Browse files
Haofeiclaude
andcommitted
Add JIT roadmap (native tier design + verification invariants)
Documents the path from the tier-0 seam to native codegen / tiering / deopt, the no-gap invariants each tier must hold, and why native must be a separate crate (forbid(unsafe)). The implementable foundation (Phase 0 + tier-0) is done; this lays out Phases 2-3 with the seams identified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b831f77 commit d7b4d15

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

docs/jit-roadmap.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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

Comments
 (0)