[PATCH 3/5] reassoc: Rewrite floating-point plus/mult trees.

Robin Dapp <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
From: Robin Dapp <[email protected]>

This patch adds a wrapper for the multiplicative factors in plus/mult
tree rewriting.  Wrapping plus/mult allows to easily add floating-point
handling to the rewriting functions and also permits extending to
vectors later.  Apart from that, there's no structural change and the
wi::plus/mult operations are done with real_arithmetic instead.
As far as I know there is no further guard necessary as reassoc only
works on floating-point types when -fassociative-math is specified.

	PR tree-optimization/113583

gcc/ChangeLog:

	* tree-ssa-reassoc.cc (constant_maybe_expensive): Add
	floating-point variant.
	(struct mult_factor): Wrapper for multiplicative factor.
	(struct plus_mult_tree_state): Use wrapper for hashmap and
	constant.
	(linearize_plus_mult_tree): Add float handling.
	(expand_plus_mult_tree): Use wrapper.
	(gather_plus_mult_tree_stats): Use wrapper.
	(rewrite_plus_mult_tree): Use wrapper and allow float.
	(reassociate_bb): Allow float for plus/mult.

gcc/testsuite/ChangeLog:

	* gcc.dg/tree-ssa/reassoc-54.c: New test.
	* gcc.dg/tree-ssa/reassoc-55.c: New test.
---
 gcc/testsuite/gcc.dg/tree-ssa/reassoc-54.c |  18 ++
 gcc/testsuite/gcc.dg/tree-ssa/reassoc-55.c |  30 +++
 gcc/tree-ssa-reassoc.cc                    | 244 +++++++++++++++++----
 3 files changed, 248 insertions(+), 44 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/reassoc-54.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/reassoc-55.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-54.c b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-54.c
new file mode 100644
index 00000000000..e95c677545d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-54.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2 -ffast-math -fdump-tree-reassoc-details" } */
+
+double
+foo (double fs)
+{
+  return 0.5 * (fs + 1.0) + 0.25 * (fs + 2.0);
+}
+
+double srcn, feqa, fs;
+
+double
+foo2 (void)
+{
+  return srcn - 0.5 * (4.0 - feqa) + 0.25 * (4.0 + fs);
+}
+
+/* { dg-final { scan-tree-dump-times "Rewriting plus/mult" 2 "reassoc1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/reassoc-55.c b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-55.c
new file mode 100644
index 00000000000..e101cbde5b3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/reassoc-55.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-O2 -ffast-math -fno-tree-vectorize -fdump-tree-reassoc-details" } */
+
+/* This is from PR tree-optimization/113583  */
+
+#define SX 100
+#define SY 100
+#define SZ 130
+#define NN 20
+#define OMEGA 0.123
+#define LAMBDA (1.0 / (0.5 + 3.0 / (16.0 * (1.0 / OMEGA - 0.5))))
+#define CST1 (1.0 / 3.0)
+#define FEQS (CST1 * (1.0 + 4.5 * 1.0 * 1.0))
+
+void
+foo (double *src, double *dst)
+{
+  double avg_diff;
+  unsigned i = 0;
+
+  for (i = 0; i < SX * SY * SZ * NN; i += NN)
+    {
+      avg_diff = 0.5 * (src[i + 1] + src[i + 2]) - FEQS;
+
+      dst[i] = src[i] - OMEGA * (src[i] - CST1 * 1.0);
+      dst[i + 1] = src[i + 1] - OMEGA * avg_diff - LAMBDA * (0.1 - 0.2);
+    }
+}
+
+/* { dg-final { scan-tree-dump-times "Rewriting plus/mult" 2 "reassoc1" } } */
diff --git a/gcc/tree-ssa-reassoc.cc b/gcc/tree-ssa-reassoc.cc
index 211e8778d6c..440c6271e28 100644
--- a/gcc/tree-ssa-reassoc.cc
+++ b/gcc/tree-ssa-reassoc.cc
@@ -1871,6 +1871,148 @@ constant_maybe_expensive (REAL_VALUE_TYPE)
   return false;
 }
 
