diff --git a/test/wcc/930_free_noop_run.c b/test/wcc/930_free_noop_run.c index ab80ca81..69f7d174 100644 --- a/test/wcc/930_free_noop_run.c +++ b/test/wcc/930_free_noop_run.c @@ -15,6 +15,8 @@ * no-op's documented leak semantics — the pointee stays valid), free * of a struct field, free of a CALL operand twice (the side effect * must run per call — a global counter observes both evaluations), + * free in a 1M-iteration loop (the no-op must not accumulate stack + * damage — a leaked push per free would segfault), * and the Hare-port shape alloc-then-free round-trip (import rt; * *i64 — i64 sidesteps the pre-existing unrelated cs≠ww alloc(value) * size divergence on narrow pointee types, filed separately). The @@ -93,6 +95,26 @@ static const struct row rows[] = { " return 0;\n" "};\n", 0 }, + /* free() in a hot loop: the no-op must not accumulate stack + * damage — a leaked 8B push per free would blow the 8MiB stack + * long before 1M iterations (segfault, not a wrong exit code). */ + { "free_loop_no_stack_damage", + "let g: i32 = 0;\n" + "fn bump(p: *i32) *i32 = {\n" + " g = g + 1;\n" + " return p;\n" + "};\n" + "export fn main() i32 = {\n" + " let x: i32 = 1;\n" + " let i: i32 = 0;\n" + " for (i < 1000000) {\n" + " free(bump(&x));\n" + " i += 1;\n" + " };\n" + " if (g != 1000000) { return 5; };\n" + " return 0;\n" + "};\n", + 0 }, /* The verbatim Hare-port shape: alloc then free, deref after. * Pre-#27 this was THE w6l undefined-reference repro. */ { "free_alloc_roundtrip",