https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127748

            Bug ID: 127748
           Summary: [17 Regression] wrong code with popcnt emitted on
                    almost-Hacker's Delight popcnt pattern
           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: gabravier at gmail dot com
                CC: Reshma.Roy at amd dot com
  Target Milestone: ---

unsigned f(unsigned x, unsigned z) {
  x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
  x = (x & 0x0f0f0f0f) + ((x >> 4) & 0x0f0f0f0f);
  x = (x & 0x00ff00ff) + ((x >> 8) & 0x00ff00ff);
  return (z & 0xffff) + (x >> 16);
}

For this code, GCC 16.2 with -O2 -mpopcnt emits:

f:
        mov     eax, edi
        shr     edi
        movzx   esi, si
        and     eax, 1431655765
        and     edi, 1431655765
        add     edi, eax
        mov     eax, edi
        shr     edi, 2
        and     eax, 858993459
        and     edi, 858993459
        add     eax, edi
        mov     edx, eax
        shr     eax, 4
        and     eax, 252645135
        and     edx, 252645135
        add     edx, eax
        mov     eax, edx
        shr     edx, 8
        and     eax, 16711935
        and     edx, 16711935
        add     eax, edx
        shr     eax, 16
        add     eax, esi
        ret

GCC trunk emits:

f:
        xor     eax, eax
        popcnt  eax, edi
        ret

This is incorrect - the result should incorporate the value of z correctly. For
instance, f(0, 1) returns 0 instead of 1.

The cause looks to me to be this:

/* To recognize the popcnt pattern for 32-bit from Hacker's Delight
   int Gia_WordCountOnes32c ( uint32_t uword)
   {
   uword = (uword & 0x55555555) + ((uword>>1) & 0x55555555);
   uword = (uword & 0x33333333) + ((uword>>2) & 0x33333333);
   uword = (uword & 0x0f0f0f0f) + ((uword>>4) & 0x0f0f0f0f);
   uword = (uword & 0x00ff00ff) + ((uword>>8) & 0x00ff00ff);
   return (uword & 0x0000ffff) + (uword>>16);
   or
   return (uword & 0x0000ffff) + ((uword>>16) & 0x0000ffff);
   }
*/

  (simplify
     (plus:c
       (bit_and @step_4 INTEGER_CST@9)
       (rshift
         (plus:c@step4
           (bit_and @step3 INTEGER_CST@7)
        (bit_and
           (rshift
              (plus:c@step3
                  (bit_and @step2 INTEGER_CST@5)
                  (bit_and
                     (rshift
                        (plus:c@step2
                           (bit_and @step1 INTEGER_CST@3)
                           (bit_and
                              (rshift
                                  (plus:c@step1
                                      (bit_and @0 INTEGER_CST@1)
                                      (bit_and (rshift @0 INTEGER_CST@2) @1))
                                  INTEGER_CST@4)
                              INTEGER_CST@3))
                        INTEGER_CST@6)
                     INTEGER_CST@5))
              INTEGER_CST@8)
           INTEGER_CST@7))
         INTEGER_CST@10))
   (with {
    unsigned prec = TYPE_PRECISION (type);
    }
    (if (prec == 32
         && TYPE_UNSIGNED (type)
         && compare_tree_int (@2, 1) == 0
         && compare_tree_int (@4, 2) == 0
         && compare_tree_int (@6, 4) == 0
         && compare_tree_int (@8, 8) == 0
         && compare_tree_int (@10, 16) == 0
         && compare_tree_int (@1, 0x55555555) == 0
         && compare_tree_int (@3, 0x33333333) == 0
         && compare_tree_int (@5, 0x0F0F0F0F) == 0
         && compare_tree_int (@7, 0x00FF00FF) == 0
         && compare_tree_int (@9, 0x0000FFFF) == 0)
       (if (direct_internal_fn_supported_p (IFN_POPCOUNT, type,
                                            OPTIMIZE_FOR_BOTH))
        (convert (IFN_POPCOUNT:type @0))))))

...where "step_4" should be "step4". I presume the fact step_4 doesn't actually
exists makes it degenerate into a wildcard match? At least that's the result
functionally. (perhaps genmatch should warn for this x))

It appears to have been originally caused by r17-489-g8ca1e887847e2f

PS: The ideal output I think would be smth like this:

f:
        movzx   eax, si
        shr     edi, 16
        popcnt  edi, edi
        add     eax, edi
        ret

(untested, but I believe it to be correct)

...although I certainly would not exactly expect it to be a common pattern :p
(I suppose GCC could split the pattern in pieces for `popcnt(x & 0xFFFF)` and
`popcnt(x >> 16)`, leaving this one as `(z & 0xffff) + popcount(x >> 16)` and
then have a separate rule for `popcnt(x & 0xFFFF) + popcount(x >> 16)` - that
latter pattern would perhaps have non-nil other users, even if the former alone
isn't exactly gonna be common)

Reply via email to