+static inline bool
+constant_maybe_expensive (tree t)
+{
+  tree type = TREE_TYPE (t);
+  gcc_checking_assert (INTEGRAL_TYPE_P (type)
+			|| SCALAR_FLOAT_TYPE_P (type));
+  if (INTEGRAL_TYPE_P (type))
+    return constant_maybe_expensive (wi::to_wide (t));
+  else
+    return constant_maybe_expensive (TREE_REAL_CST (t));
+}
+
+/* Helper to wrap operations for the constant multiplicative factors in
+   plus/mult trees.  */
+struct mult_factor
+{
+  wide_int cst;
+  REAL_VALUE_TYPE fcst;
+
+  HOST_WIDE_INT prec;
+  tree type;
+  bool is_float;
+
+  bool is_zero ()
+    {
+      if (!is_float)
+	return cst == wi::zero (prec);
+      else
+	return real_equal (&fcst, &dconst0);
+    }
+
+  void mul (tree other)
+    {
+      tree other_type = TREE_TYPE (other);
+      gcc_checking_assert ((!is_float && INTEGRAL_TYPE_P (other_type))
+			   || (is_float && SCALAR_FLOAT_TYPE_P (other_type)));
+      if (!is_float)
+	cst = wi::mul (cst, wi::to_wide (other));
+      else
+	real_arithmetic (&fcst, MULT_EXPR, &fcst,
+			 TREE_REAL_CST_PTR (other));
+    }
+
+  void mul (mult_factor &other)
+    {
+      gcc_checking_assert (is_float == other.is_float);
+      if (!is_float)
+	cst = wi::mul (cst, other.cst);
+      else
+	real_arithmetic (&fcst, MULT_EXPR, &fcst,
+			 &other.fcst);
+    }
+
+  void plus (tree other)
+    {
+      tree other_type = TREE_TYPE (other);
+      gcc_checking_assert ((!is_float && INTEGRAL_TYPE_P (other_type))
+			    || (is_float && SCALAR_FLOAT_TYPE_P (other_type)));
+      if (!is_float)
+	cst = wi::add (cst, wi::to_wide (other));
+      else
+	real_arithmetic (&fcst, PLUS_EXPR, &fcst,
+			 TREE_REAL_CST_PTR (other));
+    }
+
+  void plus (mult_factor &other)
+    {
+      gcc_checking_assert (is_float == other.is_float);
+      if (!is_float)
+	cst = wi::add (cst, other.cst);
+      else
+	real_arithmetic (&fcst, PLUS_EXPR, &fcst,
+			 &other.fcst);
+    }
+
+  void neg ()
+    {
+      if (!is_float)
+	cst = wi::neg (cst);
+      else
+	fcst = real_value_negate (&fcst);
+    }
+
+  bool is_one ()
+    {
+      if (!is_float)
+	return cst == wi::one (prec);
+      else
+	return real_equal (&fcst, &dconst1);
+    }
+
+  bool is_m1 ()
+    {
+      if (!is_float)
+	return cst == wi::minus_one (prec);
+      else
+	return real_equal (&fcst, &dconstm1);
+    }
+
+  bool maybe_expensive ()
+    {
+      if (!is_float)
+	return constant_maybe_expensive (cst);
+      else
+	return constant_maybe_expensive (fcst);
+    }
+
+  tree to_tree ()
+    {
+      if (!is_float)
+	return wide_int_to_tree (type, cst);
+      else
+	{
+	  REAL_VALUE_TYPE r;
+	  real_convert (&r, TYPE_MODE (type), &fcst);
+	  return build_real (type, r);
+	}
+    }
+
+  mult_factor (tree t)
+    {
+      type = TREE_TYPE (t);
+      prec = TYPE_PRECISION (type);
+      if (INTEGRAL_TYPE_P (type))
+	{
+	  is_float = false;
+	  cst = wi::to_wide (t);
+	  fcst = dconst0;
+	}
+      else if (SCALAR_FLOAT_TYPE_P (type))
+	{
+	  is_float = true;
+	  fcst = TREE_REAL_CST (t);
+	}
+      else
+	gcc_unreachable ();
+    }
+
+  mult_factor ()
+    : fcst (dconst0), prec (0), type (NULL_TREE), is_float (false) {}
+};
+
 struct plus_mult_tree_state
 {
   /* Stats for costing.  */
@@ -1888,21 +2030,21 @@ struct plus_mult_tree_state
   /* Operands of the cst/plus/mult sequence in program order.  */
   auto_vec<tree> ops;
   /* Maps SSA names to their multiplicative factors.  */
-  hash_map<tree, wide_int> op_factor_map;
+  hash_map<tree, mult_factor> op_factor_map;
 
   /* Constant factor without SSA name.  */
-  wide_int cst;
+  mult_factor cst;
 
   /* Visited statements.  */
   auto_vec<gimple *> visited;
 
-  plus_mult_tree_state (HOST_WIDE_INT prec)
+  plus_mult_tree_state (tree type)
     : mults_before (0), mults_after (0),
       plus_before (0), plus_after (0),
       maybe_expensive_constants_before (0),
       maybe_expensive_constants_after (0),
       changed (false),
-      cst (wi::zero (prec)) {}
+      cst (build_zero_cst (type)) {}
 };
 
 /* Recursively walk a tree of additions, products, as well as constants
@@ -1916,19 +2058,18 @@ struct plus_mult_tree_state
    of is passed in LOOP.  */
 
 static void
