vm-ext: phase E — JIT skips lambdas containing extension opcodes

Adds Sx_vm.bytecode_uses_extension_opcodes — an operand-aware
bytecode scanner that walks past CONST u16, CALL_PRIM u16+u8, and
CLOSURE u16+dynamic upvalue descriptors so operand bytes that happen
to be ≥200 don't false-positive as extension opcodes.

jit_compile_lambda calls the scanner on the inner closure's bytecode.
On hit it returns None — the lambda then runs through CEK
interpretation. The VM's dispatch fallthrough still routes the
extension opcodes themselves through the registry; this change just
prevents the JIT from claiming code it has no plan for.

Tests: 7 new foundation cases — pure core eligible, head/middle/
post-CLOSURE detection, CONST + CALL_PRIM + CLOSURE-descriptor false-
positive avoidance. +7 pass vs Phase D baseline, no regressions
across 11 conformance suites.

Loop complete: acceptance criteria 1-4 met. Hand-off to the Erlang
loop — lib/erlang/vm/dispatcher.sx's Phase 9b stub can now be
replaced with a real hosts/ocaml/lib/extensions/erlang.ml consumer.
This commit is contained in:
2026-05-15 01:53:39 +00:00
parent f3192f7fda
commit f026177e63
3 changed files with 184 additions and 7 deletions

View File

@@ -8,7 +8,10 @@ future language port that wants performance-critical opcodes.
Reference: `plans/erlang-on-sx.md` Phase 9, `plans/fed-sx-design.md` §17.5,
`hosts/ocaml/lib/sx_vm.ml` (current VM).
Status: **in progress** on `loops/sx-vm-extensions`.
Status: **complete** on `loops/sx-vm-extensions` (Phases A-E landed
2026-05-14 / 2026-05-15). Ready for first real consumer
(`hosts/ocaml/lib/extensions/erlang.ml`, replacing the Phase 9b stub
dispatcher in `lib/erlang/vm/dispatcher.sx`).
---
@@ -317,9 +320,9 @@ The JIT (lazy lambda compilation) currently compiles based on opcode ranges.
Extension opcodes (≥200) should fall through to interpretation, not be
JIT-compiled in v1.
- [ ] Mark extension opcodes as "interpret only" in the JIT pre-analysis.
- [ ] Lambda containing only core opcodes JIT-compiles as before.
- [ ] Lambda containing any extension opcode runs interpreted.
- [x] Mark extension opcodes as "interpret only" in the JIT pre-analysis.
- [x] Lambda containing only core opcodes JIT-compiles as before.
- [x] Lambda containing any extension opcode runs interpreted.
JITing extension opcodes is a follow-up project; v1 keeps the JIT scope
unchanged and just makes it correctly route mixed bytecode.
@@ -457,6 +460,29 @@ familiarity.
Newest first.
- **2026-05-15** — Phase E done. Loop complete (acceptance criteria
1-4 all met). New `Sx_vm.bytecode_uses_extension_opcodes` walks
bytecode operand-aware (CONST u16 indices, CALL_PRIM u16+u8,
CLOSURE u16+dynamic upvalue descriptors) so values that happen to
be ≥200 don't false-positive as extension opcodes. Wired into
`jit_compile_lambda`: when the inner closure's bytecode contains
any extension opcode, JIT returns None and the lambda runs
interpreted via CEK (the dispatch fallthrough still routes
extension opcodes through the registry — this just prevents the
JIT from claiming ownership of code it can't optimise). 7 new
foundation tests (`jit extension-opcode awareness` suite): pure
core eligible, head/middle/post-CLOSURE detection, CONST + CALL_PRIM
+ CLOSURE-descriptor false-positive avoidance. +7 pass vs Phase D
baseline (4833 vs 4826), 1111 pre-existing failures unchanged.
Conformance suites green: erlang 530/530, haskell 285/285, datalog
276/276, prolog 590/590, smalltalk 847/847, common-lisp 487/487,
apl 562/562, js 148/148, forth 632/638 (pre-existing), tcl 3/4
(pre-existing), ocaml-on-sx unit 607/607.
Loop done. Hand-off: the Erlang loop's Phase 9b stub dispatcher in
`lib/erlang/vm/dispatcher.sx` can now be replaced with a real
`hosts/ocaml/lib/extensions/erlang.ml` consumer.
- **2026-05-15** — Phase D done. New `hosts/ocaml/lib/extensions/` subtree
wired into the `sx` library via `(include_subdirs unqualified)`.
`extensions/test_ext.ml` is the canonical worked example: two