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