ae905de00c3a13a2efe3ac64e13a1e6bf2f11c9f
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4ea8bc5faf |
iter embedding-abi-m3.1 (DONE 6-7): freeze the value layout — DESIGN.md frozen-layout SSOT + lockstep pointers + enforceable byte-pin
Tasks 6-7 of the Boss-repaired split dispatch (Tasks 1-5 committed
|
||
|
|
c9a84b33b3 |
iter embedding-abi-m2.1 (PARTIAL 4/9 + Boss spec-defect repair): ctx ABI + de-globalisation
Tasks 1-4 land fully review-green and are the cohesive shippable
subset (the per-thread embedding ABI, fully wired + proven):
- T1: FIXED-FIRST alloc-guard baseline pin (RED captured -> GREEN at T4).
- T2: runtime/rc.c gains ailang_ctx_t {alloc_count,free_count} +
ailang_ctx_new/_free + __thread __ail_tls_ctx; the two increment
sites become 'if (_ctx) _ctx->... else g_rc_...'. g_rc_* statics +
ailang_rc_stats_atexit + the constructor RETAINED VERBATIM as the
null-ctx (single-threaded executable) fallback. rc_accounting_tsan.c
+ driver: per-ctx 8x200000 tsan-clean (de-globalisation positive proof).
- T3: codegen Target::StaticLib forwarder gains a leading 'ptr %ctx'
+ one '@__ail_tls_ctx = external thread_local global ptr' decl +
TLS save/store/restore around the BYTE-UNCHANGED internal call;
_adapter/_clos + internal arg vector untouched (M1 decision held);
doc-comment provisionality narrowed to the value/record layout.
- T4: build_staticlib rejects --alloc != rc (RC-only swarm artefact).
Boss adjudication of the orchestrator's Task-5 BLOCKED (spec-defect,
correctly surfaced not hacked): swarm.c's -DSHARED_CTX negative
control is structurally impossible — examples/embed_backtest_step.ail
is a non-allocating scalar kernel that never writes a ctx field, and
__ail_tls_ctx is __thread, so a shared-ctx scalar swarm has no
shared-memory write to race on (the spec's OWN item-1 honesty point).
The de-globalisation teeth belong to the item-1 rc_accounting harness
(shared ctx genuinely races on ctx->alloc_count; orchestrator
independently confirmed tsan exit 66). Spec amended in lockstep
(Goal coherent-stop, must-fail-axis item 2, Testing item 3,
Acceptance) so item 3's negative control is item-1's by the
de-globalisation-proof / capability-demo split; plan Task 5
restructured (swarm.c per-ctx capability demo only; negative-control
standing test relocated into the item-1 harness driver) + Task 3's
plan-transcription error (@ail_backtest_step ->
@ail_embed_backtest_step_step) corrected. This is a Boss
consistency-repair of an internally-contradictory clause whose intent
is already correctly realised in the same spec — not a redesign; no
brainstorm bounce. Task-5 working files discarded (corrected plan
reproduces them; a structurally-RED test must not enter main).
INDEX.md + final journal land at iter completion (re-dispatch [5,9]).
|
||
|
|
fc5f4590f8 |
rc: opt-in alloc/free stats counter for diagnosing leaks
Two non-atomic uint64_t counters in runtime/rc.c, incremented from
ailang_rc_alloc and the to-zero branch of ailang_rc_dec. An
__attribute__((constructor)) registers an atexit handler IFF the
AILANG_RC_STATS env var is non-empty at startup; the handler prints
ailang_rc_stats: allocs=N frees=M live=K
to stderr. Default-disabled so production binaries stay quiet.
Used by the e2e test infrastructure for assertions about RC
correctness (e.g. tail-recursive list-sum must not leak outer cells)
and by the bencher / debugger when diagnosing leak shape from a
fixture run. Single-threaded; non-atomic — same scope as the rest
of runtime/rc.c. The two unconditional increments on the hot paths
are negligible relative to the libc malloc/free already there.
|
||
|
|
6b3ff3bbed |
tidy: rewrite stale 18b/18c.x doc-headers to match shipped state
First half of the post-18-arc tidy-iter (per the new CLAUDE.md iter-cycle rule). Architect's drift review flagged module-doc headers describing 18b's leak-everything snapshot or 18c.x's "deferred" debt that has since shipped. Doc-only changes; cargo build clean, cargo test --workspace green at e2e=61, no behavioural change. - runtime/rc.c top-of-file header: rewrote from "Iter 18b deliberately stops at the layout and the alloc... programs leak every allocation" (false post-18c.3) to a stage summary spanning 18b–18e. Fixed `--memory=rc` reference (renamed to `--alloc=rc` in 18b's CLI work). Updated ailang_rc_inc / ailang_rc_dec block comments to point at `drop_<m>_<T>` and the worklist as the cascade owners, not at "18c will wire this up". - ailang-check uniqueness.rs module-doc: replaced the "deferred to later iters" block (which named 18c.4 + 18d as future work, both shipped) with a current "what this pass does NOT do" block. Cross-fn reasoning is still genuinely deferred; per-type drop fns and recursive cascades are NOT this pass's job by design (codegen does them, not the inference). - ailang-codegen emit_drop_fn_for_type doc + in-body comment: rewrote "Iter 18e replaces the recursive call with an iterative worklist free" to describe the actual shipped behaviour — the 18e (drop-iterative) annotation routes annotated types through emit_iterative_drop_fn_for_type; unannotated types stay recursive by orchestrator design (cheaper IR, no worklist alloc). Held back for the second half of the tidy-iter (pending the ailang-bencher determinism result): - DESIGN.md Decision 10 line 700 says modes are "mandatory" but lines 940–952 admit they're opt-in with deferred mandatoriness. The bench result either supports tightening the mandatoriness claim or backs down to "opt-in with performance benefit" — orchestrator-level decision blocked on the bench data. - Dynamic-tag partial-drop debt is captured in JOURNAL but should be surfaced in DESIGN as a known precision gap. |
||
|
|
ce6ab8ee44 |
Iter 18e: (drop-iterative) annotation + worklist allocator
Closes the 18-arc's stack-recursion limit. Recursive drop cascades from 18c.4 overflow on long ADT chains (Linux's 8 MB default stack maxes out around 1M cells of List). The new opt-in (drop-iterative) annotation on a Def::Type switches the synthesised drop_<m>_<T> body from recursive to iterative-with- explicit-worklist for that type. Schema: - TypeDef.drop_iterative: bool. Default false; serde-skip when false so existing fixtures' canonical JSON hashes stay stable. - Form-A: (drop-iterative) clause inside (data T ...). Worklist runtime (4 new ABI symbols in runtime/rc.c): - ailang_drop_worklist_new(initial_capacity) - ailang_drop_worklist_push(wl, ptr) - ailang_drop_worklist_pop(wl) -> ptr - ailang_drop_worklist_free(wl) Heap stretchy buffer, doubling on overflow, null-filtering on push. Lean 4 / Roc precedent documented in the runtime; the slot-repurposing strategy was considered and rejected because not every box has a free pointer-typed slot to thread the worklist through (Cons head is i64, slot 1 is ptr but it's the field we're following — no free slot). Codegen (emit_iterative_drop_fn_for_type): for a drop_iterative type, drop_<m>_<T>(ptr %p) emits a worklist loop. Fields of the SAME annotated type push onto the worklist (mono-typed); fields of DIFFERENT types call their own drop fn directly (recursive on those, but only if THEY are themselves recursive — i.e. one level of cascade jump maximum). Mono-typed-worklist is sound for the deep-self- recursion case the iter targets (List of List of T just needs the spine flattened). Tests: - examples/rc_drop_iterative_long_list — 1M-cell List of Int with (drop-iterative) annotation. - alloc_rc_drop_iterative_handles_million_cell_list E2E — builds + runs under --alloc=rc, asserts clean exit. With annotation: exits 0. Without annotation (control): SIGSEGV at exit code 139 (verified by hand). Worklist is load- bearing. - iter18e_drop_iterative_emits_worklist_body_no_self_recursion IR-shape: worklist body has br to loop_head AND no direct recursive call into drop_<m>_<T>. - iter18e_no_annotation_keeps_recursive_drop_body — control: unannotated variant still emits the 18c.4 recursive shape. - 3 surface parse-tests for the annotation round-trip. Test deltas: e2e 58 -> 61 (+3), surface 18 -> 21 (+3). All other buckets unchanged. cargo test --workspace green. Known debt (deliberate): - Mono-typed worklist: cross-type drop-iterative fields call the other type's drop fn directly. A heterogeneous worklist would be more general but adds tag tracking complexity for a case (drop-iterative T containing drop-iterative T') that's narrower than the deep-self- recursion target. Documented in emit_iterative_drop_fn_for_type's doc. - Closure / Type::Var / Type::Forall fields fall back to shallow ailang_rc_dec via field_drop_call — same as the recursive variant. - Dynamic-tag partial-drop fallback (head_or_zero epilogue shallow dec when moved_slots non-empty) — out of scope per brief. |
||
|
|
1eed78c41e |
Iter 18b: RC runtime + --alloc=rc routing
Wires up reference-counting allocator end-to-end without any inc/dec emission. Programs run under --alloc=rc and produce correct stdout (validated against --alloc=gc); they leak every allocation, exactly like the pre-Boehm era. The point of 18b is to establish the runtime contract before 18c adds the inc/dec-emission codegen pass. runtime/rc.c — new file. 8-byte uint64 refcount header prepended to every payload; ailang_rc_alloc(size) returns a ptr to the payload (header at ptr-8). ailang_rc_inc / dec are declared but never called by codegen yet; they exist so 18c can wire codegen against a stable runtime ABI. dec frees on zero refcount but does NOT recursively dec child references — that's 18c's job once it has per-ctor type info. crates/ailang-codegen/src/lib.rs — AllocStrategy::Rc variant added; fn_name() returns "ailang_rc_alloc". Single-line extension because Iter 18a's bump path had already centralised the allocator-symbol decision on fn_name() for all four allocation sites. crates/ail/src/main.rs — --alloc=rc accepted by Build/Run; parse_alloc_strategy extended; locate_rc_runtime() helper mirrors locate_bump_runtime; new Rc arm in build_to compiles runtime/rc.c with clang -O2 -c and links the resulting .o into the final binary (no -lgc). E2E coverage: alloc_rc_produces_same_stdout_as_gc on list.ail.json (42), alloc_rc_matches_gc_on_std_list_demo for broader allocation-site coverage. Total e2e bundle: 51 tests (was 49). Hand-verified on: sum.ail.json --alloc=rc → 55 list.ail.json --alloc=rc → 42 borrow_own_demo.ail.json --alloc=rc → 3 then 6 std_list_demo.ail.json --alloc=rc → matches --alloc=gc cargo build/test --workspace green; git diff examples/ empty. |