vect: Fix up lowering of TRUNC_MOD_EXPR by negative constant [PR94524]
authorJakub Jelinek <jakub@redhat.com>
Wed, 8 Apr 2020 19:22:05 +0000 (21:22 +0200)
committerJakub Jelinek <jakub@redhat.com>
Wed, 8 Apr 2020 19:22:05 +0000 (21:22 +0200)
The first testcase below is miscompiled, because for the division part
of the lowering we canonicalize negative divisors to their absolute value
(similarly how expmed.c canonicalizes it), but when multiplying the division
result back by the VECTOR_CST, we use the original constant, which can
contain negative divisors.

Fixed by computing ABS_EXPR of the VECTOR_CST.  Unfortunately, fold-const.c
doesn't support const_unop (ABS_EXPR, VECTOR_CST) and I think it is too late
in GCC 10 cycle to add it now.

Furthermore, while modulo by most negative constant happens to return the
right value, it does that only by invoking UB in the IL, because
we then expand division by that 1U+INT_MAX and say for INT_MIN % INT_MIN
compute the division as -1, and then multiply by INT_MIN, which is signed
integer overflow.  We in theory could do the computation in unsigned vector
types instead, but is it worth bothering.  People that are doing % INT_MIN
are either testing for standard conformance, or doing something wrong.
So, I've also added punting on % INT_MIN, both in vect lowering and vect
pattern recognition (we punt already for / INT_MIN).

2020-04-08  Jakub Jelinek  <jakub@redhat.com>

PR tree-optimization/94524
* tree-vect-generic.c (expand_vector_divmod): If any elt of op1 is
negative for signed TRUNC_MOD_EXPR, multiply with absolute value of
op1 rather than op1 itself at the end.  Punt for signed modulo by
most negative constant.
* tree-vect-patterns.c (vect_recog_divmod_pattern): Punt for signed
modulo by most negative constant.

* gcc.c-torture/execute/pr94524-1.c: New test.
* gcc.c-torture/execute/pr94524-2.c: New test.

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/pr94524-1.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/pr94524-2.c [new file with mode: 0644]
gcc/tree-vect-generic.c
gcc/tree-vect-patterns.c

index 025a4a679378c371f96f062f0bb99aad38d9fcff..dcdfae1ae8cfb8013b2dd2bc6aeaefbbf07582c6 100644 (file)
@@ -1,3 +1,13 @@
+2020-04-08  Jakub Jelinek  <jakub@redhat.com>
+
+       PR tree-optimization/94524
+       * tree-vect-generic.c (expand_vector_divmod): If any elt of op1 is
+       negative for signed TRUNC_MOD_EXPR, multiply with absolute value of
+       op1 rather than op1 itself at the end.  Punt for signed modulo by
+       most negative constant.
+       * tree-vect-patterns.c (vect_recog_divmod_pattern): Punt for signed
+       modulo by most negative constant.
+
 2020-04-08  Richard Biener  <rguenther@suse.de>
 
        PR rtl-optimization/93946
index 8916b897cc41c2d832af8d657952e27884dc6a6b..134280d1369e8f68c3dad8a91328e4f46368c8d0 100644 (file)
@@ -1,5 +1,9 @@
 2020-04-08  Jakub Jelinek  <jakub@redhat.com>
 
