https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113551
Yuxuan Shui <yshuiv7 at gmail dot com> changed: What |Removed |Added ---------------------------------------------------------------------------- Summary|[13 Regression] |[13 Regression] |Miscompilation with -O1 |Miscompilation with -O1 |-funswitch-loops |-fno-strict-overflow |-fno-strict-overflow | --- Comment #9 from Yuxuan Shui <yshuiv7 at gmail dot com> --- So, to add some details to people said above, and to make sure I understood this correctly. What gcc did here is: 1. move the `if (!&dso->i)` branch out of the loop, duplicate the loop for the then/else branches. 2. `&dso->i` cannot be 0, so the else branch is eliminated. 3. in the then branch, this condition confused the compiler. it thought since `&dso->i` is not 0, `dso` is not 0 either, causing the bug. I diffed `-fdump` outputs and it does match what I expected to see. (minus is the correct one, plus the incorrect) @@ -2686,7 +2686,11 @@ ;; 4 succs { 5 6 } ;; 6 succs { 3 } ;; 5 succs { 1 } -Removing basic block 6 +Folding predicate _1 == 0B to 0 +Exported global range table: +============================ +_1 : [irange] int * [1, +INF] +Merging blocks 4 and 6 * * * this made me think whether loop unswitching is actually relevant here. since 12.3 also unswitches this loop, but doesn't miscompile. so I manually unswitched the loop: void bug(struct obj **root, struct obj *dso) { if (!&dso->i) { while (1) { struct obj *this = *root; if (dso == (void *)0) // should return here return; if (dso == this) return; // shouldn't reach here assert_not_null(dso); break; } } else { while (1) { struct obj *this = *root; if (dso == (void *)0) // should return here return; if (dso == this) return; // shouldn't reach here assert_not_null(dso); } } } and now the miscompile happens without -funswitch-loops. I wonder if this happens on trunk as well. looks this this is a -fno-strict-overflow related ranger bug.