fix: run the backend at -Oless for unoptimized builds - #1821
Open
ghaith wants to merge 4 commits into
Open
Conversation
Every call to an aggregate-returning function allocates a caller-side temporary sized to the declared return type (STRING[2048] for the stdlib string functions). These allocas carried no lifetime information, so LLVM kept one dead slot per call site alive for the whole function: string-heavy POU bodies easily exceed 100 KB of frame and overflow constrained runtime task stacks. Bracket each lowered statement's temporaries with llvm.lifetime.start/end so LLVM's stack coloring can overlap slots of consecutive statements; a string-heavy test body shrinks from ~111 KB to ~10 KB of frame at the default optimization level. Unoptimized builds are unchanged: stack coloring is a machine pass that only runs when the backend optimizes. Temps whose address outlives their statement are pinned instead (Allocation::statement_scoped = false) and get no markers: ADR/REF arguments, REF= right-hand sides, and interface fat-pointer captures (the polymorphism lowering wraps those in ADR before this pass runs). Loop bookkeeping temporaries keep their function-long storage for the same reason. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-Onone keeps the IR pipeline at O0 — no inlining, unrolling or other debug-affecting IR transforms — but now creates the TargetMachine at codegen level Less so LLVM's machine pipeline runs. Most importantly this enables stack coloring, which merges the lifetime-bracketed call temporaries: a string-heavy function block drops from ~112 KB to ~10 KB of frame at -Onone, fitting constrained runtime task stacks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ghaith
force-pushed
the
backport/1.0.x/onone-stack-coloring
branch
from
July 21, 2026 11:48
9fab2f7 to
0e3419d
Compare
ghaith
force-pushed
the
backport/1.0.x/lifetime-markers
branch
from
July 21, 2026 11:48
8a51916 to
91fe41e
Compare
…one-stack-coloring Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Base automatically changed from
backport/1.0.x/lifetime-markers
to
release/1.0.x
July 29, 2026 08:29
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.
Companion to #1819 for
release/1.0.x(stacked on it; retargets automatically when #1819 merges). Together they close the string-temp stack-exhaustion problem for unoptimized builds.Problem
#1819 brackets lowered call temporaries with lifetime markers so LLVM's stack coloring can overlap their slots — but stack coloring is a machine pass that only runs when the backend optimizes. At
-Ononethe markers are inert, and string-heavy POU bodies keep one deadSTRING[2048]slot per call site: a representative function block needs ~112 KB of frame and overflows constrained task stacks at entry.Fix
Decouple the two optimization dials.
-Ononekeeps the IR pipeline atdefault<O0>— no inlining, no unrolling, no IR-level transforms that damage source-level debugging — but creates theTargetMachineat codegen levelLess, so the machine pipeline (instruction selection via SelectionDAG, greedy register allocation, and crucially stack coloring) runs.Frame size of the representative function block at
-Onone: 111,760 B → 10,312 B.Debug-experience validation
Compared gdb behavior on a multi-file test program (small inlinable functions, cross-file calls, unrollable and data-dependent loops, string-taking and string-returning functions) at
-Ononebefore/after, plus-Oless/-Odefault/-Oaggressivefor context:info args/info locals, andfinishbehave identically; zero<optimized out>locals (those appear starting at-Oless, caused by the IR pipeline, which stays off here).nextrepeated the loop-header line and never showed the body is gone with SelectionDAG.Testing
Full unit suite, snapshot tests (no drift) and the lit end-to-end suite pass. Frame measurement above from
objdumpon the compiled object.🤖 Generated with Claude Code