An alloca built at its use site re-executes on every pass through that block, and LLVM reclaims allocas only at ret — so loop-body locals, nested-loop index slots, and emitter spill temps (ig.tmp, sret slots, ABI coercion temps, byval materialization) grew the stack per iteration and long loops segfaulted on stack exhaustion. New LLVMEmitter.buildEntryAlloca inserts after existing entry-block allocas and restores the builder position; every LLVMBuildAlloca site reachable during instruction emission now routes through it. Initialization stores stay at the use site (per-iteration re-init is unchanged), and entry slots become mem2reg-promotable. The 35 .ir snapshot diffs are pure alloca position moves (type multisets verified identical per file). Regression: examples/0047-basic-loop-local-stack-reuse.sx (segfaulted pre-fix on both the 1M-iteration body-local loop and the 3M-iteration nested loop).
27 lines
792 B
Plaintext
27 lines
792 B
Plaintext
// Loop-body locals reuse one stack slot per frame: a body-declared local
|
|
// (and every compiler temp) must not grow the stack per iteration, so
|
|
// million-iteration loops run in constant stack. Covers body locals,
|
|
// nested loops (the inner loop's hidden index slot), and element reads.
|
|
// Regression (issue 0109): allocas were emitted at their use site, so each
|
|
// iteration re-executed them — LLVM only reclaims allocas at `ret`, and
|
|
// these loops segfaulted on stack exhaustion.
|
|
|
|
#import "modules/std.sx";
|
|
|
|
main :: () -> s32 {
|
|
sum := 0;
|
|
for 0..1000000: (i) {
|
|
buf : [128]s64 = ---;
|
|
buf[0] = i;
|
|
sum += buf[0];
|
|
}
|
|
print("sum={}\n", sum);
|
|
|
|
n := 0;
|
|
for 0..3000000: (i) {
|
|
for 0..1: (j) { n += 1; }
|
|
}
|
|
print("n={}\n", n);
|
|
0
|
|
}
|