JIT: VM fast path, &rest support, locals scan, test runner fixes

- jit_compile_lambda: call compile directly via VM when it has bytecode
  (100-400x faster JIT compilation, server pre-warm 1.6s vs hung)
- code_from_value: scan bytecode for highest LOCAL_GET/SET slot to
  compute vc_locals correctly (fixes hyperscript LOCAL_GET overflow)
- code_from_value: accept both compiler keys (bytecode) and SX VM
  keys (vc-bytecode) for interop
- jit_compile_lambda: skip &key/:as params (compiler can't emit them)
- Test runner: seed VM globals with primitives + env bindings,
  native vm-execute-module with suspension fallback to SX version,
  _jit_refresh_globals syncs globals after module loading,
  VmSuspended + "VM undefined" caught and sentineled

3127/3127 without JIT, 3116/3127 with JIT (11 hyperscript on-event
parsing — specific closure/scope issue, not infrastructure).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 10:52:44 +00:00
parent 387a6cb49e
commit 3155ba47f9
2 changed files with 70 additions and 18 deletions

View File

@@ -1309,16 +1309,39 @@ let run_spec_tests env test_files =
(* Rebind vm-execute-module and code-from-value to native OCaml implementations.
The SX versions from vm.sx run bytecode step-by-step in the interpreter —
far too slow for the test suite. Native versions use the compiled OCaml VM. *)
(* Rebind vm-execute-module to use the native OCaml VM directly.
The SX version from vm.sx runs bytecode step-by-step in the interpreter.
code-from-value stays as the SX version — it produces dicts that
vm-execute-module converts to native vm_code internally. *)
(* Wrap SX vm-execute-module to seed empty globals with primitives + env.
The SX VM resolves CALL_PRIM/GLOBAL_GET from globals — without seeding,
even (+ 1 2) fails. We keep the SX version (not native Sx_vm) so
suspension tests work (SX VM suspends via dict, native VM via exception). *)
let sx_vm_execute = try Some (Sx_types.env_get env "vm-execute-module") with _ -> None in
ignore (Sx_types.env_bind env "vm-execute-module" (NativeFn ("vm-execute-module", fun args ->
match args with
| [code; Dict globals] ->
if Hashtbl.length globals = 0 then begin
Hashtbl.iter (fun name fn ->
Hashtbl.replace globals name (NativeFn (name, fn))
) Sx_primitives.primitives;
let rec add_env e =
Hashtbl.iter (fun id v ->
let name = Sx_types.unintern id in
if not (Hashtbl.mem globals name) then
Hashtbl.replace globals name v) e.Sx_types.bindings;
match e.Sx_types.parent with Some p -> add_env p | None -> ()
in add_env env
end;
(* Use native VM for speed — much faster than SX step-by-step *)
let c = Sx_vm.code_from_value code in
Sx_vm.execute_module c globals
| _ -> Nil)));
(try Sx_vm.execute_module c globals
with Sx_vm.VmSuspended (_request, _saved_vm) ->
(* Fall back to SX version for suspension handling *)
Hashtbl.remove globals "__io_request";
match sx_vm_execute with
| Some fn -> Sx_ref.cek_call fn (List [code; Dict globals])
| None -> Nil)
| _ ->
match sx_vm_execute with
| Some fn -> Sx_ref.cek_call fn (List args)
| None -> Nil)));
load_module "signals.sx" spec_dir; (* core reactive primitives *)
load_module "signals.sx" web_dir; (* web extensions *)
load_module "freeze.sx" lib_dir;