Repository navigation
Release v1.15.5 to v1.15.8 from BuddhiLW/main - #21
Merged
Merged
Conversation
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@9c091bb...3d3c42e) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 7.0.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
…ision, double-array reduce, float loop, byte-array from seq)
…O-065) fastBinaryFixnum becomes fastBinaryNumber: a double paired with a double or fixnum computes + - * < <= > >= in f64, exactly what promote.*Promoting and math.pairwise do once either operand is a double. = not= mod rem quot still defer to the builtin (cross-category equality, float sign-of-zero rules). The float arm sits out of line (fastNonFixnum) so the fixnum dispatch keeps its shape; inline, it cost 04_arith_loop +9%. diff_test pins 16 float cases VM == TreeWalk. User instructions, ReleaseSafe -Dwasm, taskset -c 2, min of 2-3, O-064 commit -> this commit: 40 seq_dot_1k 13.25G -> 11.65G -12% 41 seq_dot_100k 13.74G -> 12.21G -11% 44 float_loop_recur 2.52G -> 1.86G -26% 02 fib_loop 67.0M -> 66.8M 0% 04 arith_loop 778.0M -> 810.8M +4.2% (accepted: dispatch shape) 01 fib_recursive 355.8M -> 362.4M +1.8% Card: CLJW-NUMERIC-SEQ-PERF (20261021235636-3459e0ce).
agent, locking, concurrency_stress, the java.io family, reducers, repl, json_fs, tokenizer_long_input, extend_via_metadata and bfs_queue_gc now run in the shared suite image. shutdown-agents cases stay in a child cljw since the call is process-global. Divergence pins repointed to the suites; e2e ceiling 322 -> 305. Smell-audited: 0: no smells; scripts deleted with their run_all rows and fixtures
# Conflicts: # .scratch/RESUME.md
… a suite gc_torture, http_client, http_server, load_file_source, repl_classpath, stdin_read_line, vm_error_loc_sidecar, wasm_polyglot and eval_budget each assert something only a fresh cljw process shows (startup env, a bound port, stdin, rendered stderr, exit codes, external toolchains), so they carry the e2e-process-boundary marker with the reason. eval_budget's in-process cljw.eval/with-budget cases move to suites/eval_budget_test. Counted scripts 305 -> 296. Smell-audited: 0: no smells; each marker reason checked against the script body
phase16_wasm_component, _component_multiexport and _component_budget and the catchability half of phase16_wasm_ffi become deftests: wasm_component_test, wasm_component_multiexport_test (the ADR-0135 echo table, one assertion per row), wasm_component_budget_test and wasm_ffi_catch_test. wasm_ffi keeps only the exit-time leak report and is marked a process boundary. Probe fixtures deleted, debt refs repointed. Counted scripts 296 -> 292. Smell-audited: 0: no smells; every probe marker kept as an assertion message
The linear-memory probe and the engine-selection probe become wasm_memory_test and wasm_engine_select_test, one assertion per probe label. scripts/run_wasm_gate.sh now also runs every wasm_*_test suite so the wasm-only quick path keeps its coverage. Counted scripts 292 -> 290. Smell-audited: 0: no smells; run_wasm_gate exercised against the suites
test/run_all.sh: both sides deleted rows for migrated scripts (phase15 set_bang/stm_dosync by #89, phase16 locking/agent/concurrency_stress here); none of the five scripts exists after the merge, so neither side's rows stay. check_e2e_count.sh baseline is the combined count: 231 (18 exempt by marker). check_e2e_reach and check_e2e_dup pass. Smell-audited: 0: merge of two deletions plus the recomputed ceiling
Each node of a one-coll map/filter/take/iterate chain allocated an interpreted arity-0 closure and re-entered eval to run its body. Over an unchunked source such as (range) = (iterate inc 0) that was one closure and one eval re-entry per element per layer. A LazySeq can now hold a builtin step plus two operands (lazy_seq.allocNative). The steps in lang/primitive/lazy_step.zig build the same nodes as the clj bodies: chunk-cons over a chunked source via the O-032 drains, cons plus the next node otherwise. filter skips rejected elements and empty chunks within one realization. iterate does not call f for its head, as clj's Iterate. take keeps the interpreted body for a non-integer n. lazy_chain (3 reps): 1208 ms -> ~270 ms, bb 141 ms. Realization counts are pinned against bb in lazy_step_test.clj. VM and tree_walk give the same results under --compare. Smell-audited: 1: one native-step slot on LazySeq instead of a node type per producer
lazy_chain 13.75x -> 2.28x bb on the cherry-pick of #91 onto v1.15.4 staging (bench/vs_bb -n 3, load 12.6). Smell-audited: 1: changelog and bench row only, no code
# Conflicts: # .dev/optimizations.md
Smell-audited: 1: changelog only, no code
zwasm d77568a0 (BuddhiLW/zwasm fix/wasi-p2-adapter-imports, upstream zwasm/zwasm#634): a component built with the wasi_snapshot_preview1 adapter no longer fails wasm/load-component with UnknownImport. The adapter's env::memory re-export resolves, and filesystem-error-code plus the 0.2 clock resolution rows are served. This pins a fix branch only until fork #7 merges (fork Actions are disabled); the merge has the same tree, so the .hash carries over.
perf(lazy): native lazy nodes for map/filter/take/iterate (lazy_chain 13.75x -> 2.28x bb)
# Conflicts: # CHANGELOG.md
A vector's TailNode carried an inline [32]Value (a 272-byte cell for a
2-vector) and an ArrayMap an inline [16]Value (152 bytes for a 2-key map).
Both now keep their slots as trailing storage in the same GC cell
(allocWithTail, the ADR-0184 shape), sized to len / 2*count and read
through items() / entries(). Every persistent change copies the cell
anyway, so the reserve bought nothing.
200k retained {:id i :v [i (inc i)]}: last_live 97.2 -> 29.6 MB
(486 -> 148 B per entry). gc_churn: bytes_alloc 497 -> 175 MB, peak RSS
353 -> 160 MB. Instructions per literal are unchanged (perf stat,
pinned): the remaining time gap to bb is VM dispatch, recorded in O-091.
Smell-audited: 1: removes a fixed-capacity reserve that no persistent op used; one cell per node kept, no new seam
A String paid two allocations: the GC cell and a separate gpa.dupe of its bytes, freed again by the per-tag finaliser at sweep. perf of (count (str i)) in a loop showed libc malloc/free at about 12% of samples. Payloads up to page_heap.max_small_bytes - @sizeof(String) now live in the tail of the String's own cell (allocWithTail). A storage enum in a former padding byte tells the finaliser and ownedBytes which arm applies; larger payloads keep the owned infra block. The inline alloc runs inside a fabrication region, since byte_view may be the tail of an unrooted String. Pinned user instructions: str loop 2790 -> 2300 per iteration over the empty loop; vs_bb string_ops 5959M -> 5219M, gc_churn 8369M -> 8011M. Smell-audited: 1: stale module doc that argued against inline storage rewritten; no address-adjacency test, explicit storage tag instead
perf(eval): inline f64 arith/compare in the VM intrinsics (O-065) + numeric seq bench cells
# Conflicts: # CHANGELOG.md
Splits gc_churn into the retained build, the full churn loop, the small map+vector literal, (str i) and the empty recur loop, timed per piece. Runs on cljw and bb alike. The measurements behind CLJW-PERF-SMALL-LITERAL, CLJW-PERF-STR-1ARG and CLJW-PERF-RECUR-LOOP came from it. Smell-audited: 1: bench probe only, no runtime code
# Conflicts: # CHANGELOG.md
perf(collection): size vector tails and array maps to their contents
# Conflicts: # CHANGELOG.md
perf(string): store short string payloads inline in the GC cell
threshold = max(floor, last_live) instead of max(floor, 2 * last_live). Peak committed heap is about live + threshold, so the 2x growth factor held a large live set near 3x itself in RSS. Shrinking the retained empty pages instead does not lower the peak (the next cycle maps them again, pages_released 89 -> 11117), so the growth factor is the lever. Adds an adaptiveThreshold unit test and a heap-return suite case (peak committed under 2.5x live; base measures 3.0x, new 2.0x). Smell-audited: 1: one-line policy change in gc_trigger, no new knob, retention budget untouched, three doc comments updated
The bench-vs-bb job restores the gate's x86_64-linux .zig-cache read-only. That cache can hold a WriteFiles manifest (stub_wf, which writes bootstrap_sources_stub.zig for cache_gen) whose o/<digest> output dir is gone. Zig then fails with "failed to check cache ... file_hash FileNotFound" instead of rebuilding. It hit #99 twice on 2026-10-24, from a cache saved by a green gate run, so the save-side guard in ci.yml (never save a cancelled job) does not cover this case. The job never saves the cache, so the fix is local: on a failed build, drop .zig-cache and build cold once. Smell-audited: 1: read-only consumer recovers locally; no save-side change without a reproducer
…line fastBinaryNumber Recovered work-in-progress from the previous session (CLJW-PERF-RECUR-LOOP). Smell-audited: 1: WIP recovery, no new smells; cold ops dispatched via toCold prong
A jolt --library build exports jolt_library_init and jolt_lookup only; the export! entry points are function pointers, not dynamic symbols. cljw runs the init (221 to 495 ms) and the lookups by name today. An interim C shim in the example dir re-exports the looked-up pointers by name, so add, mandel and the hive C ABI run now, and hive_call.clj drives the jolt library unchanged. The call-by-address form is written in jolt_lib.clj and reported pending CLJW-FFI-CALL-ADDRESS. build.sh extracts the Chez kernel the prebuilt jolt embeds and links a chez front end, because jolt 0.8.19 --library otherwise fails on a missing scheme.h and a missing chez executable.
Best-of-N harness across cljw, bb and jolt. Findings in hive memory 20261111113312-4b8b6ce6.
cljw.ffi resolved a C function only by (lib, name), so a function pointer
a C API hands back (a vtable slot, a plugin registry, jolt_lookup) was
unreachable. (ffi/function-at addr arg-types ret-type) turns an integer
code address into an ordinary Clojure fn with the same types and limits
as function.
A separate fn, not an overload of function: the :closed rule stays on
the one constructor whose code a library owns. New leaves -prepare-at and
-invoke-at take no library; a Function records in state[2] whether a
library owns it, and -invoke-at refuses an owned one. No library owns an
address, so close does not reach it and the caller keeps the code loaded.
A NULL address (0 or nil) throws {:ffi/error :symbol :address addr} at
creation and is never called; a bad signature throws :signature with
:address in place of :symbol. No new error kind.
The errors-as-data builders move to ffi/errors.zig, which keeps
surface.zig under the 400-line ceiling.
Fixture: get_adder, get_halver and get_null_fn return pointers to static
callees, so only the returned address reaches them.
Smell-audited: 2: separate constructor keeps the lifetime rule single-owner; error builders extracted to their own module instead of growing surface.zig
…leased, not absent Two passages said there is no wasm32 build. ADR-0193 landed one in August and CI compiles it on every Linux run. State what the build is (single- threaded, no network, no FFI, no embedded engine) and that no release ships it.
jolt_lookup hands back function pointers, and ffi/function-at (merged from
feat/ffi-call-address) calls them, so jolt_lib.clj now runs the init, the
lookups, add, the mandel kernel and the hive C ABI against libdemo.so with
no C in between. A missing export is address 0 and function-at refuses it
with {:ffi/error :symbol :address 0}.
Measured over five alternating runs on a loaded host: the 60k-call mandel
grid takes 407 to 502 ms through function-at and 385 to 531 ms through the
shim, so the shim (shim.c, libdemo_named.so and its build step) is deleted
and the example has one path. README states the numbers with date and
versions; landscape.md points at the example.
Smell-audited: 1: one call path; the interim shim is removed rather than kept beside it
…nd memory budgets Builds cljw for wasm32-wasi (ReleaseSmall, 3,692,268 B) and runs it from a native cljw through wasm/run. Records size, startup, the confinement results (fuel trap, memory.grow refusal, no file or env authority) and the limits of the single-threaded build. The guest needs one preopened directory because it reads ./deps.edn at startup and WASI resolves no relative path without one.
…(ADR-0208) check_accepted_divergences.sh now requires a `check` on every AD row and evaluates its expr through one portable driver, scripts/ad_check_render.clj: on the built cljw (must render the recorded `cljw`, one process per row) and, when the Clojure CLI is on PATH, on Clojure 1.12.4 with TZ Asia/Tokyo and en-US (must render `clj`). A row whose two recorded renderings are equal fails as STALE. Without clojure the clj half prints a boxed SKIPPED banner; AD_CHECK_REQUIRE_CLJ=1 makes the skip fail. The step moves after build_cljw and joins SMOKE_CORE, since a src change can converge a divergence. Smell-audited: 2: ADR-0208, DA fork run, disposition recorded in the ADR
…7 (ADR-0208) Run on cljw v1.15.3 and Clojure 1.12.4: 61 executable checks, 8 prose. No row is STALE. AD-065's JVM claim was false: a running future's (.cancel f false) answers [true true] on the JVM too; the real divergence is that the JVM lets the task finish. AD-077 records that cljw accepts an ^int fn parameter hint where clj refuses it (F-004, F-005, ADR-0059). Smell-audited: 1: data backfill under ADR-0208
The Linux gate installed ripgrep with apt-get. On 2026-11-11 the runner could not resolve its Ubuntu mirror and six consecutive runs across five branches failed in that step before any build. Fetch the release tarball from GitHub and verify its SHA-256, the way the same step already installs babashka. The pin lives in .github/versions.lock.
ci: install ripgrep from a checksum-pinned release, not apt
docs(examples): cljw hosting cljw as a wasm32-wasi guest
docs(polyglot): run hosts.cljc on jolt, a fourth Clojure host
AD ledger: every accepted divergence carries an executable check, re-run on cljw and clj (ADR-0208)
feat(ffi): call a function pointer with ffi/function-at
bench(numeric): fib30 and loop10m probes with a best-of-5 runner
docs(ffi): cljw calls a Clojure library compiled by jolt
docs(readme): cljw.ffi, the jolt library route, four hosts, and the changelog
Release: cljw.ffi function-at, jolt library route, cljw as a Wasm guest, AD staleness gate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings clojurewasm/ClojureWasm main up to v1.15.7 (fork tag 706d327), covering v1.15.5, v1.15.6 and v1.15.7.
into; cheaper small GC cells;inc/deccompile to direct ops.cljw.http.serveraccepts a byte-array:bodyverbatim; thedocs/examples/fractal/Mandelbrot example with its two e2e gates.reduce-kvon vectors indexes in Zig (-71.7% instructions);deriveraises clj's own errors and class ancestors reachObject; glibc mmap threshold pinned at startup; the fractal example grows to nineteen engines and four formulas with Julia sets.See CHANGELOG.md [1.15.5], [1.15.6] and [1.15.7].