https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128588
Bug ID: 128588
Summary: [14/15/16/17 Regression] wrong code: if-to-switch
turns (a | b) != 0 of two anti-ranges into a switch on
their union
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: ---
Fails with trunk (gcc 17.0.0 20261109, a74179d08523) and with 11.1.0 through
16.2.0; 7.5.0 through 10.5.0 work.
Target: x86_64-pc-linux-gnu (target-independent).
(x not in [13, 15]) | (x != 20) is true for every int, so f must return 8 for
every x other than 1, 2 and 3.
__attribute__((noipa)) int
f (int x)
{
if (x == 1) return 1;
if (x == 2) return 2;
if (x == 3) return 3;
_Bool a = (unsigned) x - 13u > 2u; /* x not in [13, 15] */
_Bool b = x != 20;
if (a | b)
return 8;
return 9;
}
int
main (void)
{
if (f (20) != 8 || f (14) != 8)
__builtin_abort ();
return 0;
}
gcc -O1 t.c && ./a.out
Observed: abort, f (14) and f (20) return 9. Expected: exit 0. -O2, -O3 and -Os
fail too. -O0 and -O2 -fdisable-tree-iftoswitch work.
Godbolt (trunk): https://godbolt.org/z/v34E3Wfdj
The iftoswitch dump has case 1, case 2, case 3, case 13 ... 15 and case 20, and
the last two labels go to the edge that returns 9.
Cause: find_conditions (gimple-if-to-switch.cc) accepts _3 = a | b; if (_3 !=
0) by making one range entry per IOR operand. It only checks that both use the
same SSA name and have the same in_p. Here both are anti-ranges (in_p false).
It then uses the false edge as the switch target and emits a case label for
each range. But (x not in R1) | (x not in R2) is the complement of the
intersection of R1 and R2, not of their union.
Possible fix (not tested): in find_conditions, give up when an IOR has more
than one range and !in_p.
Disclaimer: This test case was found with LLM-assisted targeted testing, and
the report was written with LLM assistance.