Repository navigation
fix(runtime): release tail-drop frames before the last child - #1438
Conversation
…ld order compile_book puts each constructor's spine in CID_T: the word of its last field whose type holds its own datatype (255 when none). term_drop swaps the spine into the last slot when it opens the node, so the last child's early free of bendlang#1438 fires on the spine whatever the field order: a list with its tail first, before a word or behind a List drops in constant frames. The inner loop has one pop for the last and the exhausted child, and skips the dead counter and cursor writes there. tests/rfc/drop_spine covers the three shapes, owned and shared. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ld order compile_book puts each constructor's spine in CID_T: the word of its last field whose type holds its own datatype (255 when none). term_drop swaps the spine into the last slot when it opens the node, so the last child's early free of bendlang#1438 fires on the spine whatever the field order: a list with its tail first, before a word or behind a List drops in constant frames. The inner loop has one pop for the last and the exhausted child, and skips the dead counter and cursor writes there. tests/rfc/drop_spine covers the three shapes, owned and shared. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
d3bc283 to
d757f53
Compare
…ld order compile_book puts each constructor's spine in CID_T: the word of its last field whose type holds its own datatype (255 when none). term_drop swaps the spine into the last slot when it opens the node, so the last child's early free of bendlang#1438 fires on the spine whatever the field order: a list with its tail first, before a word or behind a List drops in constant frames. The inner loop has one pop for the last and the exhausted child, and skips the dead counter and cursor writes there. tests/rfc/drop_spine covers the three shapes, owned and shared. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
d757f53 to
ad071bf
Compare
…ld order compile_book puts each constructor's spine in CID_T: the word of its last field whose type holds its own datatype (255 when none). term_drop swaps the spine into the last slot when it opens the node, so the last child's early free of bendlang#1438 fires on the spine whatever the field order: a list with its tail first, before a word or behind a List drops in constant frames. The inner loop has one pop for the last and the exhausted child, and skips the dead counter and cursor writes there. tests/rfc/drop_spine covers the three shapes, owned and shared. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A pushed frame drops its trailing trivial slots, so a constructor, closure, task, Array or wide node frees before its last live child whatever words follow it; an Array or wide node whose live end is under 256 becomes a plain frame at its block's class. The tail column of CID_T is computed where the table is printed: the last box of the last field that holds the node's own datatype (a Maybe's pointer, not its tag), so a spine declared first or wrapped frees as early as one declared last. drop_spine adds a list whose spine sits in a flat Maybe; drop_array_spine runs a spine through a 4-cell and a 512-cell Array. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ctr_spine picks the last box of a field that holds the node's own datatype. When that field has no box (Succ's Nat word, a phantom parameter), the old offset arithmetic gave -1, which Metal rejects as a narrowing in the CID_T initializer, so any such program with a `!` call failed its GPU build. The trailing-trivial trim runs one loop for plain and Array frames. comp.ts goes from 64,048 to 63,990 ttok. tests/rfc/drop_spine_wordless covers the wordless field under `!`.
40f9cc3 to
5a1ba75
Compare
…omp.ts back under its cap - CID_T's spine column is the spine's distance from the node's end (0: no swap), as u32, so a wide node past 255 words gets its swap too. ctr_spine reads lay_node's own arms and counts a box by the type it stores, so a phantom or a sibling box in a flat field is not a spine. - A node with no live cell frees at once, without a frame; the walker no longer checks for an empty frame. - An Array or wide node whose live end is 256 or more keeps that end in its last cell, so the walker stops there and frees early instead of scanning the trailing trivial cells a second time. - heap_free fills a freed block with a bad shared pointer under -DBEND_POISON (CCC_OVERRIDE_OPTIONS=+-DBEND_POISON), so a read of a freed block fails. - Tests: a 300-field node with its spine at word 200, and a flat field with a phantom of the node's type. - comp.ts is 63,970 ttok: the File type comes from file_new, and smaller simplifications in the C runtime, the IO layer and the JS runtime. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
I pushed follow-up commits (2287053, then 7509c88) on top of a merge of current main. They fix the review findings. Here is what changed compared to your head:
Tested on macOS (M-series):
🤖 Generated with Claude Code |
…h lay_holds The File type, the ternaries and the other simplifications outside term_drop are back as they were on main; comp.ts goes over its cap again (64,778 ttok), to be cut separately. ctr_spine walks each field's slots with lay_holds, which recurses into flat lays with plain loops, and term_drop has a comment on its frames and the live end a large node keeps in its last cell. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Thanks for pushing the follow-ups and for separating the drop correctness changes from the cap work. I have read the updated 7509c88 notes, including the remaining 64,778-ttok failure. I am not treating the macOS runs as Linux/CUDA qualification or trimming unrelated comments to hide the cap. I will update the old 255-sentinel wording when qualifying this head. |
|
Completed the promised Linux qualification at 7509c88 and corrected the stale PR description. CID_T spine metadata is now documented as an unsigned distance from the last word, with 0 meaning no swap; wide/phantom-flat and live-end follow-ups are preserved. All five fixtures passed emitted JS plus normal/poisoned native C at 1 and 4 threads: 25 exact-output checks. Native GPU execution was explicitly disabled, and captured Clang output confirms -DBEND_POISON was applied. No source changes or force push. The known 64,778/64,000 comp.ts cap failure remains an explicit merge hold; I did not rerun it, alter caps or trim unrelated code. No CUDA/Metal, current-head sanitizer, fresh performance or full-cluster gate claim; your macOS/poison control evidence remains attributed to you. |
…hrough lay_holds, a node with no live cell goes through the frame like any other, and the two new comments are gone
What 6f7d462 changes (on top of 7509c88 plus a merge of
Checks (macOS):
Not run: Linux, CUDA, the hub corpus. |
|
Thanks. I am taking 6f7d462 as the base and keeping the term_drop and BEND_POISON notes. I will reduce the PR-added implementation rather than trim unrelated comments or ask to raise the cap, then qualify the result on Linux. Your macOS results remain the baseline; I will not rerun them just to confirm them. AI-assisted; no subagents. |
|
Kept 6f7d462 and merged current main, then pushed fcd72b6. Reusing lay_pack and simplifying the related frame code gets comp.ts down to 63,985 tokens; repo is 54/54 without changing the cap. The frame comment remains in shorter form, and the poison note is unchanged. Linux: all 20 native ownership checks pass, including BEND_POISON; the bounded 200-fixture CID comparison found no differences. Full cluster tests are still unavailable here, and I have not presented the Mac results as my own. The description now reflects this head instead of the obsolete 7509c88 implementation. |
|
Follow-up after #1581 landed: merged b869cca and pushed 1059b08. The integrated compiler is 63,998/64,000 tokens, the 20 Linux native checks pass, and the new GitHub run for this exact head is green: https://github.com/bendlang/bend/actions/runs/38767613356 . The earlier red run was the 64,006-token merge; this is a new passing head, not a rerun of that failure. No cap change or removal of the maintainer notes. |
- term_drop reads a node's first word only when it has a live word, so a zero-arity task no longer drops its continuation. - An array frame reads its live end once and keeps it beside the counter instead of reloading the last cell for every element. - CID_T is u16[3] (6 bytes a row, was u32[3]); a wide node's spine distance needs 16 bits. Rows are built with each constructor's layout. - ty_holds takes a family name, shared by lay_of and lay_holds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Each row names its fields (Fid: ar, resw, bangs, nofk; Cid: ar, hot, spine). A Cid row is 4 bytes; FID_T's two flags take a byte each, as Metal has no bitfields. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A field nested n deep in a two-armed inline datatype made lay_holds visit 2^n arms: reg/show_shared_layout (24 deep) took 16-110 s to compile and put the test gate past its cap. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bendlang#1438's tail-drop spine and bendlang#1447's device rounds come in as written. The constructor table takes bendlang#1438's Cid struct with this branch's nat flag (a foreign Nat answer checked as it comes in) as one more byte; lay_holds keys by ty_key. tests/io/nat_host_neg takes bendlang#1656's JS-only form. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Drops the fid_bangs, cid_hot and cid_nat macros, turn_fits and the CUDA bend_dev exit; shortens main_pure, turn_groups, turn_set, map_msb_u, map_bit and comments; io_run requeues through io_push. lay_holds keeps its memo: without it fan-out types compile 7-13x slower (a 200-type chain of 100-ctor types: 0.31 s vs 4.06 s).
main's ty_holds now takes p as a datatype name too (#1438); a name predicate keeps the first-met arguments, as lay_of's re-entry check did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Drops the fid_bangs, cid_hot and cid_nat macros, turn_fits and the CUDA bend_dev exit; shortens main_pure, turn_groups, turn_set, map_msb_u, map_bit and comments; io_run requeues through io_push. lay_holds keeps its memo: without it fan-out types compile 7-13x slower (a 200-type chain of 100-ctor types: 0.31 s vs 4.06 s).
bendlang/bend#1438 ships in 2.0.38, which CI already pins. The minimal reproduction now stays flat across all 24 repeats. Since 2.0.38, runtime flags go inside +RTS ... -RTS, so the documented run command changes too.
Fixes #1307.
term_dropfrees a node's frame before descending into its last live child, so recursive spines release cell by cell. Every child is still dropped once.This keeps the maintainer's
6f7d462dcommit and merges main throughb869cca4(including #1581); no history was replaced. The no-live case uses the ordinary frame,cid_spineis gone, and spine metadata comes from the constructor's own type.lay_holdsreuseslay_packinstead of maintaining a second word-by-word union algorithm; the one-use spine calculation lives beside its CID row. Wide-node distances still use the actual layout width, not the encoded arity.The frame-invariant comment remains, tightened to describe the shortened-wide-end case. The
BEND_POISONnote is unchanged. Related C declarations and address calculations are simplified; no code moved out of comp.ts.Current Linux checks
Linux x86-64, Clang 22.1.8:
drop_spine,drop_array_spine,drop_spine_flat,drop_spine_wide,drop_spine_wordless), each compiled at-O3with and withoutBEND_POISON, then executed with 1 and 4 threads: 20 exact-output native checks passed on the integrated head.bun gates/repo.ts: PASS: 54 / 54.comp.ts: 63,998 / 64,000 ttok, including main's new host declaration. Caps unchanged; no WONTFIX or release changelog edits.The full test gate was attempted but could not resolve the private
clusterSSH host, before any shard ran. The installer gate reports SKIP because the sibling site checkout is absent. No current-head macOS, CUDA/Metal, hub, cluster perf/safe, sanitizer or performance result is claimed. Earlier measurements and the maintainer's Mac qualification remain in the review history; they are not fresh Linux evidence.AI-assisted without subagents.