[Bug tree-optimization/126488] New: [16/17 Regression] Wrong code with (v1 cmp v2) op (v1 icmp v2)

"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=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
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.