[PATCH 5/5] reassoc: Rewrite signed plus/mult trees.

Robin Dapp <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
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
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.