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

Reply via email to