vm-ext: enable JIT in epoch serving mode (Smalltalk 847/847, Datalog 356/356)
register_jit_hook is now installed in the persistent (epoch) serving-mode branch of sx_server.ml, not just --http/cli/site. Smalltalk-on-SX conformance under JIT is 847/847 — identical to the no-JIT baseline; Datalog 356/356. run_tests --jit/no-jit are byte-identical before/after (no regression). Five distinct root causes fixed (not one "miscompile"): 1. Serving mode never loaded lib/compiler.sx, so JIT used the native Sx_compiler.compile stub (arity-0 bytecode, params as GLOBAL_GET → "VM undefined: <param>"). Server-mode branch now loads compiler.sx before registering the hook, matching http/cli/site. 2. compile-cond / compile-case-clauses / compile-guard-clauses only treated keyword :else and true as the catch-all, not the bare symbol `else` that the CEK's is-else-clause? accepts → GLOBAL_GET "else". (lib/compiler.sx) 3. OP_DIV produced a float for non-divisible Integer/Integer (1/2 → 0.5) instead of the exact Rational the "/" primitive returns. Now delegates to the primitive, matching CEK. (sx_vm.ml) 4. OP_EQ / _fast_eq lacked Rational/ListRef cases that the "=" primitive's safe_eq has → (= 1/2 1/2) false under JIT. OP_EQ now delegates non-scalars to the "=" primitive; _fast_eq gained rational + ListRef. (sx_vm.ml, sx_runtime.ml) 5. Continuation-based control flow (Smalltalk ^expr non-local return, block escape, exceptions via call/cc) can't run in the stack VM. New data-driven exclusion set Sx_types.jit_excluded + `jit-exclude!` primitive, consulted in jit_compile_lambda (covers both the CEK hook and vm_call's tiered path). lib/smalltalk/eval.sx self-declares its continuation dispatch core interpret-only; pure helpers still JIT. The SUnit suite-runner test helper pharo-test-class miscompiles mid-loop and is excluded in tests/tokenize.sx. Also adds SX_JIT_DENY / SX_JIT_ONLY env-var bisection filters to the serving hook. Known residual documented in plans/jit-bytecode-correctness.md: the hook re-runs a failed VM execution via CEK (correct result, possible duplicate side effects); adopting run_tests' propagate-don't-rerun semantics is deferred to avoid changing shared VM/CEK behavior under this loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1160,6 +1160,22 @@ let sx_render_to_html expr env =
|
||||
|
||||
let _jit_warned : (string, bool) Hashtbl.t = Hashtbl.create 16
|
||||
|
||||
(* Bisection aid: env-var-driven JIT filter. Lets us narrow which named
|
||||
lambda the VM miscompiles without rebuilding.
|
||||
SX_JIT_DENY=name1,name2 — never JIT these (substring match on exact name).
|
||||
SX_JIT_ONLY=name1,name2 — JIT ONLY these (exact name); skip all others. *)
|
||||
let _jit_deny_set =
|
||||
match Sys.getenv_opt "SX_JIT_DENY" with
|
||||
| None | Some "" -> []
|
||||
| Some s -> String.split_on_char ',' s |> List.map String.trim
|
||||
let _jit_only_set =
|
||||
match Sys.getenv_opt "SX_JIT_ONLY" with
|
||||
| None | Some "" -> []
|
||||
| Some s -> String.split_on_char ',' s |> List.map String.trim
|
||||
let _jit_name_allowed name =
|
||||
(not (List.mem name _jit_deny_set))
|
||||
&& (match _jit_only_set with [] -> true | only -> List.mem name only)
|
||||
|
||||
let rec make_vm_suspend_marker request saved_vm =
|
||||
let d = Hashtbl.create 3 in
|
||||
Hashtbl.replace d "__vm_suspended" (Bool true);
|
||||
@@ -1178,6 +1194,8 @@ let rec make_vm_suspend_marker request saved_vm =
|
||||
let register_jit_hook env =
|
||||
Sx_runtime._jit_try_call_fn := Some (fun f args ->
|
||||
match f with
|
||||
| Lambda l when (match l.l_name with Some n -> not (_jit_name_allowed n) | None -> false) ->
|
||||
None (* bisection filter excluded this name *)
|
||||
| Lambda l ->
|
||||
(match l.l_compiled with
|
||||
| Some cl when not (Sx_vm.is_jit_failed cl) ->
|
||||
@@ -4538,6 +4556,29 @@ let () =
|
||||
else begin
|
||||
(* Normal persistent server mode *)
|
||||
let env = make_server_env () in
|
||||
(* JIT needs the SX bytecode compiler (lib/compiler.sx) as its `compile`
|
||||
binding — the native Sx_compiler.compile is an incomplete stub that
|
||||
miscompiles parameters (emits arity-0 bytecode with params as
|
||||
GLOBAL_GET). http/cli/site modes already load compiler.sx; the
|
||||
persistent (epoch) serving mode must too before enabling the hook,
|
||||
or every JIT-compiled function fails at runtime with "VM undefined:
|
||||
<param>" and falls back to CEK (with double-executed side effects). *)
|
||||
(_import_env := Some env;
|
||||
let project_dir = try Sys.getenv "SX_PROJECT_DIR" with Not_found ->
|
||||
try Sys.getenv "SX_ROOT" with Not_found ->
|
||||
if Sys.file_exists "/app/spec" then "/app" else Sys.getcwd () in
|
||||
let lib_base = try Sys.getenv "SX_LIB_DIR" with Not_found ->
|
||||
project_dir ^ "/lib" in
|
||||
let compiler_path = lib_base ^ "/compiler.sx" in
|
||||
let compiler_path =
|
||||
if Sys.file_exists compiler_path then compiler_path
|
||||
else if Sys.file_exists "lib/compiler.sx" then "lib/compiler.sx"
|
||||
else compiler_path in
|
||||
try load_library_file compiler_path; rebind_host_extensions env
|
||||
with exn ->
|
||||
Printf.eprintf "[sx-server] WARNING: failed to load compiler.sx for JIT (%s) — JIT disabled\n%!"
|
||||
(Printexc.to_string exn));
|
||||
register_jit_hook env;
|
||||
send "(ready)";
|
||||
(* Main command loop *)
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user