aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2026-03-22 10:45:22 +0100
committerlemon <lsof@mailbox.org>2026-03-22 10:49:30 +0100
commitf9e3a52eaa6ae91388aa247182da6df3cc8d5a05 (patch)
tree0fa1b5604ebeaf4a1efbcb5242cf8bb07b4f1d76
parent79874c83bf76a5b3efd3d558933b90d9b53b829e (diff)
mem2reg: fix rare edge case with weird control flow
mem2reg was assuming alloca's appeared before all their uses. When this didn't happen it broke the logic re. sealed blocks. Normally this doesn't happen in most natural code. Even with gotos, RPO would assure this in most cases, the exception I found is a goto that jumps into a loop past a variable declaration that is earlier in the loop. Then even RPO would keep the alloca itself past the first uses, breaking things.
-rw-r--r--src/ir_mem2reg.c6
-rw-r--r--test/18-goto2.c24
2 files changed, 30 insertions, 0 deletions
diff --git a/src/ir_mem2reg.c b/src/ir_mem2reg.c
index ae41833..043ac9b 100644
--- a/src/ir_mem2reg.c
+++ b/src/ir_mem2reg.c
@@ -257,6 +257,12 @@ mem2reg(Function *fn)
do {
if (use->u == USERJUMP) goto Skip;
Instr *m = &instrtab[use->u];
+ if (use->blk->id < sb.lastvisit) {
+ /* alloca appears after some of its uses; happens rarely, only
+ * generated with code with gotos into loops. breaks this algo
+ * due to reprocessing sealed blocks */
+ goto Skip;
+ }
if (oisload(m->op) && (!sz || sz == loadsz(m->op))) {
sz = loadsz(m->op);
k = loadcls(m->op);
diff --git a/test/18-goto2.c b/test/18-goto2.c
new file mode 100644
index 0000000..8742d35
--- /dev/null
+++ b/test/18-goto2.c
@@ -0,0 +1,24 @@
+/* EXPECT:
+ok
+*/
+
+void h(int x){}
+
+/* isolated mem2reg bug from something in regalloc: the goto into the loop past
+ * the `int nqueue` declaration caused a miscompilation */
+void test(void) {
+ int curi = 5;
+ goto start2;
+ for (curi; curi >= 0; curi--) {
+ int nqueue;
+ start2:
+ for (nqueue = 2; nqueue > 0; ) {
+ h(nqueue--);
+ }
+ }
+}
+int puts(const char *);
+int main(void) {
+ test();
+ puts("ok");
+}