mutual TCO: trampoline tail-edge SCCs (lifts broker's 175-frame cliff)#22
Merged
Conversation
…cliff Direct self-tail recursion was already collapsed into while(1)+continue; mutual cycles weren't, so the STOMP broker's 7-fn writer_loop ↔ handle_frame ↔ handle_<command> SCC overflowed minicoro's 256 KB stack at exactly 175 frames per connection (see examples/stomp_broker/NOTES.md "Milestone 6: lived experience"). The compiler now finds these cycles via Tarjan's SCC over the fn_def→fn_def tail-edge graph it already builds for mark_process_tail. Every viable SCC member emits as gem_fn_<name>_body plus a thin trampoline wrapper; an intra-SCC tail call writes a global TLB (gem_tail_fn / gem_tail_args / gem_tail_argc / gem_tail_env) and returns instead of doing a real C call, so the wrapper iterates at constant depth. Sweep cells previously at delivered ≈ N×175 (100×200, 50×500, 200×100) now complete cleanly. Cells that hit a different cliff (500×100, 10×1000) survive 3× longer but still die — those are mailbox or process-table issues, the next-tier bottleneck the original M6 prompt anticipated would surface once the recursion ceiling was lifted. Full numbers in NOTES.md "Milestone 6, second pass".
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.
Summary
gem_fn_<name>_bodyplus a thin trampoline-loop wrapper. Intra-SCC tail calls set a global TLB (gem_tail_fn,gem_tail_args,gem_tail_argc,gem_tail_env) and return instead of doing a real C call, so a 7-fn cycle iterates at constant stack depth.while(1)+continueshape inside the body — only cross-fn intra-SCC tail calls take the trampoline.examples/stomp_broker/NOTES.md"Milestone 6, second pass" for the new sweep numbers — cells previously dead at delivered ≈ N×175 now complete; cells that hit a different cliff survive 3× longer).What's in the diff
runtime/gem.h+runtime/gem_error.c:GEM_MAX_TAIL_ARGS = 16, plus the four globals for the TLB.compiler/codegen.gem:find_tail_call_sccs(Tarjan + viability filter — bails the whole SCC silently on any member with rest params, defaults, boxed params, name-shadow, or > 16 params).scc_wrapper_foremits the trampoline loop with per-iteration arena reset (gated likeemit_tco_continue).emit_scc_tail_callwrites the TLB, pops the body's frame, returnsGEM_NIL.compile_fnandcompile_stmt_returnwired to detect SCC membership and intra-SCC tail-call sites.examples/96_mutual_tco.gem: 2-cycle (even/odd, 20k iters), 3-cycle (broker shape, 50k), and a mixed direct-self-TCO + mutual case (10k). Output appended toexpected_output.txt.bootstrap/stage0.cregenerated;make bootstraproundtrip clean on first pass.examples/stomp_broker/NOTES.mdsecond-pass writeup;docs/ROADMAP.md"Deep non-tail recursion ceiling" downgraded P1 → P3 with what's left (catchable stack-overflow, growable stacks);docs/OPTIMIZATIONS_LOG.mdshipped entry under Codegen Output;CLAUDE.mdKey Decisions updated.Test plan
make test— all 124 examples + LSP smokes green.make bootstrap— clean roundtrip on first pass; clean rebuild from newstage0.cpasses the full suite.bash examples/stomp_broker/smoke_test.sh— 4/4 pass.bash benchmarks/stomp/sweep.sh— sweep numbers in NOTES.md "Milestone 6, second pass".examples/96_mutual_tco.gemexercises 2-/3-/5-fn SCCs plus a member with both direct-self TCO and cross-fn cycles in the same body.Not in this PR
GEM_MAX_PROCS=4096build).These are the next-tier issues the second-pass cliff (mailbox / process-table) surfaces; per the resume prompt, they're for a later session.