Merge loops/sx-vm-extensions into architecture: serving-mode JIT (opt-in)

Brings the bytecode JIT to the persistent epoch serving mode, gated opt-in via
SX_SERVING_JIT=1 (default OFF → zero change for existing loops). Includes the
correctness fixes that make the JIT match the CEK interpreter, and the
interpret-only exclusions that keep continuation-based guest interpreters safe.

Kernel / shared:
- SX_SERVING_JIT gate in sx_server.ml (loads lib/compiler.sx + register_jit_hook
  only when opted in).
- compiler.sx-as-`compile` correctness: else-symbol in compile-cond/case/guard;
  OP_DIV rational; OP_EQ/_fast_eq rational+ListRef; callable? accepts VmClosure.
- Three composable interpret-only signals in jit_compile_lambda:
  (1) jit-exclude! name / "ns-*" prefix; (2) PUSH_HANDLER recursive bytecode
  scan (guard/handler-bind/Dream catch); (3) jit-exclude-callers-of! +
  code_refs_escaping_caller (call/cc-establishing form callers).

Per-guest interpret-only declarations in each guest runtime: smalltalk (dispatch
core + pharo-test-class), scheme (scheme-*/scm-*), erlang (er-*/erlang-*),
prolog (pl-*), common-lisp (cl-*/clos-* + condition-form callers), js
(js-*/jp-*), haskell (hk-*).

Verified under SX_SERVING_JIT=1 (== CEK, no hang): host 181, smalltalk 847,
scheme/flow 166, erlang 530, prolog 590/mod 390, haskell 285, common-lisp 487,
js 148, apl 152, datalog/forth/ocaml. run_tests --jit 4813/1131 (was 4809/1135,
improved), no-jit 4834/1110 (unchanged). Default-OFF gate => no loop regresses.
This commit is contained in:
2026-06-28 17:05:31 +00:00
6 changed files with 93 additions and 1 deletions

View File

@@ -764,3 +764,17 @@
;; a compiled frame can't transfer control through a CEK continuation. Exclude
;; the cl-/clos- namespaces from JIT. See Sx_types.jit_excluded_prefixes.
(jit-exclude! "cl-*" "clos-*")
;; cl-restart-case / cl-handler-case / cl-handler-bind wrap their body in
;; call/cc (restarts + non-local handler exit). Any function that CALLS one of
;; these (e.g. SX fixtures driving the condition system: parse-recover,
;; interactive-debugger) must also be interpret-only: JIT'ing such a caller
;; forces the call/cc form into a nested cek-run where the captured
;; continuation runs-to-completion-and-returns instead of escaping, so a
;; restart fails to abort and the body falls through (accumulation/no-abort).
(jit-exclude-callers-of! "cl-restart-case" "cl-handler-case" "cl-handler-bind")
;; Also the INVOKE side: cl-invoke-restart / cl-invoke-debugger / cl-signal
;; trigger the continuation escape; a JIT'd caller can't let the escape
;; propagate out of its frame (e.g. make-policy-debugger building a debugger
;; hook that invokes a restart). Mark their callers interpret-only too.
(jit-exclude-callers-of! "cl-invoke-restart" "cl-invoke-debugger" "cl-signal" "cl-error-with-debugger")