diff --git a/CLAUDE.md b/CLAUDE.md
index ad985b387..833faa19f 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -310,7 +310,7 @@ Most layout is obvious from `ls`. Non-obvious ones worth knowing:
- `utils/detect-dupe/` (in-repo dupe finder) and `utils/find-dupe/` (Claude-based judge that needs `daspkg install --root utils/find-dupe` + `ANTHROPIC_API_KEY`) — both also exposed as MCP tools
- `utils/mcp/`, `utils/daslang-live/`, `utils/daspkg/` — in-tree tools (most also have skills under `skills/`)
- `tutorials/language/` (language tour) vs `tutorials//` (per-area) — never put tutorials in `modules//tutorial/`
-- **Array/dim/vector indexing lives across 5 tiers** — bug fixes (bounds checks, neg-index detection, width-aware bounds) usually need parallel edits in all of them: AOT C++ ([include/daScript/simulate/aot.h](include/daScript/simulate/aot.h)), interpreter non-fused ([include/daScript/simulate/runtime_array.h](include/daScript/simulate/runtime_array.h) + [include/daScript/simulate/simulate_nodes.h](include/daScript/simulate/simulate_nodes.h)), interpreter fused ([src/simulate/simulate_fusion_at_array.cpp](src/simulate/simulate_fusion_at_array.cpp) + [src/simulate/simulate_fusion_at.cpp](src/simulate/simulate_fusion_at.cpp)), JIT ([modules/dasLLVM/daslib/llvm_jit.das](modules/dasLLVM/daslib/llvm_jit.das)), and AST const-fold ([src/ast/ast_simulate.cpp](src/ast/ast_simulate.cpp)). Debug builds bypass the fused permutations — a fix that lands only in the fused path passes Release CI and trips Debug-ARM. Bump `LLVM_JIT_CODEGEN_VERSION` in `modules/dasLLVM/daslib/llvm_macro.das` after JIT changes to invalidate cached `.dll`s
+- **Array/dim/vector indexing lives across 5 tiers** — bug fixes (bounds checks, neg-index detection, width-aware bounds) usually need parallel edits in all of them: AOT C++ ([include/daScript/simulate/aot.h](include/daScript/simulate/aot.h)), interpreter non-fused ([include/daScript/simulate/runtime_array.h](include/daScript/simulate/runtime_array.h) + [include/daScript/simulate/simulate_nodes.h](include/daScript/simulate/simulate_nodes.h)), interpreter fused ([src/simulate/simulate_fusion_at_array.cpp](src/simulate/simulate_fusion_at_array.cpp) + [src/simulate/simulate_fusion_at.cpp](src/simulate/simulate_fusion_at.cpp)), JIT ([modules/dasLLVM/daslib/llvm_jit.das](modules/dasLLVM/daslib/llvm_jit.das)), and AST const-fold ([src/ast/ast_simulate.cpp](src/ast/ast_simulate.cpp)). Debug builds bypass the fused permutations — a fix that lands only in the fused path passes Release CI and trips Debug-ARM. Bump `LLVM_JIT_CODEGEN_VERSION` in `modules/dasLLVM/daslib/llvm_jit_run.das` after JIT emitter/ABI changes to invalidate cached `.dll`s (the cache hash also folds per-function AOT hashes, so AST-level changes self-invalidate without a bump)
## MCP Server (AI Tool Integration)
diff --git a/QUOTE_LOWERING.md b/QUOTE_LOWERING.md
index 5249dd994..99e3a7fce 100644
--- a/QUOTE_LOWERING.md
+++ b/QUOTE_LOWERING.md
@@ -247,14 +247,94 @@ JIT work in scope before then is the Phase 1 diagnostic (clean `unsupported` mes
replacing the misleading "Internal jit error" panic). The QuotePass gate stays
`aot_macros`-only until this phase; extending it to `jit_enabled` lands here.
-- [ ] Extend QuotePass gate to `jit_enabled`.
-- [ ] `tests/jit_tests/` coverage for quote under `options jit` (lowered path through
- LlvmJitVisitor — make-struct of handled types, `to_array_move`, by-name lookups).
+- [x] Extend QuotePass gate to `jit_enabled` (2026-06-12). Two traps found on first contact,
+ both via `daslib/typemacro_boost`:
+ 1. **Wrapper return type must be the raw quote's type.** `fn.result = autoinfer` leaked
+ the concrete node type (`quote(0)` → `ExprConstInt?` instead of `Expression?`),
+ breaking `cond ? clone_expression(x) : quote(0)` (30411, cond arms must match
+ exactly). Fix: `fn.result = clone_type(expr._type)` — identity by construction.
+ 2. **Macro-module programs must not lower under the jit trigger.** Macro contexts are
+ never JITted (llvm_macro's `is_compiling_macros()` skip), and the lowered
+ construction frames overflowed the macro-context stack at apply time
+ (`stack overflow while calling @typemacro_boost::\`quote\`lowered\`28`). Predicate
+ gotcha: `is_compiling_macros()` reads `Program::isCompilingMacros`, which is only
+ true during `makeMacroModule`'s simulate — at infer time (when QuotePass runs) use
+ `prog.flags.needMacroModule` instead (set at annotation-apply, pre-infer). The
+ infer-time noAot-skip in ast_infer_type.cpp mirrors the same predicate. Raw quotes
+ reaching JIT from such modules fall back per-function via the new
+ DisableJitVisitor `preVisitExprQuote` (warn + disable, replacing whole-file panic).
+- [x] Coverage instead of new jit_tests: lifted the `-jit` folder skips (quote, ast,
+ ast_match, no_aot) in tests/.das_test + the matching `--exclude`s on
+ jit_cache_all_tests, and un-marked `[no_jit]` in tests/template/test_push_block_list
+ (its stated reason — JIT can't lower ExprQuote — is gone; qmacro_block now JITs).
+ Folder results: quote 20/20, ast 1/1, ast_match 380/380 (2 graceful per-function
+ fallbacks in daslib/ast_match — macro module, stays raw by design), flatten/no_aot
+ 14/14, template 10/10. The gc skip was lifted too, then RESTORED — see next item.
+- [x] NOT quote-related, surfaced (and re-buried) while lifting the gc skip: tests/gc is
+ unsound under JIT for two reasons. (1) **LLVM JIT does not implement
+ `force_escape_free` / `force_allocate_on_stack`** — heap grows where interp stays
+ flat; scope-exit free aborts "not a chunk pointer" on JIT-allocated owning nodes
+ under persistent_heap; nested `new Inner` make-struct field arrives null. (2) The
+ one Debug CI caught after the per-function `[no_jit]` round: **`heap_collect` cannot
+ see a heap pointer whose only reference is a local in a jitted frame** (native-stack
+ locals are invisible to the collector) — the object is freed as garbage, and the
+ test's later `delete` double-frees (Debug memory_model.h:109 assert; Release passes
+ by luck until the slot is reused — probe: interp prints n.x=7, jit prints n.x=-1,
+ with or without force_escape_free). (2) is systemic for GC-semantics tests, so the
+ folder-level `-jit` skip in tests/.das_test is restored (root cause in the comment)
+ and the scoped `[no_jit]` markers were reverted. Lift again only when the JIT spills
+ GC roots somewhere the collector can scan. Upstream fix is its own work item.
+- [x] Pre-existing CI infra bug exposed by the gc crash (NOT this PR's doing, heals once
+ the gc skip is back): when the main `-jit` sweep crashes, build.yml falls back to
+ `--isolated-mode` — 32 worker subprocesses all spawn with `-jit -jit`, whose harness
+ dll hash differs from the outer run's prewarmed one → every worker cache-misses the
+ SAME dll path simultaneously and they race writing the same `.o`/`.dll`: "file
+ format not recognized" (half-written .o), "failed to set dynamic section sizes:
+ file truncated" (rewritten mid-link), and the llvm_jit_run.das:342 post-write
+ verify assert (dll swapped between write and reopen). Cost 3 collateral
+ typer_errors "failures" on linux Debug. Fix candidates: lock/atomic-rename in
+ write_dll, or prewarm the worker-flag-combination dll before fan-out.
+- [x] Local full-tree `-jit` sweep caveat (NOT this PR's doing): the in-process sweep
+ aborts (exit 127, no diagnostic) at tests/language/table_operations.das
+ `ta_test_lock_panic` — a deliberate table-lock panic inside jitted code fails to
+ unwind to `recover` and kills the process. **Pre-existing**: reproduces with master
+ daslib state, and the content-addressed dll hash is identical with/without the
+ Phase 5 changes (same codegen input). Local-env specific (LLVM 22.1.5 Windows,
+ opt-level 3); master CI Windows -jit is green (different LLVM, plus the
+ `|| --isolated-mode` fallback built into the CI step). Local full-tree signal
+ obtained with `--exclude table_operations --exclude test_linq_table_source` (the
+ second file is just the next deliberate-panic test the abort moves to); CI is the
+ authoritative gate.
+- [x] **Third trap, found only by the full sweep (order-dependent linq failures):** a
+ module that is macro-CALLED but not itself a macro module (daslib/linq_fold_decs —
+ plain functions invoked from linq_boost's fold macros) has `needMacroModule ==
+ false`, so it DOES lower under the jit trigger — and its lowered quotes then
+ evaluate at macro-apply time on the calling macro module's macro-context stack:
+ `stack overflow while calling @linq_fold_decs::\`quote\`lowered\`17` → 31206 → the
+ consumer file fails to compile (tests/linq/test_linq_fold_terminal_select +
+ test_linq_from_decs). Order-dependent because the overflow margin depends on
+ module-cache state at compile time — single-folder runs passed, full-tree
+ root-form runs failed deterministically (bisect: my code at master folder set
+ still failed; zero-predecessor root form still failed → not contamination at all,
+ a per-file stack margin). FIX: `Program::makeMacroModule` sizes the MACRO context
+ to `max(getContextStackSize(), 1MB)` when `aot_macros || jit_enabled` — the
+ headroom lands exactly where lowered quotes evaluate at macro-apply time, for any
+ embedder, with no driver changes. First attempt was a `policies.stack = 1MB` bump
+ in the three jit drivers (main.cpp / dastest / jit.exe), mirroring the
+ `-aot-macros` flow — REGRESSION: `policies.stack` also sizes the PRODUCED
+ program's runtime context, so cross-compiled wasm executables tried to carve 1MB
+ out of wasm linear memory and trapped at runtime (CI `wasm_cross`, f2s.wasm
+ "thrown Wasm exception"). Reverted; the `-aot-macros` global bump keeps its
+ historical behavior. Durable fix remains chunking the lowered construction into
+ bounded frames (Phase 6 candidate) — that would let macro contexts drop the
+ special-casing entirely.
- [ ] Decide + implement JIT-of-macro-contexts: lift the `is_compiling_macros()` skip in
llvm_macro.das behind a policy. This is the compile-time payoff twin of AOT'd
macro modules. Separate PR; needs its own bake time.
-- [ ] `LLVM_JIT_CODEGEN_VERSION` bump only if emitters change (diagnostic-only changes
- don't).
+- [x] `LLVM_JIT_CODEGEN_VERSION` bump NOT needed: emitters unchanged (gate + collection
+ only), and the dll cache hash folds per-function AOT hashes, so lowered ASTs and
+ changed collection sets self-invalidate. (CLAUDE.md pointer fixed: the constant
+ lives in llvm_jit_run.das, not llvm_macro.das.)
## Phase 6 — extra coverage + payoff measurement
diff --git a/daslib/quote.das b/daslib/quote.das
index e9a0739c6..2b3c63b64 100644
--- a/daslib/quote.das
+++ b/daslib/quote.das
@@ -483,7 +483,10 @@ class QuoteConverter : AstVisitor {
}
var fn = new Function(at = expr.at, atDecl = expr.at, name := fname,
flags = FunctionFlags.generated | FunctionFlags.privateFunction)
- fn.result = new TypeDecl(baseType = Type.autoinfer)
+ // preserve the raw quote's static type (Expression?) — autoinfer would leak the
+ // concrete node type (e.g. ExprConstInt?) and break consumers that need type
+ // identity, e.g. `cond ? clone_expression(x) : quote(0)`
+ fn.result = expr._type != null ? clone_type(expr._type) : new TypeDecl(baseType = Type.autoinfer)
var blk = new ExprBlock(at = expr.at)
emplace_new(blk.list) <| new ExprReturn(at = expr.at, subexpr = conv)
fn.body = blk
@@ -497,9 +500,16 @@ class QuotePass : AstPassMacro {
//! Pass macro that processes quoted AST expressions.
def override apply(prog : ProgramPtr; mod : Module?) : bool {
// Unwrapping ExprQuote is slow and inefficient, do it only if necessary.
- // Triggered by the aot_macros policy (`daslang -aot -aot-macros`) or per-module
- // `options aot_macros` (self-contained tests, interpreted A/B).
- let lower = compiling_program().policies.aot_macros || (prog._options |> find_arg("aot_macros") ?as tBool ?? false)
+ // Triggered by the aot_macros policy (`daslang -aot -aot-macros`), the jit_enabled
+ // policy (lowered quotes codegen natively, raw ones can't), or per-module
+ // `options aot_macros` (self-contained tests). Macro-module programs are excluded
+ // from the jit trigger: macro contexts are never JITted (see llvm_macro's
+ // is_compiling_macros skip), and the lowered construction frames can overflow the
+ // macro-context stack at apply time. Raw quotes reaching JIT codegen from such
+ // modules fall back per-function via DisableJitVisitor.
+ let lower = (compiling_program().policies.aot_macros
+ || (compiling_program().policies.jit_enabled && !prog.flags.needMacroModule)
+ || (prog._options |> find_arg("aot_macros") ?as tBool ?? false))
if (!lower) return false // nothing to do
var astVisitor = new QuoteConverter(mod = compiling_module())
make_visitor(*astVisitor) $(astVisitorAdapter) {
diff --git a/modules/dasLLVM/daslib/llvm_jit.das b/modules/dasLLVM/daslib/llvm_jit.das
index 4b9d13254..0ecb11409 100644
--- a/modules/dasLLVM/daslib/llvm_jit.das
+++ b/modules/dasLLVM/daslib/llvm_jit.das
@@ -3391,7 +3391,7 @@ class public LlvmJitVisitor : AstVisitor {
// ExprQuote
def override preVisitExprQuote(expr : ExprQuote?) : void {
- unsupported(expr, "quote( ) is not jit-able without aot_macros lowering (daslib/quote)")
+ unsupported(expr, "raw quote( ) is not jit-able — daslib/quote lowering (jit_enabled / aot_macros gates) did not run for this module")
}
// ExprIfThenElse
@@ -7645,6 +7645,14 @@ class public DisableJitVisitor : AstVisitor {
}
}
+ def override preVisitExprQuote(expr : ExprQuote?) : void {
+ // jit_enabled programs get quotes lowered by daslib/quote's QuotePass before we
+ // ever run; a raw one means lowering didn't fire for the defining module (macro
+ // modules stay raw by design) — skip the function instead of failing the file
+ to_log(LOG_WARNING, "LLVM JIT: raw quote( ) — daslib/quote lowering (jit_enabled / aot_macros gates) did not run for this module at {expr.at}, falling back to interpreter.\n")
+ disable = true
+ }
+
def override preVisitExprForBody(expr : ExprFor?) : void {
for (_, ssrc in expr.iteratorVariables, expr.sources) {
assume ssrc_t = ssrc._type
diff --git a/skills/writing_tests.md b/skills/writing_tests.md
index 45a2c32e2..aa2e8bd30 100644
--- a/skills/writing_tests.md
+++ b/skills/writing_tests.md
@@ -21,7 +21,7 @@ directory filter below instead.)
`tests/.das_test` is a daslang script dastest compiles per run; its `can_visit_folder`
pinvoke gates whole directories per mode — e.g. `no_aot/`, `ast/`, `ast_match/` are
-skipped under `--use-aot` / `-jit`, module dirs (dasSQLITE, dasPUGIXML…) skip when the
+skipped under `--use-aot`, module dirs (dasSQLITE, dasPUGIXML…) skip when the
module isn't built in. **The filter is looked up only at the `--test` ROOT path** —
`--test tests` finds and applies it, but `--test tests/flatten` looks for
`tests/flatten/.das_test` (absent) and walks into `no_aot/` unfiltered, producing
@@ -33,14 +33,20 @@ false `error[50101]` / JIT failures. For AOT/JIT validation, sweep `--test tests
`tests/.das_test` defines `can_visit_folder(folder_name, result)` — dastest consults it
per subfolder during file collection (only for the `.das_test` at the `--test `
argument; directly naming a child folder bypasses it). It gates folders on module
-availability (`dasHV`, `dasSQLITE`, …) and on sweep mode by scanning argv for `-jit` /
-`--use-aot` (e.g. `ast`, `ast_match`, `no_aot`, `gc`, `quote` skip under `-jit` — runtime
-quote/qmacro the JIT can't codegen). Two traps: a whole-folder JIT/AOT failure usually
-means a missing entry here, NOT a per-file fix; and the `jit_cache_all_tests` prewarm
-target (utils/CMakeLists.txt) does NOT consult it — its `--exclude` list mirrors the
-`-jit` skips manually and must be updated in the same change. Per-function `[test, no_jit]`
-(tests/template/test_push_block_list.das) is the finer-grained alternative when only some
-tests in a kept folder can't JIT.
+availability (`dasHV`, `dasSQLITE`, …) and on sweep mode by scanning argv — `--use-aot`
+skips `ast`, `ast_match`, `no_aot`; `-jit` skips only `gc` (heap_collect can't see heap
+pointers whose only reference is a local in a jitted frame — native-stack locals are
+invisible to the collector, so GC-semantics tests are interp-only; the other former `-jit`
+skips were lifted once `jit_enabled` started triggering daslib/quote lowering). Two traps:
+a whole-folder JIT/AOT failure usually means a missing entry here, NOT a per-file fix; and
+the `jit_cache_all_tests` prewarm target (utils/CMakeLists.txt) does NOT consult it — its
+`--exclude` list mirrors the skips manually and must be updated in the same change.
+Per-function `[no_jit]` is the finer-grained alternative when only some functions in a
+kept folder can't JIT — put it on the function whose CODE diverges under JIT, not just the
+`[test]` wrapper (JITted callees replace their SimNode bodies, so an interpreted wrapper
+still calls jitted workers). Beware Release-blind divergence: memory bugs (double-free,
+reuse-after-collect) only trip the Debug memory_model.h assert, so a green local Release
+sweep does NOT prove a lifted skip is sound — Debug CI is the oracle.
## Test file structure
diff --git a/src/ast/ast_infer_type.cpp b/src/ast/ast_infer_type.cpp
index fa25ad6b1..905c0fe91 100644
--- a/src/ast/ast_infer_type.cpp
+++ b/src/ast/ast_infer_type.cpp
@@ -1438,9 +1438,12 @@ namespace das {
expr->type->firstType = new TypeDecl(Type::tHandle);
expr->type->firstType->annotation = (TypeAnnotation *)Module::require("ast_core")->findAnnotation("Expression");
// mark quote as noAot, unless daslib/quote lowering will replace it
- // (aot_macros policy or per-module `options aot_macros` — same gate as QuotePass)
+ // (aot_macros or jit_enabled policy, or per-module `options aot_macros` — same gate
+ // as QuotePass, including its macro-module exclusion for the jit trigger)
if (func) {
- if (!program->policies.aot_macros && !program->options.getBoolOption("aot_macros", false)) {
+ if (!program->policies.aot_macros
+ && !(program->policies.jit_enabled && !program->needMacroModule)
+ && !program->options.getBoolOption("aot_macros", false)) {
func->noAot = true;
}
}
diff --git a/src/ast/ast_simulate.cpp b/src/ast/ast_simulate.cpp
index effde64ea..5be767ce7 100644
--- a/src/ast/ast_simulate.cpp
+++ b/src/ast/ast_simulate.cpp
@@ -3532,7 +3532,16 @@ namespace das
void Program::makeMacroModule ( TextWriter & logs ) {
isCompilingMacros = true;
- thisModule->macroContext = get_context(getContextStackSize());
+ int macroStackSize = getContextStackSize();
+ if ( policies.aot_macros || policies.jit_enabled || options.getBoolOption("aot_macros", false) ) {
+ // quote lowering (daslib/quote) is active (same triggers as its QuotePass gate,
+ // including the per-module option): a lowered quote evaluates one large
+ // construction frame per quote, and macro-called functions evaluate theirs on
+ // THIS context's stack at macro-apply time. Size only the macro context — a
+ // global policies.stack bump would leak into produced exe/wasm runtime stacks.
+ macroStackSize = das::max(macroStackSize, 1 * 1024 * 1024);
+ }
+ thisModule->macroContext = get_context(macroStackSize);
thisModule->macroContext->category = uint32_t(das::ContextCategory::macro_context);
auto oldAot = policies.aot;
auto oldHeap = policies.persistent_heap;
diff --git a/tests/.das_test b/tests/.das_test
index e8ecd3772..cf0422277 100644
--- a/tests/.das_test
+++ b/tests/.das_test
@@ -39,26 +39,22 @@ def can_visit_folder(folder_name : string; var result : bool?) {
}
}
// A meta-test that requires the compiler frontend at runtime (e.g. daslib/flatten ->
- // macro_boost) can't AOT-link in the full-tree batch, and one that runs qmacro at runtime
- // (flatten_optimize re-entry) carries quote nodes LLVM JIT can't codegen — interp only.
- if (folder_name == "no_aot") {
+ // macro_boost) can't AOT-link in the full-tree batch. Under -jit it runs fine: quote( )
+ // nodes are lowered by daslib/quote when jit_enabled is set.
+ if (folder_name == "no_aot" || folder_name == "ast_match" || folder_name == "ast") {
let args <- get_command_line_arguments()
for (arg in args) {
- if (arg == "--use-aot" || arg == "-jit") {
- *result = false
- return
- }
- }
- }
- if (folder_name == "ast_match" || folder_name == "ast") {
- let args <- get_command_line_arguments()
- for (arg in args) {
- if (arg == "--use-aot" || arg == "-jit") {
+ if (arg == "--use-aot") {
*result = false
return
}
}
}
+ // GC tests are interp-only under JIT: jitted frames keep locals on the native stack,
+ // so heap_collect can't see a heap pointer whose only reference is a jitted local and
+ // frees it (double-free / reuse-after-collect; Debug memory_model.h assert catches it,
+ // Release passes by luck). Lift only once the JIT spills GC roots somewhere the
+ // collector can scan.
if (folder_name == "gc") {
let args <- get_command_line_arguments()
for (arg in args) {
@@ -68,16 +64,4 @@ def can_visit_folder(folder_name : string; var result : bool?) {
}
}
}
- // The A/B baseline fixture keeps raw quote( ) nodes LLVM JIT can't codegen.
- // AOT stays on (the lowered twins are the point). Lift with the JIT lowering lane.
- if (folder_name == "quote") {
- let args <- get_command_line_arguments()
- for (arg in args) {
- if (arg == "-jit") {
- *result = false
- return
- }
- }
- }
-
}
diff --git a/tests/template/test_push_block_list.das b/tests/template/test_push_block_list.das
index a94c7cfde..a2515dbca 100644
--- a/tests/template/test_push_block_list.das
+++ b/tests/template/test_push_block_list.das
@@ -11,16 +11,10 @@ require dastest/testing_boost public
// result into an ``array``, cloning each. Use in place of a
// cluster of ``stmts |> push <| qmacro_expr() { single_stmt }`` calls when
// emitting multi-statement snippets.
-//
-// All tests carry ``[no_jit]`` because ``qmacro_block`` codegens to an
-// ``ExprQuote`` wrapping a multi-statement block, which the LLVM JIT does
-// not lower (interp + AOT lanes handle it fine). Single-expression
-// ``qmacro(...)`` calls in the peer ``tests/ast_match/test_peel_lambda_*``
-// tests DO JIT — only the block form is affected.
// ── Multi-statement append ──────────────────────────────────────────────
-[test, no_jit]
+[test]
def test_appends_all_statements(t : T?) {
ast_gc_guard() {
var stmts : array
@@ -36,7 +30,7 @@ def test_appends_all_statements(t : T?) {
// ── Single-statement (degenerate but legal) ────────────────────────────
-[test, no_jit]
+[test]
def test_single_statement(t : T?) {
ast_gc_guard() {
var stmts : array
@@ -50,7 +44,7 @@ def test_single_statement(t : T?) {
// ── Composes with prior + subsequent pushes ────────────────────────────
-[test, no_jit]
+[test]
def test_interleaves_with_other_pushes(t : T?) {
ast_gc_guard() {
var stmts : array
@@ -71,7 +65,7 @@ def test_interleaves_with_other_pushes(t : T?) {
// ── Pipe form ───────────────────────────────────────────────────────────
-[test, no_jit]
+[test]
def test_pipe_form(t : T?) {
ast_gc_guard() {
var stmts : array
@@ -86,7 +80,7 @@ def test_pipe_form(t : T?) {
// ── Null input is a silent no-op ────────────────────────────────────────
-[test, no_jit]
+[test]
def test_null_block_is_noop(t : T?) {
ast_gc_guard() {
var stmts : array
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index f373b3d03..671ca4097 100644
--- a/utils/CMakeLists.txt
+++ b/utils/CMakeLists.txt
@@ -104,13 +104,14 @@ foreach(util ${DAS_UTILS})
endforeach()
add_dependencies(all_utils_exe dastest_exe)
-# Prewarm the JIT dll cache for the whole test tree, except the folders dastest
-# also excludes under -jit (gc, ast_match, ast, no_aot, quote — they use runtime quote/qmacro
-# the JIT can't lower). Reuses the jit_exe target built by the foreach above (bin/jit.exe, built
-# with --jit-register-all-modules). Run explicitly:
+# Prewarm the JIT dll cache for the whole test tree, except module-gated folders and gc
+# (mirrors tests/.das_test: gc stays interp-only under -jit — heap_collect can't see
+# jitted-frame locals; quote/qmacro folders prewarm fine now that jit_enabled triggers
+# daslib/quote lowering). Reuses the jit_exe target built by the foreach above
+# (bin/jit.exe, built with --jit-register-all-modules). Run explicitly:
# ninja jit_cache_all_tests
add_custom_target(jit_cache_all_tests
- COMMAND ${UTIL_BIN_DIR}/jit.exe ${PROJECT_SOURCE_DIR}/tests --parallel 0 $,--jit-opt-level=0,--jit-opt-level=3> --exclude gc --exclude dasHV --exclude dasPUGIXML --exclude dasSQLITE --exclude stbimage --exclude live_host --exclude audio --exclude strudel --exclude ast_match --exclude ast --exclude no_aot --exclude quote
+ COMMAND ${UTIL_BIN_DIR}/jit.exe ${PROJECT_SOURCE_DIR}/tests --parallel 0 $,--jit-opt-level=0,--jit-opt-level=3> --exclude gc --exclude dasHV --exclude dasPUGIXML --exclude dasSQLITE --exclude stbimage --exclude live_host --exclude audio --exclude strudel
DEPENDS jit_exe
WORKING_DIRECTORY ${UTIL_BIN_DIR}
COMMENT "JIT-compiling tests/ into the .jitted_scripts cache"
diff --git a/utils/jit/main.das b/utils/jit/main.das
index 20b276763..ff556fc3b 100644
--- a/utils/jit/main.das
+++ b/utils/jit/main.das
@@ -5,10 +5,9 @@ require daslib/clargs
require daslib/fio
require daslib/jobque_boost
require daslib/strings_boost
-require math
require llvm/daslib/llvm_jit_cli
-require llvm/daslib/llvm_jit_run
+require llvm/daslib/llvm_jit_run // nolint:STYLE030 registration side effect: bakes the native llvm module into a standalone -exe build (see simulate() comment below)
[CommandLineArgs]
struct JitToolArgs {
@@ -287,7 +286,7 @@ def jit_compile_one(input : string; tool : JitToolArgs; jit : JitCliOptions) : b
// marked no-jit correctly), which a post-simulate run_jit call
// would miss. `require llvm_jit_run` above bakes the native llvm
// module into a standalone -exe build of this tool.
- simulate(program) $(sok, context, serrors) {
+ simulate(program) $(sok, _context, serrors) {
if (!sok) {
to_log(LOG_ERROR, "jit: failed to simulate {input}\n{serrors}\n")
success = false