[Bug tree-optimization/126490] New: Wrong code with some bitwise folds for bitint

"ktkachov at gcc dot gnu.org via Gcc-bugs" <[email protected]>
Newsgroups gmane.comp.gcc.bugs
Message-ID <[email protected]/bugzilla/>
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126490

            Bug ID: 126490
           Summary: Wrong code with some bitwise folds for bitint
           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: ktkachov at gcc dot gnu.org
                CC: jakub at gcc dot gnu.org
  Target Milestone: ---

The following for aarch64:

/* match.pd:2717-2723

     (for first_op (bit_xor eq)
          second_op (eq bit_xor)
      (simplify
       (first_op:c (bit_and:c truth_valued_p@0 truth_valued_p@1) (second_op @0
@1))
         (bit_not (bit_ior @0 @1))))

   The comment above the rule says "-> !(a | b)" but the transform emits
   a bitwise ~.  ~X equals !X only when the value lives in a type of
   precision 1, and the rule has no condition on the type of the result.
   truth_valued_p @0/@1 are precision-1 values here (unsigned _BitInt(1)),
   but the == that is being replaced has type int, so genmatch builds the
   BIT_NOT_EXPR in int while the BIT_IOR_EXPR keeps the 1-bit type:
   ~(int)(a | b) is -1/-2 where the correct 0/1 answer is 1/0.

   a & b, a ^ b and a == b below are all evaluated in unsigned
   _BitInt(1) / int, so there is no undefined behaviour anywhere.  */

typedef unsigned _BitInt(1) u1;

/* ((a & b) == (a ^ b)) + 1.  Trunk folds the whole body to -(int)(a | b).  */
int __attribute__((noipa))
f_add (u1 a, u1 b)
{
  return ((a & b) == (a ^ b)) + 1;
}

/* ((a & b) == (a ^ b)) != 0.  Trunk folds the whole body to the constant 1,
   because ~(a | b) computed in int is -1 or -2, never zero.  */
int __attribute__((noipa))
f_ne (u1 a, u1 b)
{
  return ((a & b) == (a ^ b)) != 0;
}

/* ((a & b) == (a ^ b)) == 0.  Trunk folds the whole body to the constant 0. 
*/
int __attribute__((noipa))
f_eq (u1 a, u1 b)
{
  return ((a & b) == (a ^ b)) == 0;
}

/* ((a & b) == (a ^ b)) < 1.  Trunk folds the whole body to the constant 1.  */
int __attribute__((noipa))
f_lt (u1 a, u1 b)
{
  return ((a & b) == (a ^ b)) < 1;
}

/* Reference value of (a & b) == (a ^ b) for a, b in { 0, 1 }.  Every step
   goes through a volatile int, so the compiler cannot form the pattern.  */
int __attribute__((noipa))
ref (int ia, int ib)
{
  volatile int a = ia;
  volatile int b = ib;
  volatile int x = a & b;
  volatile int y = a ^ b;
  volatile int e = (x == y);
  return e;
}

int
main (void)
{
  static const int in[4][2] = { { 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 } };
  int i;

  for (i = 0; i < 4; i++)
    {
      int ia = in[i][0];
      int ib = in[i][1];
      u1 a = (u1) ia;
      u1 b = (u1) ib;
      /* a & b equals a ^ b only for a == b == 0, so e is 1, 0, 0, 0.  */
      int e = ref (ia, ib);

      if (f_add (a, b) != e + 1)
        __builtin_abort ();
      if (f_ne (a, b) != (e != 0))
        __builtin_abort ();
      if (f_eq (a, b) != (e == 0))
        __builtin_abort ();
      if (f_lt (a, b) != (e < 1))
        __builtin_abort ();
    }

  return 0;
}

aborts but passes with Clang
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.