Wire transpiled VM as active execute_module — 2644 tests pass

The transpiled VM (sx_vm_ref.ml, from lib/vm.sx) is now the ACTIVE
bytecode execution engine. sx_server.ml and sx_browser.ml call
Sx_vm_ref.execute_module instead of Sx_vm.execute_module.

Results:
- OCaml tests: 2644 passed, 0 failed
- WASM tests: 32 passed, 0 failed
- Browser: zero errors, zero warnings, islands hydrate
- Server: pages render, JIT compiles, all routes work

The VM logic now lives in ONE place: lib/vm.sx (SX).
OCaml gets it via transpilation (bootstrap_vm.py).
JS/browser gets it via bytecode compilation (compile-modules.js).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 13:34:11 +00:00
parent 54ee673050
commit e46cdf3d4d
15 changed files with 5278 additions and 2729 deletions

View File

@@ -1338,7 +1338,7 @@ let rec dispatch env cmd =
(try
let code = Sx_vm.code_from_value code_val in
let globals = env_to_vm_globals env in
let result = Sx_vm.execute_module code globals in
let result = Sx_vm_ref.execute_module code globals in
send_ok_value result
with
| Eval_error msg -> send_error msg
@@ -1353,7 +1353,7 @@ let rec dispatch env cmd =
let code = Sx_vm.code_from_value code_val in
(* VM uses the LIVE kernel env — defines go directly into it *)
let globals = env_to_vm_globals env in
let _result = Sx_vm.execute_module code globals in
let _result = Sx_vm_ref.execute_module code globals in
(* Copy defines back into env *)
Hashtbl.iter (fun k v -> Hashtbl.replace env.bindings (Sx_types.intern k) v) globals;
send_ok ()
@@ -1950,7 +1950,7 @@ let http_inject_shell_statics env static_dir sx_sxc =
let component_hash = Digest.string component_defs |> Digest.to_hex in
(* Compute per-file hashes for cache busting *)
let wasm_hash = file_hash (static_dir ^ "/wasm/sx_browser.bc.wasm.js") in
let platform_hash = file_hash (static_dir ^ "/wasm/sx-platform-2.js") in
let platform_hash = file_hash (static_dir ^ "/wasm/sx-platform.js") in
let sxbc_hash = sxbc_combined_hash (static_dir ^ "/wasm") in
(* Read CSS for inline injection *)
let css_file_names = get_app_list "css-files" ["basics.css"; "tw.css"] in

View File

@@ -72,9 +72,9 @@ SKIP = {
"vm-create-closure",
# Lambda accessors (native type)
"lambda?", "lambda-compiled", "lambda-set-compiled!", "lambda-name",
# JIT dispatch (platform-specific)
# JIT dispatch + active VM (platform-specific)
"*active-vm*", "*jit-compile-fn*",
"try-jit-call",
"try-jit-call", "vm-call-closure",
# Env access (used by env-walk)
"env-walk", "env-walk-set!",
# CEK interop
@@ -372,6 +372,13 @@ let vm_create_closure vm_val frame_val code_val =
VmClosure { vm_code = code; vm_upvalues = upvalues; vm_name = None;
vm_env_ref = m.vm_globals; vm_closure_env = f.vf_closure.vm_closure_env }
(* --- JIT sentinel --- *)
let _jit_failed_sentinel = {
vm_code = { vc_arity = -1; vc_locals = 0; vc_bytecode = [||]; vc_constants = [||] };
vm_upvalues = [||]; vm_name = Some "__jit_failed__"; vm_env_ref = Hashtbl.create 0; vm_closure_env = None
}
let _is_jit_failed cl = cl.vm_code.vc_arity = -1
(* --- Lambda accessors --- *)
let is_lambda v = match v with Lambda _ -> Bool true | _ -> Bool false
let lambda_compiled v = match v with
@@ -380,7 +387,7 @@ let lambda_compiled v = match v with
let lambda_set_compiled_b v c = match v with
| Lambda l -> (match c with
| VmClosure cl -> l.l_compiled <- Some cl; Nil
| String "jit-failed" -> l.l_compiled <- Some Sx_vm.jit_failed_sentinel; Nil
| String "jit-failed" -> l.l_compiled <- Some _jit_failed_sentinel; Nil
| _ -> l.l_compiled <- None; Nil)
| _ -> Nil
let lambda_name v = match v with
@@ -424,26 +431,45 @@ let env_walk_set_b env name value =
if find e then Nil else Nil
| _ -> Nil
(* --- Active VM tracking (module-level mutable state) --- *)
let _active_vm : vm_machine option ref = ref None
(* Forward ref — resolved after transpiled let rec block *)
let _vm_run_fn : (value -> value) ref = ref (fun _ -> Nil)
let _vm_call_fn : (value -> value -> value -> value) ref = ref (fun _ _ _ -> Nil)
(* vm-call-closure: creates fresh VM, runs closure, returns result *)
let vm_call_closure closure_val args globals =
let cl = unwrap_closure closure_val in
let prev_vm = !_active_vm in
let g = match globals with Dict d -> d | _ -> Hashtbl.create 0 in
let m = { vm_stack = Array.make 4096 Nil; vm_sp = 0;
vm_frames = []; vm_globals = g; vm_pending_cek = None } in
let vm_val = VmMachine m in
_active_vm := Some m;
ignore (vm_push_frame vm_val closure_val args);
(try ignore (!_vm_run_fn vm_val) with e -> _active_vm := prev_vm; raise e);
_active_vm := prev_vm;
vm_pop vm_val
(* --- JIT dispatch (platform-specific) --- *)
let try_jit_call vm_val f args =
let m = unwrap_vm vm_val in
match f with
| Lambda l ->
(match l.l_compiled with
| Some cl when not (Sx_vm.is_jit_failed cl) ->
(* Already compiled — run on VM *)
(try vm_push vm_val (Sx_vm.call_closure cl (to_ocaml_list args) cl.vm_env_ref)
| Some cl when not (_is_jit_failed cl) ->
(try vm_push vm_val (vm_call_closure (VmClosure cl) args (Dict cl.vm_env_ref))
with _ -> vm_push vm_val (cek_call_or_suspend vm_val f args))
| Some _ ->
(* Compile failed before — CEK fallback *)
vm_push vm_val (cek_call_or_suspend vm_val f args)
| None ->
if l.l_name <> None then begin
l.l_compiled <- Some Sx_vm.jit_failed_sentinel;
l.l_compiled <- Some _jit_failed_sentinel;
match !Sx_vm.jit_compile_ref l m.vm_globals with
| Some cl ->
l.l_compiled <- Some cl;
(try vm_push vm_val (Sx_vm.call_closure cl (to_ocaml_list args) cl.vm_env_ref)
(try vm_push vm_val (vm_call_closure (VmClosure cl) args (Dict cl.vm_env_ref))
with _ -> vm_push vm_val (cek_call_or_suspend vm_val f args))
| None ->
vm_push vm_val (cek_call_or_suspend vm_val f args)
@@ -536,7 +562,45 @@ def main():
bridge.stop()
output = PREAMBLE + "\n(* === Transpiled from lib/vm.sx === *)\n" + result + "\n"
fixups = """
(* Wire forward references to transpiled functions *)
let () = _vm_run_fn := vm_run
let () = _vm_call_fn := vm_call
(* ================================================================
Public API — matches Sx_vm interface for drop-in replacement
================================================================ *)
(** Execute a compiled module — entry point for load-sxbc, compile-blob. *)
let execute_module (code : vm_code) (globals : (string, value) Hashtbl.t) =
let cl = { vm_code = code; vm_upvalues = [||]; vm_name = Some "module";
vm_env_ref = globals; vm_closure_env = None } in
let m = { vm_stack = Array.make 4096 Nil; vm_sp = 0;
vm_frames = []; vm_globals = globals; vm_pending_cek = None } in
let vm_val = VmMachine m in
let frame = { vf_closure = cl; vf_ip = 0; vf_base = 0; vf_local_cells = Hashtbl.create 4 } in
for _ = 0 to code.vc_locals - 1 do
m.vm_stack.(m.vm_sp) <- Nil; m.vm_sp <- m.vm_sp + 1
done;
m.vm_frames <- [frame];
ignore (vm_run vm_val);
vm_pop vm_val
(** Execute a closure with args — entry point for JIT Lambda calls. *)
let call_closure (cl : vm_closure) (args : value list) (globals : (string, value) Hashtbl.t) =
vm_call_closure (VmClosure cl) (List args) (Dict globals)
(** Reexport code_from_value for callers *)
let code_from_value = code_from_value
(** Reexport jit refs *)
let jit_compile_ref = Sx_vm.jit_compile_ref
let jit_failed_sentinel = _jit_failed_sentinel
let is_jit_failed = _is_jit_failed
"""
output = PREAMBLE + "\n(* === Transpiled from lib/vm.sx === *)\n" + result + "\n" + fixups
# Write output
out_path = os.path.join(_HERE, "sx_vm_ref.ml")

View File

@@ -292,7 +292,7 @@ let api_eval_vm src_js =
| None -> env_get global_env "compile-module" in
let code_val = Sx_ref.trampoline (Sx_runtime.sx_call compile_fn [List exprs]) in
let code = Sx_vm.code_from_value code_val in
let result = Sx_vm.execute_module code _vm_globals in
let result = Sx_vm_ref.execute_module code _vm_globals in
(* Sync VM globals → CEK env so subsequent eval() calls see defines *)
Hashtbl.iter (fun name v ->
let id = intern name in
@@ -380,45 +380,44 @@ let sync_vm_to_env () =
end
) _vm_globals
(** Recursive suspension handler: resumes VM, catches further suspensions,
resolves imports locally when possible, otherwise returns JS suspension
objects that the platform's while loop can process. *)
let rec resume_with_suspensions vm result =
try
let v = Sx_vm.resume_vm vm result in
sync_vm_to_env ();
value_to_js v
with Sx_vm.VmSuspended (request, vm2) ->
handle_suspension request vm2
and handle_suspension request vm =
let op = match request with
| Dict d -> (match Hashtbl.find_opt d "op" with Some (String s) -> s | _ -> "")
| _ -> "" in
if op = "import" then
match handle_import_suspension request with
| Some result ->
(* Library already loaded — resume and handle further suspensions *)
resume_with_suspensions vm result
| None ->
(* Library not loaded — return suspension to JS for async fetch *)
Js.Unsafe.inject (make_js_suspension request (fun _result ->
resume_with_suspensions vm Nil))
else
Js.Unsafe.inject (make_js_suspension request (fun result ->
resume_with_suspensions vm result))
let api_load_module module_js =
try
let code_val = js_to_value module_js in
let code = Sx_vm.code_from_value code_val in
let _result = Sx_vm.execute_module code _vm_globals in
let _result = Sx_vm_ref.execute_module code _vm_globals in
sync_vm_to_env ();
Js.Unsafe.inject (Hashtbl.length _vm_globals)
with
| Sx_vm.VmSuspended (request, vm) ->
(* VM hit OP_PERFORM — check if we can resolve locally *)
let op = match request with
| Dict d -> (match Hashtbl.find_opt d "op" with Some (String s) -> s | _ -> "")
| _ -> "" in
if op = "import" then
match handle_import_suspension request with
| Some result ->
(* Library already loaded — resume VM and continue *)
(try
let final = Sx_vm.resume_vm vm result in
sync_vm_to_env ();
Js.Unsafe.inject (value_to_js final)
with Sx_vm.VmSuspended (req2, vm2) ->
make_js_suspension req2 (fun result ->
let v = Sx_vm.resume_vm vm2 result in
sync_vm_to_env ();
value_to_js v))
| None ->
(* Library not loaded — return suspension to JS for async fetch *)
make_js_suspension request (fun result ->
ignore result;
(* After JS loads the library file, resume the VM *)
let v = Sx_vm.resume_vm vm Nil in
sync_vm_to_env ();
value_to_js v)
else
make_js_suspension request (fun result ->
let v = Sx_vm.resume_vm vm result in
sync_vm_to_env ();
value_to_js v)
handle_suspension request vm
| Eval_error msg -> Js.Unsafe.inject (Js.string ("Error: " ^ msg))
| exn -> Js.Unsafe.inject (Js.string ("Error: " ^ Printexc.to_string exn))
@@ -629,7 +628,7 @@ let () =
in
let module_val = convert_code code_form in
let code = Sx_vm.code_from_value module_val in
let _result = Sx_vm.execute_module code _vm_globals in
let _result = Sx_vm_ref.execute_module code _vm_globals in
sync_vm_to_env ();
Number (float_of_int (Hashtbl.length _vm_globals))
| _ -> raise (Eval_error "load-sxbc: expected (sxbc version hash (code ...))"));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long