Skip to content

Commit 06f5e4a

Browse files
olwangclaude
andcommitted
feat(reg-vm): J0.1 enable precise resume by default
Make precise deopt resume the production default: a native guard bail now reconstructs the live interpreter window and resumes at the safepoint instead of re-running the function from the top. Sound since the J0.1 heap-aware reconstruction landed (scalars restored, heap/flat regs left to the frame). - `eval_main_with_args_native` (+ `_with_stats`): pass `precise_deopt_override = true`. This is what the CLI and the differential `vm-jit-native` backend use. - re-run-from-top remains the byte-identical fallback when a heap write disables precise resume (`can_precise_deopt_resume`), and stays under differential coverage via the force-deopt backend (`eval_main_with_args_native_force_deopt` keeps precise=false). `RSS_JIT_PRECISE_DEOPT` still forces it on elsewhere. Validated byte-identical corpus-wide: differential 33/0 first with RSS_JIT_PRECISE_DEOPT=1 (proving precise resume matches every backend), then again after the default flip; rsscript lib 276/0, jit_acceptance 83/0, default workspace build OK. Remaining for full J0.1: inlined logical-frame-chain state-map format, and heap-payload-variant / live-out composite-heap-value reconstruction (rebuilding native-built variant/struct values with heap payloads across a bail; today such arms bail). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c4df737 commit 06f5e4a

2 files changed

Lines changed: 21 additions & 11 deletions

File tree