+       PR tree-optimization/94524
+       * gcc.c-torture/execute/pr94524-1.c: New test.
+       * gcc.c-torture/execute/pr94524-2.c: New test.
+
        PR c++/94314
        * g++.dg/pr94314.C (A::operator new, B::operator new, C::operator new):
        Use __SIZE_TYPE__ instead of unsigned long.
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr94524-1.c b/gcc/testsuite/gcc.c-torture/execute/pr94524-1.c
new file mode 100644 (file)
index 0000000..e7365ad
--- /dev/null
@@ -0,0 +1,19 @@
+/* PR tree-optimization/94524 */
+
+typedef signed char __attribute__ ((__vector_size__ (16))) V;
+
+static __attribute__ ((__noinline__, __noclone__)) V
+foo (V c)
+{
+  c %= (signed char) -19;
+  return (V) c;
+}
+
+int
+main ()
+{
+  V x = foo ((V) { 31 });
+  if (x[0] != 12)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr94524-2.c b/gcc/testsuite/gcc.c-torture/execute/pr94524-2.c
new file mode 100644 (file)
index 0000000..9c74b7b
--- /dev/null
@@ -0,0 +1,25 @@
+/* PR tree-optimization/94524 */
+
+typedef signed char __attribute__ ((__vector_size__ (16))) V;
+
+static __attribute__ ((__noinline__, __noclone__)) V
+foo (V c)
+{
+  c %= (signed char) -128;
+  return (V) c;
+}
+
+int
+main ()
+{
+  V x = foo ((V) { -128 });
+  if (x[0] != 0)
+    __builtin_abort ();
+  x = foo ((V) { -127 });
+  if (x[0] != -127)
+    __builtin_abort ();
+  x = foo ((V) { 127 });
+  if (x[0] != 127)
+    __builtin_abort ();
+  return 0;
+}
index 2f6fd5e980c4a32ba1ffbc7ab8e6d69afa97909e..8b00f3250543e1b95630aa372b8a6429318f3d3d 100644 (file)
@@ -478,6 +478,7 @@ expand_vector_divmod (gimple_stmt_iterator *gsi, tree type, tree op0,
 {
   bool use_pow2 = true;
   bool has_vector_shift = true;
+  bool use_abs_op1 = false;
   int mode = -1, this_mode;
   int pre_shift = -1, post_shift;
   unsigned int nunits = nunits_for_known_piecewise_op (type);
@@ -618,8 +619,11 @@ expand_vector_divmod (gimple_stmt_iterator *gsi, tree type, tree op0,
 
          /* n rem d = n rem -d */
          if (code == TRUNC_MOD_EXPR && d < 0)
-           d = abs_d;
-         else if (abs_d == HOST_WIDE_INT_1U << (prec - 1))
+           {
+             d = abs_d;
+             use_abs_op1 = true;
+           }
+         if (abs_d == HOST_WIDE_INT_1U << (prec - 1))
            {
              /* This case is not handled correctly below.  */
              mode = -2;
@@ -899,6 +903,23 @@ expand_vector_divmod (gimple_stmt_iterator *gsi, tree type, tree op0,
   if (op == unknown_optab
       || optab_handler (op, TYPE_MODE (type)) == CODE_FOR_nothing)
     return NULL_TREE;
+  if (use_abs_op1)
+    {
+      tree_vector_builder elts;
+      if (!elts.new_unary_operation (type, op1, false))
+       return NULL_TREE;
+      unsigned int count = elts.encoded_nelts ();
+      for (unsigned int i = 0; i < count; ++i)
+       {
+         tree elem1 = VECTOR_CST_ELT (op1, i);
+
+         tree elt = const_unop (ABS_EXPR, TREE_TYPE (elem1), elem1);
+         if (elt == NULL_TREE)
+           return NULL_TREE;
+         elts.quick_push (elt);
+       }
+      op1 = elts.build ();
+    }
   tem = gimplify_build2 (gsi, MULT_EXPR, type, cur_op, op1);
   op = optab_for_tree_code (MINUS_EXPR, type, optab_default);
   if (op == unknown_optab
index 60dc1bfeaa405be60ef5a4bac1aca309ea359c8c..dd0c19dc5e50e3cc11e0aa1a86a22114fdee20c5 100644 (file)
@@ -3365,8 +3365,8 @@ vect_recog_divmod_pattern (stmt_vec_info stmt_vinfo, tree *type_out)
          d = abs_d;
          oprnd1 = build_int_cst (itype, abs_d);
        }
-      else if (HOST_BITS_PER_WIDE_INT >= prec
-              && abs_d == HOST_WIDE_INT_1U << (prec - 1))
+      if (HOST_BITS_PER_WIDE_INT >= prec
+         && abs_d == HOST_WIDE_INT_1U << (prec - 1))
        /* This case is not handled correctly below.  */
        return NULL;