https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127659
Bug ID: 127659
Summary: ICE: 'verify_gimple' failed (mismatching comparison
operand types) after match.pd folds popcount(x) & 1 to
parity(x) when __builtin_popcount was redeclared with
an unsigned return type
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: ice-checking, ice-on-invalid-code
Severity: normal
Priority: P3
Component: middle-end
Assignee: unassigned at gcc dot gnu.org
Reporter: captainnemo9292 at gmail dot com
Target Milestone: ---
GCC accepts a redeclaration of a builtin with a different return type and
keeps treating it as the builtin. With __builtin_popcount redeclared as
returning unsigned, the popcount(x) & 1 -> parity(x) canonicalisation then
produces a mistyped comparison:
unsigned __builtin_popcount (int a) { if (__builtin_popcount (a) & 1); }
$ gcc -c t.c
t.c:1:10: error: mismatching comparison operand types
int
unsigned int
if (_1 != 0) goto <D.2971>; else goto <D.2972>;
t.c:1:10: internal compiler error: 'verify_gimple' failed
verify_gimple_in_seq(gimple*, bool)
gimplify_body(tree_node*, bool)
gimplify_function_tree(tree_node*)
cgraph_node::analyze()
https://godbolt.org/z/rP8b76Mfe - 12.1 through trunk (17.0 20260928), -O0 and
-O2, checking builds only (the 16.2 release build compiles it). With the
builtin's own return type (int __builtin_popcount (int a) ...) or with
'return __builtin_popcount (a) & 1;' instead of the if there is no ICE.
-fdump-tree-original shows what the front end hands to the gimplifier:
if (__builtin_parity (a) != 0)
The truth-value conversion of 'popcount (a) & 1' builds NE_EXPR <(unsigned)
popcount (a) & 1, 0u>; generic match.pd then rewrites the BIT_AND_EXPR with
/* Canonicalize POPCOUNT(x)&1 as PARITY(X). */
(simplify
(bit_and (POPCOUNT @0) integer_onep)
(PARITY @0))
which has no result type conversion: the PARITY call is built with the real
builtin's return type (int) while the expression it replaces - and the zero
it is compared with - has the user's declared type (unsigned int). Either
the pattern should wrap the result in (convert ...) / check that the call type
matches the builtin's, or the C front end should not keep DECL_BUILT_IN for a
redeclaration whose return type differs.
Found by fuzzing.