From: Robin Dapp <[email protected]>

With the wrappers in place, it's easy to add overflow handling for the
signed/plus rewriting.  This patch checks for overflow when adding or
multiplying the multiplicative factors using wi::overflow_type and tries
to prove factor * op overflow through ranges.  If we encounter an overflow, we
cancel the descent and don't rewrite.  Otherwise we can safely continue.

        PR tree-optimization/122209

gcc/ChangeLog:

        * tree-ssa-reassoc.cc (struct mult_factor): Handle overflows
        in plus/mul.
        (struct plus_mult_tree_state): Add overflow flag.
        (linearize_plus_mult_tree): Handle overflow.
        (rewrite_plus_mult_tree): Cancel on overflow.
        (reassociate_bb): Allow overflow for plus/mult rewrite.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/reassoc-52.c: Remove xfail.
        * gcc.dg/tree-ssa/reassoc-56.c: Likewise.
        * gcc.dg/tree-ssa/reassoc-58.c: New test.
---
 gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c |   4 +-
 gcc/testsuite/gcc.dg/tree-ssa/reassoc-58.c |  30 ++++
 gcc/testsuite/gcc.dg/tree-ssa/reassoc-59.c |  10 ++
 gcc/tree-ssa-reassoc.cc                    | 157 +++++++++++++++++----
 4 files changed, 172 insertions(+), 29 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/reassoc-58.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/reassoc-59.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c 
b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c
index 5a194a8c9dd..37a779fb0ab 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-52.c
@@ -45,5 +45,5 @@ baz (char fs)
 /* { dg-final { scan-tree-dump-times "\\* 14;" 1 "optimized" } } */
 /* { dg-final { scan-tree-dump-times "\\* 26;" 1 "optimized" } } */
 /* { dg-final { scan-tree-dump-times "\\* 5;" 1 "optimized" } } */
-/* { dg-final { scan-tree-dump-times "\\* 6;" 1 "optimized" { xfail *-*-* } } 
} */
-/* { dg-final { scan-tree-dump-times "\\* 80;" 1 "optimized" { xfail *-*-* } } 
} */
+/* { dg-final { scan-tree-dump-times "\\* 6;" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\* 80;" 1 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-58.c 
b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-58.c
new file mode 100644
index 00000000000..65eafcaf026
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-58.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2 -fdump-tree-reassoc-details 
-fdump-tree-optimized" } */
+
+int
+foo (int t)
+{
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+  t += 3 + 3 * t;
+  t &= 0xFFF;
+
+  return t;
+}
+
+/* { dg-final { scan-tree-dump "4095" "optimized" } } */
+/* { dg-final { scan-tree-dump-times "Rewriting plus/mult" 8 "reassoc1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-59.c 
b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-59.c
new file mode 100644
index 00000000000..804bcf12ce9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-59.c
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2 -fdump-tree-reassoc-details 
-fdump-tree-optimized" } */
+
+int
+foo (int t)
+{
+  return t + 0x3fffffff + 0x3fffffff + t;
+}
+
+/* { dg-final { scan-tree-dump-times "cannot prove no overflow" 2 "reassoc1" } 
} */
diff --git a/gcc/tree-ssa-reassoc.cc b/gcc/tree-ssa-reassoc.cc
index 54a7be64e54..c99cb953f0c 100644
--- a/gcc/tree-ssa-reassoc.cc
+++ b/gcc/tree-ssa-reassoc.cc
@@ -1916,7 +1916,7 @@ struct mult_factor
        return real_equal (&fcst, &dconst0);
     }
 