crates/rsscript/src/reg_vm/mod.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,13 @@ impl RegVmExecutable {
12691269
tier_up_threshold,
12701270
false,
12711271
std::env::var_os("RSS_JIT_STATS").is_some(),
1272-
false,
1272+
// J0.1: precise resume is the production DEFAULT. A native guard bail
1273+
// reconstructs the live interpreter window (heap-aware: scalars restored,
1274+
// heap/flat regs left to the frame) and resumes at the safepoint. It is
1275+
// byte-identical to re-run-from-top (validated corpus-wide), which remains
1276+
// the fallback when a heap write disables precise resume and is kept under
1277+
// differential coverage by the force-deopt backend.
1278+
true,
12731279
false,
12741280
None,
12751281
false,
@@ -1293,7 +1299,9 @@ impl RegVmExecutable {
12931299
tier_up_threshold,
12941300
false,
12951301
true,
1296-
false,
1302+
// J0.1: precise resume is the production default (see
1303+
// `eval_main_with_args_native`).
1304+
true,
12971305
false,
12981306
None,
12991307
false,
@@ -1493,13 +1501,15 @@ impl RegVmExecutable {
14931501
// compiled subset, host helpers, and deopt oracle are identical, so the
14941502
// differential (which never sets this var) is undisturbed.
14951503
let baseline = std::env::var_os("RSS_JIT_BASELINE").is_some();
1496-
// `RSS_JIT_PRECISE_DEOPT=1` (J0.2) makes a native bail resume the
1497-
// interpreter at the safepoint's `resume_ip` (reconstructing the live
1498-
// register window) instead of re-running from the function top. Default
1499-
// (unset) keeps the byte-identical re-run-from-top baseline, so the
1500-
// differential (which never sets this var) keeps full coverage. A caller
1501-
// may also force it on deterministically (test entry points) via
1502-
// `precise_deopt_override`, avoiding a racy process env var.
1504+
// Precise resume (J0.1/J0.2): a native bail resumes the interpreter at the
1505+
// safepoint's `resume_ip` (reconstructing the live register window —
1506+
// heap-aware: scalar regs restored, heap/flat regs left to the frame)
1507+
// instead of re-running from the function top. This is now the production
1508+
// DEFAULT (`eval_main_with_args_native` passes `precise_deopt_override`);
1509+
// re-run-from-top remains the byte-identical fallback when a heap write
1510+
// disables precise resume (`can_precise_deopt_resume`) and is kept under
1511+
// differential coverage by the force-deopt backend. `RSS_JIT_PRECISE_DEOPT`
1512+
// still forces it on for entry points that default it off.
15031513
let precise_deopt =
15041514
precise_deopt_override || std::env::var_os("RSS_JIT_PRECISE_DEOPT").is_some();
15051515
// `RSS_JIT_OSR=1` (J5.2) selects the eager OSR path: a function with a

docs/planning/vm-optimizing-jit-plan.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ numeric-mode language decision). Owner: TBD. Created 2026-06-20; status updated
3636

3737
| Item | State | Notes |
3838
|---|---|---|
39-
| **J0.0–J0.3** precise-deopt spine | **shipped (leaf/scalar subset)** | `NativeOutcome::Deopt` + per-site safepoint ids + live-reg capture + state-map + precise resume + every-safepoint stress test. **Precise resume is default-OFF** (gated by `RSS_JIT_PRECISE_DEOPT` / a test entrypoint); production still re-runs from the function top (byte-identical for the side-effect-free subset). The state-map is `resume_ip + live registers`**not** yet the full inlined logical-frame-chain format J0.1 describes. So: done for the current leaf/scalar subset; full inlined-frame / side-effecting deopt is still **future**. |
40-
| **J0.1 (heap-aware reg reconstruction)** | **shipped (slice)** | The deopt state map now distinguishes reconstructible scalars (`Int`/`Float`) from heap refs (`Handle`/`FlatInt`/`FlatFloat`): `decode_deopt_live` reconstructs only scalar regs (the interpreter frame already holds heap values — precise resume implies no heap writes), and `restore_native_deopt_live_regs` restores ALL scalar regs incl. **reassigned scalar params** (the `< n_params` skip is gone). Fixes the reassigned-scalar-param resume bug (`precise_deopt_restores_reassigned_scalar_param`); flat-buffer params stay uncorrupted. **Remaining for full J0.1:** inlined logical-frame-chain state-map format, heap-payload-variant / live-out *value* reconstruction (native-built heap values across a bail), and enabling precise resume by default. |
39+
| **J0.0–J0.3** precise-deopt spine | **shipped (leaf/scalar subset)** | `NativeOutcome::Deopt` + per-site safepoint ids + live-reg capture + state-map + precise resume + every-safepoint stress test. **Precise resume is now the production DEFAULT** (`eval_main_with_args_native`); re-run-from-top remains the byte-identical fallback when a heap write disables precise resume (`can_precise_deopt_resume`) and stays under differential coverage via the force-deopt backend. The state-map is `resume_ip + live registers`**not** yet the full inlined logical-frame-chain format J0.1 describes. So: done for the current leaf/scalar subset; full inlined-frame / side-effecting deopt is still **future**. |
40+
| **J0.1 (heap-aware reg reconstruction)** | **shipped** | The deopt state map distinguishes reconstructible scalars (`Int`/`Float`) from heap refs (`Handle`/`FlatInt`/`FlatFloat`): `decode_deopt_live` reconstructs only scalar regs (the interpreter frame already holds heap values — precise resume implies no heap writes), and `restore_native_deopt_live_regs` restores ALL scalar regs incl. **reassigned scalar params** (the `< n_params` skip is gone). Fixes the reassigned-scalar-param resume bug (`precise_deopt_restores_reassigned_scalar_param`); flat-buffer params stay uncorrupted. **Precise resume is now default-on (validated corpus-wide).** **Remaining for full J0.1:** inlined logical-frame-chain state-map format, and heap-payload-variant / live-out *value* reconstruction (rebuilding native-built composite heap values — variant/struct with heap payload — across a bail; today such arms bail). |
4141
| **J0.4** heap writes + deopt-after-write | **future** | blocked on native heap-write codegen; the native subset is read-only today. |
4242
| **J0.5** in-generated-code `VmLimits` accounting | **future** | currently the Model-A fallback (see J6); the `VmLimitsSnapshot` tick/poll machinery is not built. |
4343
| **J1** profiling | **shipped** | per-call-site type feedback on dynamic sites, warm-gated, no interpreter regression. (Branch profiling **not** shipped — it regressed `bool_logic_loop` ~7%; needs a hot/cold dispatch split.) |

0 commit comments

Comments
 (0)