https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126488
Bug ID: 126488
Summary: [16/17 Regression] Wrong code with (v1 cmp v2) op (v1
icmp v2)
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
Target Milestone: ---
On aarch64:
/* match.pd:3761-3771 (bit_xor/ne) and match.pd:3774-3783 (eq) rewrite a pair
of comparisons on the same operands into a single comparison. That is only
valid when exactly one of {<, ==, >} holds for every operand pair. The
scalar arms of the guard establish this with INTEGRAL_TYPE_P (TREE_TYPE
(@0)) || POINTER_TYPE_P (TREE_TYPE (@0)). The vector arm drops that
requirement and substitutes expand_vec_cmp_expr_p (gcc/optabs-tree.cc:447),
which is only vec_cmp_icode_p || vec_cmp_eq_icode_p, a target-capability
query. It says nothing about the element type and there is no HONOR_NANS
test in either rule, so a vector of float matches. On a NaN lane <, <=, >
and >= are all false and != is true, an outcome no integer or pointer pair
can produce, so the rewrites change the value. */
typedef float v4sf __attribute__((vector_size (16)));
typedef int v4si __attribute__((vector_size (16)));
/* The folded forms. noipa keeps the arguments opaque. */
__attribute__((noipa)) v4si f_xor_lt_gt (v4sf a, v4sf b) { return (a < b) ^
(a > b); }
__attribute__((noipa)) v4si f_ne_lt_gt (v4sf a, v4sf b) { return (a < b) !=
(a > b); }
__attribute__((noipa)) v4si f_eq_lt_gt (v4sf a, v4sf b) { return (a < b) ==
(a > b); }
__attribute__((noipa)) v4si f_xor_le_ge (v4sf a, v4sf b) { return (a <= b) ^
(a >= b); }
__attribute__((noipa)) v4si f_eq_le_ge (v4sf a, v4sf b) { return (a <= b) ==
(a >= b); }
__attribute__((noipa)) v4si f_xor_lt_ne (v4sf a, v4sf b) { return (a < b) ^
(a != b); }
__attribute__((noipa)) v4si f_eq_lt_eq (v4sf a, v4sf b) { return (a < b) ==
(a == b); }
__attribute__((noipa)) v4si f_eq_le_eq (v4sf a, v4sf b) { return (a <= b) ==
(a == b); }
/* Reference: one scalar predicate at a time, each operand fetched from a
volatile object, so no rule can span a pair of comparisons. */
static volatile float fx, fy;
__attribute__((noipa)) int p_lt (void) { return fx < fy ? -1 : 0; }
__attribute__((noipa)) int p_gt (void) { return fx > fy ? -1 : 0; }
__attribute__((noipa)) int p_le (void) { return fx <= fy ? -1 : 0; }
__attribute__((noipa)) int p_ge (void) { return fx >= fy ? -1 : 0; }
__attribute__((noipa)) int p_eq (void) { return fx == fy ? -1 : 0; }
__attribute__((noipa)) int p_ne (void) { return fx != fy ? -1 : 0; }
/* Elementwise combiners matching GNU vector semantics: ^ is bitwise on the
0/-1 lane masks, == and != produce 0/-1. */
__attribute__((noipa)) int c_xor (int m1, int m2) { return m1 ^ m2; }
__attribute__((noipa)) int c_eq (int m1, int m2) { return m1 == m2 ? -1 : 0; }
__attribute__((noipa)) int c_ne (int m1, int m2) { return m1 != m2 ? -1 : 0; }
static int fails;
__attribute__((noipa)) static void chk (v4si got, const int *want)
{
for (int k = 0; k < 4; k++)
if (got[k] != want[k])
fails++;
}
volatile float qnan_src;
int main (void)
{
qnan_src = __builtin_nanf ("");
float qnan = qnan_src;
/* lane 0: NaN vs 1 (unordered). lane 1: 1 < 2. lane 2: 2 == 2.
lane 3: 3 > 1. */
v4sf a = { qnan, 1.0f, 2.0f, 3.0f };
v4sf b = { 1.0f, 2.0f, 2.0f, 1.0f };
int w_xor_lt_gt[4], w_ne_lt_gt[4], w_eq_lt_gt[4];
int w_xor_le_ge[4], w_eq_le_ge[4];
int w_xor_lt_ne[4], w_eq_lt_eq[4], w_eq_le_eq[4];
for (int k = 0; k < 4; k++)
{
fx = a[k];
fy = b[k];
int lt = p_lt (), gt = p_gt (), le = p_le ();
int ge = p_ge (), eq = p_eq (), ne = p_ne ();
w_xor_lt_gt[k] = c_xor (lt, gt);
w_ne_lt_gt[k] = c_ne (lt, gt);
w_eq_lt_gt[k] = c_eq (lt, gt);
w_xor_le_ge[k] = c_xor (le, ge);
w_eq_le_ge[k] = c_eq (le, ge);
w_xor_lt_ne[k] = c_xor (lt, ne);
w_eq_lt_eq[k] = c_eq (lt, eq);
w_eq_le_eq[k] = c_eq (le, eq);
}
chk (f_xor_lt_gt (a, b), w_xor_lt_gt);
chk (f_ne_lt_gt (a, b), w_ne_lt_gt);
chk (f_eq_lt_gt (a, b), w_eq_lt_gt);
chk (f_xor_le_ge (a, b), w_xor_le_ge);
chk (f_eq_le_ge (a, b), w_eq_le_ge);
chk (f_xor_lt_ne (a, b), w_xor_lt_ne);
chk (f_eq_lt_eq (a, b), w_eq_lt_eq);
chk (f_eq_le_eq (a, b), w_eq_le_eq);
if (fails != 0)
__builtin_abort ();
return 0;
}
aborts since GCC 16