JIT allowlist + integration tests + --test mode + clean up debug logging
JIT allowlist (sx_server.ml): - Replace try-every-lambda strategy with StringSet allowlist. Only functions in the list get JIT compiled (compiler, parser, pure transforms). Render functions that need dynamic scope skip JIT entirely — no retry overhead, no silent fallbacks. - Add (jit-allow name) command for dynamic expansion from Python bridge. - JIT failures log once with "[jit] DISABLED fn — reason" then go silent. Standalone --test mode (sx_server.ml): - New --test flag loads full env (spec + adapters + compiler + signals), supports --eval and --load flags. Quick kernel testing without Docker. Example: dune exec bin/sx_server.exe -- --test --eval '(len HTML_TAGS)' Integration tests (integration_tests.ml): - New binary exercising the full rendering pipeline: loads spec + adapters into a server-like env, renders HTML via both native and SX adapter paths. - 26 tests: HTML tags, special forms (when/if/let), letrec with side effects, component rendering, eval-expr with HTML tag functions. - Would have caught the "Undefined symbol: div/lake/init" issues from the previous commit immediately without Docker. VM cleanup (sx_vm.ml): - Remove temporary debug logging (insn counter, call_closure counter, VmClosure depth tracking) added during debugging. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -908,6 +908,32 @@ let make_server_env () =
|
||||
CEK, cache result). cek_call checks this before CEK dispatch. *)
|
||||
let _jit_compiling = ref false (* re-entrancy guard *)
|
||||
|
||||
(** Functions allowed for JIT compilation. Others go straight to CEK.
|
||||
Populated with compiler internals at registration time; extended
|
||||
dynamically via (jit-allow name) command. *)
|
||||
module StringSet = Set.Make(String)
|
||||
let jit_allowlist = ref (StringSet.of_list [
|
||||
(* Compiler internals *)
|
||||
"compile"; "compile-module"; "compile-expr"; "compile-symbol";
|
||||
"compile-dict"; "compile-list"; "compile-if"; "compile-when";
|
||||
"compile-and"; "compile-or"; "compile-begin"; "compile-let";
|
||||
"compile-letrec"; "compile-lambda"; "compile-define"; "compile-set";
|
||||
"compile-quote"; "compile-cond"; "compile-case"; "compile-case-clauses";
|
||||
"compile-thread"; "compile-thread-step"; "compile-defcomp";
|
||||
"compile-defmacro"; "compile-quasiquote"; "compile-qq-expr";
|
||||
"compile-qq-list"; "compile-call";
|
||||
"make-emitter"; "make-pool"; "make-scope"; "pool-add";
|
||||
"scope-define-local"; "scope-resolve";
|
||||
"emit-byte"; "emit-u16"; "emit-i16"; "emit-op"; "emit-const";
|
||||
"current-offset"; "patch-i16";
|
||||
(* Parser *)
|
||||
"sx-parse"; "sx-serialize"; "sx-serialize-dict";
|
||||
(* Pure transforms *)
|
||||
"aser"; "resolve-nav-path"; "tw"; "serialize";
|
||||
"render-to-html"; "render-attrs"; "render-html-element";
|
||||
"merge-spread-attrs"; "cssx-process-token";
|
||||
"signal"; "computed"; "freeze-scope"; "freeze-signal";
|
||||
])
|
||||
|
||||
let register_jit_hook env =
|
||||
Sx_ref.jit_call_hook := Some (fun f args ->
|
||||
@@ -920,29 +946,28 @@ let register_jit_hook env =
|
||||
(try Some (Sx_vm.call_closure cl args cl.vm_env_ref)
|
||||
with e ->
|
||||
let fn_name = match l.l_name with Some n -> n | None -> "?" in
|
||||
Printf.eprintf "[jit-hook] VM FAIL %s: %s (disabling JIT)\n%!" fn_name (Printexc.to_string e);
|
||||
Printf.eprintf "[jit] DISABLED %s — %s\n%!" fn_name (Printexc.to_string e);
|
||||
l.l_compiled <- Some Sx_vm.jit_failed_sentinel;
|
||||
None)
|
||||
| Some _ -> None (* compile failed — CEK handles *)
|
||||
| Some _ -> None (* compile failed or disabled — CEK handles *)
|
||||
| None ->
|
||||
let fn_name = match l.l_name with Some n -> n | None -> "?" in
|
||||
if !_jit_compiling then None
|
||||
else if not (StringSet.mem fn_name !jit_allowlist) then None
|
||||
else begin
|
||||
let fn_name = match l.l_name with Some n -> n | None -> "?" in
|
||||
_jit_compiling := true;
|
||||
let t0 = Unix.gettimeofday () in
|
||||
let compiled = Sx_vm.jit_compile_lambda l env.bindings in
|
||||
let dt = Unix.gettimeofday () -. t0 in
|
||||
_jit_compiling := false;
|
||||
Printf.eprintf "[jit-hook] %s compile %s in %.3fs\n%!"
|
||||
Printf.eprintf "[jit] %s compile %s in %.3fs\n%!"
|
||||
fn_name (match compiled with Some _ -> "OK" | None -> "FAIL") dt;
|
||||
match compiled with
|
||||
| Some cl ->
|
||||
l.l_compiled <- Some cl;
|
||||
(* Run on VM, fall back to CEK on runtime error *)
|
||||
(try Some (Sx_vm.call_closure cl args cl.vm_env_ref)
|
||||
with e ->
|
||||
let fn_name = match l.l_name with Some n -> n | None -> "?" in
|
||||
Printf.eprintf "[jit-hook] VM FAIL (first call) %s: %s (disabling JIT)\n%!" fn_name (Printexc.to_string e);
|
||||
Printf.eprintf "[jit] DISABLED %s — %s\n%!" fn_name (Printexc.to_string e);
|
||||
l.l_compiled <- Some Sx_vm.jit_failed_sentinel;
|
||||
None)
|
||||
| None -> None
|
||||
@@ -1106,6 +1131,10 @@ let rec dispatch env cmd =
|
||||
Printf.eprintf "[jit] Pre-compiled %d compiler functions in %.3fs\n%!" !count dt;
|
||||
send_ok ()
|
||||
|
||||
| List [Symbol "jit-allow"; String name] ->
|
||||
jit_allowlist := StringSet.add name !jit_allowlist;
|
||||
send_ok ()
|
||||
|
||||
| List [Symbol "aser-slot"; String src] ->
|
||||
(* Expand ALL components server-side. Uses batch IO mode.
|
||||
Calls aser via CEK — the JIT hook compiles it on first call. *)
|
||||
@@ -1449,10 +1478,69 @@ let cli_mode mode =
|
||||
Printf.eprintf "Error: %s\n" (Printexc.to_string exn); exit 1)
|
||||
|
||||
|
||||
let test_mode () =
|
||||
let env = make_server_env () in
|
||||
(* Load full spec + adapter stack *)
|
||||
let base = try Sys.getenv "SX_SPEC_DIR" with Not_found -> "spec" in
|
||||
let web_base = try Sys.getenv "SX_WEB_DIR" with Not_found -> "web" in
|
||||
let files = [
|
||||
Filename.concat base "parser.sx";
|
||||
Filename.concat base "render.sx";
|
||||
Filename.concat base "compiler.sx";
|
||||
Filename.concat web_base "signals.sx";
|
||||
Filename.concat web_base "adapter-html.sx";
|
||||
Filename.concat web_base "adapter-sx.sx";
|
||||
] in
|
||||
cli_load_files env files;
|
||||
(* Register JIT *)
|
||||
register_jit_hook env;
|
||||
(* Load any --load files *)
|
||||
let load_files = ref [] in
|
||||
let eval_exprs = ref [] in
|
||||
let args = Array.to_list Sys.argv in
|
||||
let rec scan = function
|
||||
| "--load" :: path :: rest -> load_files := path :: !load_files; scan rest
|
||||
| "--eval" :: expr :: rest -> eval_exprs := expr :: !eval_exprs; scan rest
|
||||
| _ :: rest -> scan rest
|
||||
| [] -> ()
|
||||
in scan args;
|
||||
cli_load_files env (List.rev !load_files);
|
||||
if !eval_exprs <> [] then
|
||||
List.iter (fun src ->
|
||||
try
|
||||
let exprs = Sx_parser.parse_all src in
|
||||
let result = List.fold_left (fun _ e ->
|
||||
Sx_ref.eval_expr e (Env env)) Nil exprs in
|
||||
Printf.printf "%s\n%!" (serialize_value result)
|
||||
with
|
||||
| Eval_error msg -> Printf.eprintf "Error: %s\n%!" msg; exit 1
|
||||
| exn -> Printf.eprintf "Error: %s\n%!" (Printexc.to_string exn); exit 1
|
||||
) (List.rev !eval_exprs)
|
||||
else begin
|
||||
(* Read from stdin *)
|
||||
let buf = Buffer.create 4096 in
|
||||
(try while true do
|
||||
let line = input_line stdin in
|
||||
Buffer.add_string buf line; Buffer.add_char buf '\n'
|
||||
done with End_of_file -> ());
|
||||
let src = String.trim (Buffer.contents buf) in
|
||||
if src <> "" then begin
|
||||
try
|
||||
let exprs = Sx_parser.parse_all src in
|
||||
let result = List.fold_left (fun _ e ->
|
||||
Sx_ref.eval_expr e (Env env)) Nil exprs in
|
||||
Printf.printf "%s\n%!" (serialize_value result)
|
||||
with
|
||||
| Eval_error msg -> Printf.eprintf "Error: %s\n%!" msg; exit 1
|
||||
| exn -> Printf.eprintf "Error: %s\n%!" (Printexc.to_string exn); exit 1
|
||||
end
|
||||
end
|
||||
|
||||
let () =
|
||||
(* Check for CLI mode flags *)
|
||||
let args = Array.to_list Sys.argv in
|
||||
if List.mem "--render" args then cli_mode "render"
|
||||
if List.mem "--test" args then test_mode ()
|
||||
else if List.mem "--render" args then cli_mode "render"
|
||||
else if List.mem "--aser-slot" args then cli_mode "aser-slot"
|
||||
else if List.mem "--aser" args then cli_mode "aser"
|
||||
else begin
|
||||
|
||||
Reference in New Issue
Block a user