vm-ext: gate serving-JIT behind SX_SERVING_JIT + fix continuation-guest regressions
Enabling the epoch serving-mode JIT globally regressed continuation-based guest
interpreters (the epoch mode is the shared command channel every loop's
conformance runner uses). Two-part fix:
1. SAFE DEFAULT GATE. register_jit_hook in the persistent server branch is now
opt-in via SX_SERVING_JIT=1 (default OFF). Default behaviour is unchanged
(no JIT in epoch serving) → zero regression for sibling loops. The
content/Smalltalk page server opts in.
2. GENERAL FIXES + per-guest interpret-only declarations:
- callable? (sx_server/run_tests/integration_tests/mcp_tree) now accepts
VmClosure. A JIT-compiled higher-order function returns its inner closure
as a VmClosure; callable? previously rejected it, so scheme-apply's
(callable? proc) guard failed with "not a procedure: <vm:anon>".
- jit-exclude! gains a trailing-"*" namespace-prefix form
(Sx_types.jit_excluded_prefixes), the robust way to mark a whole guest
interpreter interpret-only (a name-list misses functions in extra files —
it left erlang's vm/dispatcher JIT'd and 13 tests short).
- Per-guest exclusions in each guest's runtime.sx:
scheme "scheme-*" "scm-*" erlang "er-*" "erlang-*"
prolog "pl-*" common-lisp "cl-*" "clos-*"
js "js-*" haskell "hk-*"
Verified under opt-in JIT (== CEK, no hang): smalltalk 847/847, scheme/flow
166/166, erlang 530/530, prolog 590/590, apl 152/152, js 147/148. Residual
(documented, protected by the default gate): common-lisp 6 fails in advanced
suites (parser-recovery/debugger/CLOS/MOP). lua (0/16) and tcl (3/4) fail
identically on CEK — pre-existing, not JIT. run_tests --jit/no-jit unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -757,4 +757,10 @@
|
||||
"format-arguments" args))))
|
||||
(cl-restart-case
|
||||
(fn () (cl-signal-obj obj cl-handler-stack))
|
||||
(list "continue" (list) (fn () nil))))))
|
||||
(list "continue" (list) (fn () nil))))))
|
||||
;; ── JIT interpret-only boundary ───────────────────────────────────────────
|
||||
;; The Common-Lisp evaluator implements block/return-from, catch/throw, and
|
||||
;; the condition system via non-local control (host continuations); under JIT
|
||||
;; 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-*")
|
||||
|
||||
@@ -1202,3 +1202,11 @@
|
||||
(= name "info") (er-bif-ets-info vs)
|
||||
:else (error
|
||||
(str "Erlang: undefined 'ets:" name "/" (len vs) "'")))))
|
||||
|
||||
;; ── JIT interpret-only boundary ───────────────────────────────────────────
|
||||
;; The Erlang evaluator (er-eval-* in transpile.sx + the vm/dispatcher) recurses
|
||||
;; over the AST and the scheduler/receive path captures call/cc continuations.
|
||||
;; Under JIT the recursive eval miscompiles into a non-terminating loop and the
|
||||
;; continuation path cannot transfer control. Exclude the whole er-/erlang-
|
||||
;; namespace (covers transpile, runtime, and vm/dispatcher in one declaration).
|
||||
(jit-exclude! "er-*" "erlang-*")
|
||||
|
||||
@@ -148,3 +148,9 @@
|
||||
(fn (acc i) (str acc (char-at buf i)))
|
||||
""
|
||||
(range off (string-length buf)))))))
|
||||
|
||||
;; ── JIT interpret-only boundary ───────────────────────────────────────────
|
||||
;; The Haskell evaluator (hk-eval and the lazy-thunk forcer) recurses deeply
|
||||
;; over the AST/graph; under JIT the recursive eval can miscompile into a
|
||||
;; non-terminating loop. Exclude the hk- namespace from JIT.
|
||||
(jit-exclude! "hk-*")
|
||||
|
||||
@@ -6994,3 +6994,9 @@
|
||||
(set! js-global-this js-global)
|
||||
|
||||
(dict-set! js-global "globalThis" js-global)
|
||||
|
||||
;; ── JIT interpret-only boundary ───────────────────────────────────────────
|
||||
;; The JS evaluator (transpile.sx) uses call/cc for control flow (exceptions,
|
||||
;; early return); a JIT-compiled frame can't escape through a CEK continuation.
|
||||
;; Exclude the js- namespace from JIT. See Sx_types.jit_excluded_prefixes.
|
||||
(jit-exclude! "js-*")
|
||||
|
||||
@@ -2792,3 +2792,10 @@
|
||||
{:cut false}
|
||||
(fn () (begin (dict-set! box :n (+ (dict-get box :n) 1)) false)))
|
||||
(dict-get box :n))))
|
||||
|
||||
;; ── JIT interpret-only boundary ───────────────────────────────────────────
|
||||
;; The Prolog resolution engine (pl-solve! and friends) recurses deeply over
|
||||
;; goals/clauses with backtracking; under JIT it miscompiles into a
|
||||
;; non-terminating loop (the suite never completes). Exclude the whole pl-
|
||||
;; namespace from JIT. See Sx_types.jit_excluded_prefixes.
|
||||
(jit-exclude! "pl-*")
|
||||
|
||||
@@ -647,3 +647,11 @@
|
||||
(raise (get outcome :value)))
|
||||
(:else outcome))))))))))
|
||||
env)))
|
||||
|
||||
;; ── JIT interpret-only boundary ───────────────────────────────────────────
|
||||
;; The Scheme evaluator uses call/cc, dynamic-wind, guard/raise and applies
|
||||
;; user procedures (which may be continuations or JIT-returned closures); a
|
||||
;; JIT-compiled frame cannot transfer control through a CEK continuation.
|
||||
;; Exclude the whole scheme-/scm- namespace from JIT (robust vs a name list,
|
||||
;; which misses functions in extra files). See Sx_types.jit_excluded_prefixes.
|
||||
(jit-exclude! "scheme-*" "scm-*")
|
||||
|
||||
Reference in New Issue
Block a user