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.

Reply via email to