diff --git a/hosts/ocaml/lib/sx_vm.ml b/hosts/ocaml/lib/sx_vm.ml index c9b32647..ef0344af 100644 --- a/hosts/ocaml/lib/sx_vm.ml +++ b/hosts/ocaml/lib/sx_vm.ml @@ -582,10 +582,30 @@ let jit_compile_lambda (l : lambda) globals = | _ -> (* Compiler loaded from source — call through CEK *) Sx_ref.eval_expr (List [compile_fn; quoted]) (Env (make_env ())) in - (* Don't inject closure bindings into globals — GLOBAL_GET falls through - to vm_closure_env which has LIVE bindings. Injecting creates stale - snapshots that break mutable closure variables (set! on pos, etc.). *) - let effective_globals = globals in + (* Inject closure bindings into globals so GLOBAL_GET can find them. + Only injects values not already present in globals (preserves + existing defines). Mutable closure vars get stale snapshots here + but GLOBAL_SET writes back to vm_closure_env, and GLOBAL_GET + falls through to vm_closure_env if the global is stale. *) + let effective_globals = + let closure = l.l_closure in + let count = ref 0 in + let rec inject env = + Hashtbl.iter (fun id v -> + let name = Sx_types.unintern id in + if not (Hashtbl.mem globals name) then begin + Hashtbl.replace globals name v; + incr count + end + ) env.bindings; + match env.parent with Some p -> inject p | None -> () + in + if Hashtbl.length closure.bindings > 0 || closure.parent <> None then + inject closure; + if !count > 0 then + Printf.eprintf "[jit] %s: injected %d closure bindings\n%!" fn_name !count; + globals + in (match result with | Dict d when Hashtbl.mem d "bytecode" -> let outer_code = code_from_value result in