Repository navigation
fix(c): write a shared word back to the fork frame slot that holds it - #1615
Merged
Merged
Conversation
In sequence, a fork's middle steps read the words they hold from the frame without popping it (STK(-k)). When such a step shares one, term_keep may wrap a raw root in a redirect and return a new word, but the slot kept the old one. The step that pops the frame then read the raw word, and the join's sink freed the cell the redirect still pointed at. In Push (Xs.cnt borrows the list, Xs.push stores it) the freed cell looped the free list and --threads 1 hung. bind_pop now writes the kept word back: STK(-1) = _xs_1 = term_keep(e, _xs_1, 1). The code of 4 programs changes and of no bench: brw_fork_push, brw_fork_cross, family_two_indices (the quadrants Mat.sq shares, whose second use read the stale raw word) and the slash_boss_3d demo (Cloth.mesh and Scene.build), whose SLASH_PROBE run prints the same state. Otherwise C and JS output lose only the cut runtime comments. New witness: tests/reg/brw_fork_push.bend. A direct Push takes the parallel path at the gate's default thread count and passes, so Push runs at the leaves of a depth-12 fork tree, which run in sequence at any thread count; before this commit it hangs at 1 to 128 threads. comp.ts: 64,092 -> 63,990 / 64,000 tokens (main is 92 over its cap). To fit, comments are cut, each one a twin or a restatement of code: the C runtime's io_list comment (the JS twin says it in full), the JS runtime's show_val comment (the C twin says it in full), seg_new's (it restated seg_take and said a segment pops its frame, which a fork's middle steps do not), show_main's last sentence (the refuse error and the io_type check below), io_park_add's double-link note (io_park_cut), ring_rewind's comment (its body), the Heap comment's bump sentence (the one before it lists the misses in order) and the Blk comment's blk_node and blk_copy mappings (their call sites).
Lorenzobattistela
approved these changes
Oct 16, 2026
nicolas-abril
added a commit
to nicolas-abril/bend
that referenced
this pull request
Oct 19, 2026
comp.ts keeps this branch's structure, with upstream's changes ported in: bendlang#1615's frame-slot write-back in bind_pop; bendlang#1617's named memo functions (open_of, spine_of, uses_of), per-file literal steps keyed by string, and lay_at memoized per type key (FL.ty_opens, keyed by ty_key); bendlang#1625's F32.read regex; bendlang#1628's string_contains. bendlang#1617's per-def PROBES reset is left out: here probes and the term caches live as long as the file and units emit again across sweeps, so a reset would reuse probe indices still held in FL.opens and FL.uses. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Merged
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.
Split out of #1596, as asked in its review.
What changes
In sequence, a fork's middle steps read the words they hold from the frame without popping it (
STK(-k)). When such a step shares one of them,term_keepmay wrap a raw root in a redirect and return a new word, but the frame slot kept the old one. The step that pops the frame then read the raw word, and the join's sink freed the cell the redirect still pointed at.In
Pushbelow,Xs.cntborrows the list andXs.pushstores it. At--threads 1the freed cell loops the free list and the program hangs on main:bind_popnow writes the kept word back to the slot it came from:Witness
tests/reg/brw_fork_push.bend. The gate runs each binary at the default thread count, where a directPush(Xs.build(100n, 1))takes the parallel path and passes. So the test runsPushat the leaves of a depth-12 fork tree, and the leaves run in sequence at any thread count. On main it hangs at--threads 1,2,8,128and the default. On this branch it prints 823296 at all of them, as do the JS lane and the interpreter.Checks
A/B against main over the 809 programs that build to C and JS (tests, benches, demos). The code changes in 4 programs and no bench:
brw_fork_pushgains 1 write-back;brw_fork_crossgains 1;compile/family_two_indicesgains 3, for the quadrants thatMat.sqshares, whose second use read the stale raw word;app_slash_boss_3ddemo gains 10, inCloth.meshandScene.build.Otherwise, C and JS output lose only the cut runtime comments listed below.
brw_fork_crossprints 199 andfamily_two_indices64 and 512, as on main.SLASH_PROBE=600on the slash demo prints the same game state as main at--threads 1and16. Only the audio queue and voice counts and the frame timings differ, and those vary between runs of either build.gates/test.ts, run locally: PASS 1638 / 1640. The two failures,gfx_clicksandgfx_window, expect "no display" and fail the same way on main here, because this Mac has a desktop session.tsc -p bend2/pack/tsconfig.json(TypeScript 5.9.3): the same 11 errors as main, none incomp.ts.bun gates/repo.ts: 54 / 54.The A/B, the program runs and the test gate above were done before the last five comment cuts (
ring_rewind,io_park_add, the Heap and Blk comments,show_main). Against that build, this branch changes only comment lines: in the C of all 809 programs, and in no JS.comp.ts tokens
Main is at 64,092 / 64,000 by itself, so the repo gate currently fails on every open PR. This PR brings
comp.tsto 63,990. To fit, these comments are cut, each one a twin or a restatement of code:io_listcomment, "Bytes cross as they are…" (the JS one says it in full);io_park_add's double-link note (io_park_cut);ring_rewind's comment (its body);blk_nodeandblk_copymappings (their call sites).show_valcomment (the C twin says it in full).seg_new's comment. It restatedseg_takeand the runtime's Segments comment, and said a segment pops its frame, which a fork's middle steps do not.show_main's last sentence (therefuseerror and theio_typecheck below it).#1596, rebuilt on main, makes a disjoint set of cuts. The two merge cleanly, and with both merged
comp.tsis 63,886.