Merge loops/sx-vm-extensions into architecture: serving-JIT perform-in-HO-callback fix

Fixes the silent miscompile under SX_SERVING_JIT=1 (http-listen + cek_run_with_io):
a perform inside a native HO-primitive callback (map/filter/reduce/for-each)
unwound the native loop, corrupting the stack so the next CALL_PRIM read garbage
args (map/rest/drop). (A) call_closure_reuse resolves callback IO inline in
serving mode so the loop survives; (A') resume_vm restores _active_vm; (B)
register_jit_hook resolve_loop falls back to CEK on resume error (no 500).
Repro 9/9 (hosts/ocaml/bin/repro_jit_resume.ml); conformance unchanged 4834/1110.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 19:52:22 +00:00
4 changed files with 287 additions and 27 deletions

View File

@@ -1524,7 +1524,23 @@ let register_jit_hook env =
let rec resolve_loop req vm =
let result = resolver req (Nil) in
(try Some (Sx_vm.resume_vm vm result)
with Sx_vm.VmSuspended (req2, vm2) -> resolve_loop req2 vm2)
with
| Sx_vm.VmSuspended (req2, vm2) -> resolve_loop req2 vm2
| e ->
(* (B) Resume raised mid-execution. resolve_loop runs inside
the VmSuspended handler, so without catching here the
error escapes to the http handler (→ 500). Recover THIS
call on the CEK instead: mark jit_failed and return None
so the interpreter re-runs it (idempotent for the host's
durable reads). Self-heals on the first hit, not a retry. *)
let fn_name = match l.l_name with Some n -> n | None -> "?" in
if not (Hashtbl.mem _jit_warned fn_name) then begin
Hashtbl.replace _jit_warned fn_name true;
Printf.eprintf "[jit] %s resume fallback to CEK: %s\n%!"
fn_name (Printexc.to_string e)
end;
l.l_compiled <- Some Sx_vm.jit_failed_sentinel;
None)
in
resolve_loop request saved_vm
| None -> Some (make_vm_suspend_marker request saved_vm))
@@ -1557,7 +1573,16 @@ let register_jit_hook env =
let rec resolve_loop req vm =
let result = resolver req (Nil) in
(try Some (Sx_vm.resume_vm vm result)
with Sx_vm.VmSuspended (req2, vm2) -> resolve_loop req2 vm2)
with
| Sx_vm.VmSuspended (req2, vm2) -> resolve_loop req2 vm2
| e ->
(* (B) See note above — recover a failed resume on the
CEK instead of escaping to the handler (→ 500). *)
Printf.eprintf "[jit] %s resume fallback to CEK: %s\n%!"
fn_name (Printexc.to_string e);
Hashtbl.replace _jit_warned fn_name true;
l.l_compiled <- Some Sx_vm.jit_failed_sentinel;
None)
in
resolve_loop request saved_vm
| None -> Some (make_vm_suspend_marker request saved_vm))