-  void mul (tree other)
+  bool mul (tree other)
     {
       tree other_type = TREE_TYPE (other);
       gcc_checking_assert ((!is_float
@@ -1929,24 +1929,19 @@ struct mult_factor
        other = uniform_vector_p (other);
 
       if (!is_float)
-       cst = wi::mul (cst, wi::to_wide (other));
+       {
+         wi::overflow_type ovf;
+         cst = wi::mul (cst, wi::to_wide (other), TYPE_SIGN (type), &ovf);
+         if (!TYPE_OVERFLOW_WRAPS (type) && ovf != wi::OVF_NONE)
+           return false;
+       }
       else
        real_arithmetic (&fcst, MULT_EXPR, &fcst,
                         TREE_REAL_CST_PTR (other));
+      return true;
     }
 
-  void mul (mult_factor &other)
-    {
-      gcc_checking_assert (is_float == other.is_float);
-      gcc_checking_assert (is_vector == other.is_vector);
-      if (!is_float)
-       cst = wi::mul (cst, other.cst);
-      else
-       real_arithmetic (&fcst, MULT_EXPR, &fcst,
-                        &other.fcst);
-    }
-
-  void plus (tree other)
+  bool plus (tree other)
     {
       tree other_type = TREE_TYPE (other);
       gcc_checking_assert ((!is_float
@@ -1959,29 +1954,47 @@ struct mult_factor
        other = uniform_vector_p (other);
 
       if (!is_float)
-       cst = wi::add (cst, wi::to_wide (other));
+       {
+         wi::overflow_type ovf;
+         cst = wi::add (cst, wi::to_wide (other), TYPE_SIGN (type), &ovf);
+         if (!TYPE_OVERFLOW_WRAPS (type) && ovf != wi::OVF_NONE)
+           return false;
+       }
       else
        real_arithmetic (&fcst, PLUS_EXPR, &fcst,
                         TREE_REAL_CST_PTR (other));
+      return true;
     }
 
-  void plus (mult_factor &other)
+  bool plus (mult_factor &other)
     {
       gcc_checking_assert (is_float == other.is_float);
       gcc_checking_assert (is_vector == other.is_vector);
       if (!is_float)
-       cst = wi::add (cst, other.cst);
+       {
+         wi::overflow_type ovf;
+         cst = wi::add (cst, other.cst, TYPE_SIGN (type), &ovf);
+         if (!TYPE_OVERFLOW_WRAPS (type) && ovf != wi::OVF_NONE)
+           return false;
+       }
       else
        real_arithmetic (&fcst, PLUS_EXPR, &fcst,
                         &other.fcst);
+      return true;
     }
 
-  void neg ()
+  bool neg ()
     {
       if (!is_float)
-       cst = wi::neg (cst);
+       {
+         wi::overflow_type ovf;
+         cst = wi::neg (cst, &ovf);
+         if (!TYPE_OVERFLOW_WRAPS (type) && ovf != wi::OVF_NONE)
+           return false;
+       }
       else
        fcst = real_value_negate (&fcst);
+      return true;
     }
 
   bool is_one ()
@@ -2072,6 +2085,9 @@ struct plus_mult_tree_state
      be considered not worthwhile according to costing.  */
   bool changed;
 
+  /* True if we introduced overflow.  */
+  bool overflow;
+
   /* Operands of the cst/plus/mult sequence in program order.  */
   auto_vec<tree> ops;
   /* Maps SSA names to their multiplicative factors.  */
@@ -2089,6 +2105,7 @@ struct plus_mult_tree_state
       maybe_expensive_constants_before (0),
       maybe_expensive_constants_after (0),
       changed (false),
+      overflow (false),
       cst (build_zero_cst (type)) {}
 };
 
@@ -2115,8 +2132,18 @@ linearize_plus_mult_tree (tree op, mult_factor factor,
     {
       if (constant_maybe_expensive (op))
        state->maybe_expensive_constants_before++;
-      factor.mul (op);
-      state->cst.plus (factor);
+      bool ok = factor.mul (op);
+      if (!ok)
+       {
+         state->overflow = true;
+         return;
+       }
+      ok = state->cst.plus (factor);
+      if (!ok)
+       {
+         state->overflow = true;
+         return;
+       }
       if (!state->cst.is_zero () || !factor.is_one ())
        state->changed = true;
       return;
@@ -2128,7 +2155,7 @@ linearize_plus_mult_tree (tree op, mult_factor factor,
       && has_single_use (op)
       && (((INTEGRAL_TYPE_P (TREE_TYPE (op))
            || VECTOR_INTEGER_TYPE_P (TREE_TYPE (op)))
-          && TYPE_OVERFLOW_WRAPS (TREE_TYPE (op)))
+          /*&& TYPE_OVERFLOW_WRAPS (TREE_TYPE (op))*/)
          || FLOAT_TYPE_P (TREE_TYPE (op))))
     {
       gimple *stmt = SSA_NAME_DEF_STMT (op);
@@ -2154,7 +2181,12 @@ linearize_plus_mult_tree (tree op, mult_factor factor,
                {
                  state->mults_before++;
                  state->visited.safe_push (stmt);
-                 factor.mul (rhs2);
+                 bool ok = factor.mul (rhs2);
+                 if (!ok)
+                   {
+                     state->overflow = true;
+                     return;
+                   }
                  linearize_plus_mult_tree (rhs1, factor, state, loop);
                  return;
                }
@@ -2169,7 +2201,12 @@ linearize_plus_mult_tree (tree op, mult_factor factor,
              else if (opcode == NEGATE_EXPR)
                {
                  state->visited.safe_push (stmt);
-                 factor.neg ();
+                 bool ok = factor.neg ();
+                 if (!ok)
+                   {
+                     state->overflow = true;
+                     return;
+                   }
                  linearize_plus_mult_tree (rhs1, factor, state, loop);
                  return;
                }
@@ -2182,7 +2219,14 @@ linearize_plus_mult_tree (tree op, mult_factor factor,
   bool existed;
   mult_factor &slot = state->op_factor_map.get_or_insert (op, &existed);
   if (existed)
-    slot.plus (factor);
+    {
+      bool ok = slot.plus (factor);
+      if (!ok)
+       {
+         state->overflow = true;
+         return;
+       }
+    }
   else
     slot = factor;
 
@@ -2355,6 +2399,44 @@ cost_plus_mult_tree (plus_mult_tree_state *state)
   return worthwhile;
 }
 
+/* Return true if the tree in rewritten form as described by STATE can
+   overflow or false otherwise.
+   This relies on the ranges of the linearized ops.  We check the overflow
+    mult_factor * OP
+   as well as the partial sum up to OP.  */
+
+static bool
+plus_mult_tree_overflow_p (struct plus_mult_tree_state *state)
+{
+  wide_int sum_lo = state->cst.cst;
+  wide_int sum_hi = state->cst.cst;
+  for (tree op : state->ops)
+    {
+      mult_factor &factor = *state->op_factor_map.get (op);
+      if (factor.is_zero ())
+       continue;
+      int_range_max r;
+      get_range_query (cfun)->range_of_expr (r, op);
+
+      wi::overflow_type ovf1, ovf2;
+      wide_int lo1 = wi::mul (r.lower_bound (), factor.cst, TYPE_SIGN
+                             (TREE_TYPE (op)), &ovf1);
+      wide_int hi1 = wi::mul (r.upper_bound (), factor.cst, TYPE_SIGN
+                             (TREE_TYPE (op)), &ovf2);
+      if (ovf1 != wi::OVF_NONE || ovf2 != wi::OVF_NONE)
+       return true;
+
+      sum_hi = wi::add (sum_hi, wi::smax (hi1, lo1),
+                       TYPE_SIGN (TREE_TYPE (op)), &ovf2);
+      sum_lo = wi::add (sum_lo, wi::smin (lo1, hi1),
+                       TYPE_SIGN (TREE_TYPE (op)), &ovf1);
+      if (ovf1 != wi::OVF_NONE || ovf2 != wi::OVF_NONE)
+       return true;
+    }
+
+  return false;
+}
+
 /* Try to rewrite/expand a tree consisting of additions, multiplications
    by constants, and negates like
      a + 3 * (a + b + 1) + 2
@@ -2389,8 +2471,30 @@ rewrite_plus_mult_tree (gimple *stmt)
   if (!state.changed)
     return false;
 
+  if (state.overflow)
+    return false;
+
   gather_plus_mult_tree_stats (&state);
 
+  /* Check for overflow.  */
+  if ((INTEGRAL_TYPE_P (type)
+       || VECTOR_INTEGER_TYPE_P (type))
+      && TYPE_OVERFLOW_UNDEFINED (type))
+    {
+      if ((INTEGRAL_TYPE_P (type)
+          && plus_mult_tree_overflow_p (&state))
+         || VECTOR_INTEGER_TYPE_P (type))
+       {
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Not rewriting plus/mult tree at ");
+             print_generic_expr (dump_file, lhs);
+             fprintf (dump_file,  ": cannot prove no overflow.\n");
+           }
+         return false;
+       }
+    }
+
   /* Check if it's worthwhile to follow through with the expansion.  */
   bool worthwhile = cost_plus_mult_tree (&state);
 
@@ -7716,8 +7820,7 @@ reassociate_bb (basic_block bb)
                      || (FLOAT_TYPE_P (TREE_TYPE (lhs))
                          && !HONOR_NANS (TREE_TYPE (lhs))
                          && !HONOR_SIGNED_ZEROS (TREE_TYPE (lhs))))
-                 && has_single_use (lhs)
-                 && !type_can_overflow)
+                 && has_single_use (lhs))
                {
                  if (rewrite_plus_mult_tree (stmt))
                    {
-- 
2.54.0

Reply via email to