https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128356
Bug ID: 128356
Summary: [13/14/15/16/17 Regression] wrong code with
-funswitch-loops: switch with more than 255 case
ranges on one edge
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: wrong-code
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: lzy20000419 at outlook dot com
Target Milestone: ---
Created attachment 66070
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=66070&action=edit
testcase
When a loop contains a switch on an invariant index and one outgoing edge has
more than 255 separate case
values, loop unswitching computes the range for "this edge is not taken" by
inverting an edge range that was
already widened, and then removes code in the unswitched copy that can still
run.
```c
#define C2(x) case (x): case (x) + 2:
#define C4(x) C2(x) C2((x) + 4)
#define C8(x) C4(x) C4((x) + 8)
#define C16(x) C8(x) C8((x) + 16)
#define C32(x) C16(x) C16((x) + 32)
#define C64(x) C32(x) C32((x) + 64)
#define C128(x) C64(x) C64((x) + 128)
#define C256(x) C128(x) C128((x) + 256)
volatile int v;
__attribute__((noipa)) int
f (unsigned a, int n)
{
int s = 0;
for (int i = 0; i < n; i++)
{
switch (a)
{
C256 (0) /* case 0: case 2: ... case 510: */
s += 1;
break;
default:
s += 2;
if (a == 509)
s += 100;
break;
}
v++;
}
return s;
}
int
main (void)
{
if (f (509, 3) != 306)
__builtin_abort ();
return 0;
}
```
```
$ gcc -O3 t.c && ./a.out
Aborted
$ gcc -O2 -funswitch-loops t.c && ./a.out
Aborted
$ gcc -O3 -fno-unswitch-loops t.c && ./a.out ; gcc -O2 t.c && ./a.out
(both pass)
```
f (509, 3) returns 6 instead of 306: the `a == 509` test in the default arm is
folded to false. No UB
(-fsanitize=undefined,address and clang -O3 both pass). Fails on Compiler
Explorer with GCC 13.1, 13.2, 14.2,
15.3, 16.2 and current trunk (gsnapshot, 2026-10-23); GCC 12.2 and 12.5 pass.
On AArch64, `-O3` returns 6 on
GCC 13.4, 14.1, 14.2, 15.2, 16.1 and trunk, and 306 on GCC 12.5.
Godbolt: https://godbolt.org/z/Ws93jMMdz (same function; `main` prints `f(509,
3)` instead of aborting:
trunk and AArch64 trunk `-O3` print 6, GCC 12.5, AArch64 12.5, clang 23.1.0,
and trunk
`-O3 -fno-unswitch-loops` print 306). A variant where the probe is a
second switch (`case 509: ... case 513: ...`) instead of the `if` fails the
same way.