-linearize_plus_mult_tree (tree op, wide_int factor,
+linearize_plus_mult_tree (tree op, mult_factor factor,
 			  struct plus_mult_tree_state *state,
 			  class loop *loop)
 {
   /* Add constants to the "global" constant.  */
-  if (TREE_CODE (op) == INTEGER_CST)
+  if (TREE_CODE (op) == INTEGER_CST || TREE_CODE (op) == REAL_CST)
     {
-      wide_int wcst = wi::to_wide (op);
-      if (constant_maybe_expensive (wcst))
+      if (constant_maybe_expensive (op))
 	state->maybe_expensive_constants_before++;
-      state->cst += wi::mul (wcst, factor);
-      if (state->cst != wi::zero (state->cst.get_precision ())
-	  || factor != wi::one (state->cst.get_precision ()))
+      factor.mul (op);
+      state->cst.plus (factor);
+      if (!state->cst.is_zero () || !factor.is_one ())
 	state->changed = true;
       return;
     }
@@ -1937,7 +2078,9 @@ linearize_plus_mult_tree (tree op, wide_int factor,
   if (TREE_CODE (op) == SSA_NAME
       && can_reassociate_op_p (op)
       && has_single_use (op)
-      && TYPE_OVERFLOW_WRAPS (TREE_TYPE (op)))
+      && ((INTEGRAL_TYPE_P (TREE_TYPE (op))
+	   && TYPE_OVERFLOW_WRAPS (TREE_TYPE (op)))
+	  || SCALAR_FLOAT_TYPE_P (TREE_TYPE (op))))
     {
       gimple *stmt = SSA_NAME_DEF_STMT (op);
       basic_block bb;
@@ -1953,13 +2096,14 @@ linearize_plus_mult_tree (tree op, wide_int factor,
 	  if (can_reassociate_op_p (rhs1)
 	      && (!rhs2 || can_reassociate_op_p (rhs2)))
 	    {
-	      if (opcode == MULT_EXPR && TREE_CODE (rhs2) == INTEGER_CST)
+	      if (opcode == MULT_EXPR
+		  && (TREE_CODE (rhs2) == INTEGER_CST
+		      || TREE_CODE (rhs2) == REAL_CST))
 		{
 		  state->mults_before++;
 		  state->visited.safe_push (stmt);
-		  wide_int wcst = wi::to_wide (rhs2);
-		  linearize_plus_mult_tree (rhs1, wi::mul (wcst, factor),
-					    state, loop);
+		  factor.mul (rhs2);
+		  linearize_plus_mult_tree (rhs1, factor, state, loop);
 		  return;
 		}
 	      else if (opcode == PLUS_EXPR)
@@ -1973,10 +2117,8 @@ linearize_plus_mult_tree (tree op, wide_int factor,
 	      else if (opcode == NEGATE_EXPR)
 		{
 		  state->visited.safe_push (stmt);
-		  wide_int negated
-		    = wi::mul (wi::minus_one (state->cst.get_precision ()),
-			       factor);
-		  linearize_plus_mult_tree (rhs1, negated, state, loop);
+		  factor.neg ();
+		  linearize_plus_mult_tree (rhs1, factor, state, loop);
 		  return;
 		}
 	    }
@@ -1986,8 +2128,12 @@ linearize_plus_mult_tree (tree op, wide_int factor,
   /* We have a leaf.  If we have seen it before, adjust its factor.
      Otherwise, add it to the cache.  */
   bool existed;
-  wide_int &slot = state->op_factor_map.get_or_insert (op, &existed);
-  slot = existed ? slot + factor : factor;
+  mult_factor &slot = state->op_factor_map.get_or_insert (op, &existed);
+  if (existed)
+    slot.plus (factor);
+  else
+    slot = factor;
+
   if (!existed)
     state->ops.safe_push (op);
   else
@@ -2006,7 +2152,6 @@ expand_plus_mult_tree (gimple *stmt, plus_mult_tree_state *state)
 
   tree old_lhs = gimple_get_lhs (stmt);
   tree type = TREE_TYPE (old_lhs);
-  wide_int zero = wi::zero (state->cst.get_precision ());
 
   gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
   location_t loc = gimple_location (stmt);
@@ -2015,10 +2160,12 @@ expand_plus_mult_tree (gimple *stmt, plus_mult_tree_state *state)
   auto_vec<tree> sum_factors;
   for (tree op : state->ops)
     {
-      wide_int factor = *state->op_factor_map.get (op);
-      if (factor == zero)
+      mult_factor &factor = *state->op_factor_map.get (op);
+
+      if (factor.is_zero ())
 	continue;
-      if (factor == wi::minus_one (factor.get_precision ()))
+
+      if (factor.is_m1 ())
 	{
 	  tree nlhs = make_ssa_name (type);
 	  gimple *neg = gimple_build_assign (nlhs, NEGATE_EXPR, op);
@@ -2028,9 +2175,9 @@ expand_plus_mult_tree (gimple *stmt, plus_mult_tree_state *state)
 	  gimple_set_location (neg, loc);
 	  sum_factors.safe_push (gimple_get_lhs (neg));
 	}
-      else if (factor != wi::one (factor.get_precision ()))
+      else if (!factor.is_one ())
 	{
-	  tree cst = wide_int_to_tree (type, factor);
+	  tree cst = factor.to_tree ();
 	  tree nlhs = make_ssa_name (type);
 	  gimple *prod = gimple_build_assign (nlhs, MULT_EXPR, op, cst);
 	  gsi_insert_before (&gsi, prod, GSI_SAME_STMT);
@@ -2044,9 +2191,9 @@ expand_plus_mult_tree (gimple *stmt, plus_mult_tree_state *state)
     }
 
   /* Include the constant factor if there is one.  */
-  if (state->cst != zero)
+  if (!state->cst.is_zero ())
     {
-      tree tcst = wide_int_to_tree (type, state->cst);
+      tree tcst = state->cst.to_tree ();
       sum_factors.safe_push (tcst);
     }
 
@@ -2054,7 +2201,7 @@ expand_plus_mult_tree (gimple *stmt, plus_mult_tree_state *state)
   /* Nothing to be done, zero the old LHS.  */
   if (!n)
     {
-      gimple_assign_set_rhs_from_tree (&gsi, wide_int_to_tree (type, zero));
+      gimple_assign_set_rhs_from_tree (&gsi, build_zero_cst (type));
       update_stmt (stmt);
     }
   /* If we only have one factor, just assign it to the old LHS.  */
@@ -2115,21 +2262,22 @@ gather_plus_mult_tree_stats (struct plus_mult_tree_state *state)
   gcc_checking_assert (state->plus_after == 0);
   gcc_checking_assert (state->mults_after == 0);
 
-  HOST_WIDE_INT prec = state->cst.get_precision ();
-  if (constant_maybe_expensive (state->cst))
+  if (state->cst.maybe_expensive ())
     state->maybe_expensive_constants_after = 1;
+
   for (auto it : state->op_factor_map)
     {
-      if (constant_maybe_expensive (it.second))
+      if (it.second.maybe_expensive ())
 	state->maybe_expensive_constants_after++;
-      if (it.second != wi::zero (prec)
-	  && it.second != wi::one (prec)
-	  && it.second != wi::minus_one (prec))
+      if (!it.second.is_zero ()
+	  && !it.second.is_one ()
+	  && !it.second.is_m1 ())
 	state->mults_after++;
-      if (it.second != wi::zero (prec))
+      if (!it.second.is_zero ())
 	state->plus_after++;
     }
-  if (state->cst != wi::zero (prec))
+
+  if (!state->cst.is_zero ())
     state->plus_after++;
   state->plus_after--;
 }
@@ -2172,14 +2320,19 @@ static bool
 rewrite_plus_mult_tree (gimple *stmt)
 {
   tree lhs = gimple_get_lhs (stmt);
-  gcc_checking_assert (INTEGRAL_TYPE_P (TREE_TYPE (lhs))
+  gcc_checking_assert ((INTEGRAL_TYPE_P (TREE_TYPE (lhs))
+			|| (SCALAR_FLOAT_TYPE_P (TREE_TYPE (lhs))
+			    && !HONOR_NANS (TREE_TYPE (lhs))
+			    && !HONOR_SIGNED_ZEROS (TREE_TYPE (lhs))))
 		       && has_single_use (lhs));
 
-  HOST_WIDE_INT prec = TYPE_PRECISION (TREE_TYPE (lhs));
-  plus_mult_tree_state state (prec);
+  tree type = TREE_TYPE (lhs);
+  plus_mult_tree_state state (type);
+
+  mult_factor one (build_one_cst (type));
 
   /* Linearize the tree and gather stats about it.  */
-  linearize_plus_mult_tree (lhs, wi::one (prec), &state,
+  linearize_plus_mult_tree (lhs, one, &state,
 			    loop_containing_stmt (stmt));
   if (!state.changed)
     return false;
@@ -7506,7 +7659,10 @@ reassociate_bb (basic_block bb)
 		 sum = cst + cst1 * A + cst2 * B + ...
 		 do so.  While at it, remove 0-factor elements.  */
 	      if ((rhs_code == PLUS_EXPR || rhs_code == MULT_EXPR)
-		  && INTEGRAL_TYPE_P (TREE_TYPE (lhs))
+		  && (INTEGRAL_TYPE_P (TREE_TYPE (lhs))
+		      || (SCALAR_FLOAT_TYPE_P (TREE_TYPE (lhs))
+			  && !HONOR_NANS (TREE_TYPE (lhs))
+			  && !HONOR_SIGNED_ZEROS (TREE_TYPE (lhs))))
 		  && has_single_use (lhs)
 		  && !type_can_overflow)
 		{
-- 
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.