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:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user