diff mbox series

[COMMITTED] Rewrite bounds_of_var_in_loop() to use ranges.

Message ID 20230501062906.564803-9-aldyh@redhat.com
State New
Headers show
Series [COMMITTED] Rewrite bounds_of_var_in_loop() to use ranges. | expand

Commit Message

Aldy Hernandez May 1, 2023, 6:29 a.m. UTC
Little by little, bounds_of_var_in_loop() has grown into an
unmaintainable mess.  This patch rewrites the code to use the relevant
APIs as well as refactor it to make it more readable.

gcc/ChangeLog:

	* gimple-range-fold.cc (tree_lower_bound): Delete.
	(tree_upper_bound): Delete.
	(vrp_val_max): Delete.
	(vrp_val_min): Delete.
	(fold_using_range::range_of_ssa_name_with_loop_info): Call
	range_of_var_in_loop.
	* vr-values.cc (valid_value_p): Delete.
	(fix_overflow): Delete.
	(get_scev_info): New.
	(bounds_of_var_in_loop): Refactor into...
	(induction_variable_may_overflow_p): ...this,
	(range_from_loop_direction): ...and this,
	(range_of_var_in_loop): ...and this.
	* vr-values.h (bounds_of_var_in_loop): Delete.
	(range_of_var_in_loop): New.
---
 gcc/gimple-range-fold.cc |  80 +----------
 gcc/vr-values.cc         | 282 ++++++++++++++++-----------------------
 gcc/vr-values.h          |   4 +-
 3 files changed, 117 insertions(+), 249 deletions(-)
diff mbox series

Patch

diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index 1b76e6e02a3..96cbd799488 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -944,60 +944,6 @@  fold_using_range::range_of_cond_expr  (vrange &r, gassign *s, fur_source &src)
   return true;
 }
 
-// Return the lower bound of R as a tree.
-
-static inline tree
-tree_lower_bound (const vrange &r, tree type)
-{
-  if (is_a <irange> (r))
-    return wide_int_to_tree (type, as_a <irange> (r).lower_bound ());
-  // ?? Handle floats when they contain endpoints.
-  return NULL;
-}
-
-// Return the upper bound of R as a tree.
-
-static inline tree
-tree_upper_bound (const vrange &r, tree type)
-{
-  if (is_a <irange> (r))
-    return wide_int_to_tree (type, as_a <irange> (r).upper_bound ());
-  // ?? Handle floats when they contain endpoints.
-  return NULL;
-}
-
-// Return the maximum value for TYPE.
-
-static inline tree
-vrp_val_max (const_tree type)
-{
-  if (INTEGRAL_TYPE_P (type)
-      || POINTER_TYPE_P (type))
-    return wide_int_to_tree (const_cast <tree> (type), irange_val_max (type));
-  if (frange::supports_p (type))
-    {
-      REAL_VALUE_TYPE r = frange_val_max (type);
-      return build_real (const_cast <tree> (type), r);
-    }
-  return NULL_TREE;
-}
-
-// Return the minimum value for TYPE.
-
-static inline tree
-vrp_val_min (const_tree type)
-{
-  if (INTEGRAL_TYPE_P (type)
-      || POINTER_TYPE_P (type))
-    return wide_int_to_tree (const_cast <tree> (type), irange_val_min (type));
-  if (frange::supports_p (type))
-    {
-      REAL_VALUE_TYPE r = frange_val_min (type);
-      return build_real (const_cast <tree> (type), r);
-    }
-  return NULL_TREE;
-}
-
 // If SCEV has any information about phi node NAME, return it as a range in R.
 
 void
@@ -1006,30 +952,8 @@  fold_using_range::range_of_ssa_name_with_loop_info (vrange &r, tree name,
 						    fur_source &src)
 {
   gcc_checking_assert (TREE_CODE (name) == SSA_NAME);
-  tree min, max, type = TREE_TYPE (name);
-  if (bounds_of_var_in_loop (&min, &max, src.query (), l, phi, name))
-    {
-      if (!is_gimple_constant (min))
-	{
-	  if (src.query ()->range_of_expr (r, min, phi) && !r.undefined_p ())
-	    min = tree_lower_bound (r, type);
-	  else
-	    min = vrp_val_min (type);
-	}
-      if (!is_gimple_constant (max))
-	{
-	  if (src.query ()->range_of_expr (r, max, phi) && !r.undefined_p ())
-	    max = tree_upper_bound (r, type);
-	  else
-	    max = vrp_val_max (type);
-	}
-      if (min && max)
-	{
-	  r.set (min, max);
-	  return;
-	}
-    }
-  r.set_varying (type);
+  if (!range_of_var_in_loop (r, name, l, phi, src.query ()))
+    r.set_varying (TREE_TYPE (name));
 }
 
 // -----------------------------------------------------------------------
diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc
index 31df6b85ce6..86c1bf8ebc6 100644
--- a/gcc/vr-values.cc
+++ b/gcc/vr-values.cc
@@ -52,23 +52,6 @@  along with GCC; see the file COPYING3.  If not see
 #include "range-op.h"
 #include "gimple-range.h"
 
-/* Returns true if EXPR is a valid value (as expected by compare_values) --
-   a gimple invariant, or SSA_NAME +- CST.  */
-
-static bool
-valid_value_p (tree expr)
-{
-  if (TREE_CODE (expr) == SSA_NAME)
-    return true;
-
-  if (TREE_CODE (expr) == PLUS_EXPR
-      || TREE_CODE (expr) == MINUS_EXPR)
-    return (TREE_CODE (TREE_OPERAND (expr, 0)) == SSA_NAME
-	    && TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST);
-
-  return is_gimple_min_invariant (expr);
-}
-
 /* Return true if op is in a boolean [0, 1] value-range.  */
 
 bool
@@ -184,178 +167,139 @@  check_for_binary_op_overflow (range_query *query,
   return true;
 }
 
-static inline void
-fix_overflow (tree *min, tree *max)
+/* Set INIT, STEP, and DIRECTION the the corresponding values of NAME
+   within LOOP, and return TRUE.  Otherwise return FALSE, and set R to
+   the conservative range of NAME within the loop.  */
+
+static bool
+get_scev_info (vrange &r, tree name, gimple *stmt, class loop *l,
+	       tree &init, tree &step, enum ev_direction &dir)
 {
-  /* Even for valid range info, sometimes overflow flag will leak in.
-     As GIMPLE IL should have no constants with TREE_OVERFLOW set, we
-     drop them.  */
-  if (TREE_OVERFLOW_P (*min))
-    *min = drop_tree_overflow (*min);
-  if (TREE_OVERFLOW_P (*max))
-    *max = drop_tree_overflow (*max);
-
-  gcc_checking_assert (compare_values (*min, *max) != 1);
+  tree ev = analyze_scalar_evolution (l, name);
+  tree chrec = instantiate_parameters (l, ev);
+  tree type = TREE_TYPE (name);
+  if (TREE_CODE (chrec) != POLYNOMIAL_CHREC)
+    {
+      r.set_varying (type);
+      return false;
+    }
+  if (is_gimple_min_invariant (chrec))
+    {
+      if (is_gimple_constant (chrec))
+	r.set (chrec, chrec);
+      else
+	r.set_varying (type);
+      return false;
+    }
+
+  init = initial_condition_in_loop_num (chrec, l->num);
+  step = evolution_part_in_loop_num (chrec, l->num);
+  if (!init || !step)
+    {
+      r.set_varying (type);
+      return false;
+    }
+  dir = scev_direction (chrec);
+  if (dir == EV_DIR_UNKNOWN
+      || scev_probably_wraps_p (NULL, init, step, stmt,
+				get_chrec_loop (chrec), true))
+    {
+      r.set_varying (type);
+      return false;
+    }
+  return true;
 }
 
-/* Given a VAR in STMT within LOOP, determine the bounds of the
-   variable and store it in MIN/MAX and return TRUE.  If no bounds
-   could be determined, return FALSE.  */
+/* Return TRUE if STEP * NIT may overflow when calculated in TYPE.  */
 
-bool
-bounds_of_var_in_loop (tree *min, tree *max, range_query *query,
-		       class loop *loop, gimple *stmt, tree var)
+static bool
+induction_variable_may_overflow_p (tree type,
+				   const wide_int &step, const widest_int &nit)
 {
-  tree init, step, chrec, tmin, tmax, type = TREE_TYPE (var);
-  enum ev_direction dir;
-  int_range<2> r;
+  wi::overflow_type ovf;
+  signop sign = TYPE_SIGN (type);
+  widest_int max_step = wi::mul (widest_int::from (step, sign),
+				 nit, sign, &ovf);
 
-  chrec = instantiate_parameters (loop, analyze_scalar_evolution (loop, var));
+  if (ovf || !wi::fits_to_tree_p (max_step, type))
+    return true;
 
-  /* Like in PR19590, scev can return a constant function.  */
-  if (is_gimple_min_invariant (chrec))
-    {
-      *min = *max = chrec;
-      fix_overflow (min, max);
-      return true;
-    }
+  /* For a signed type we have to check whether the result has the
+     expected signedness which is that of the step as number of
+     iterations is unsigned.  */
+  return (sign == SIGNED
+	  && wi::gts_p (max_step, 0) != wi::gts_p (step, 0));
+}
 
-  if (TREE_CODE (chrec) != POLYNOMIAL_CHREC)
-    return false;
+/* Set R to the range from BEGIN to END, assuming the direction of the
+   loop is DIR.  */
 
-  init = initial_condition_in_loop_num (chrec, loop->num);
-  step = evolution_part_in_loop_num (chrec, loop->num);
+static void
+range_from_loop_direction (irange &r, tree type,
+			   const irange &begin, const irange &end,
+			   ev_direction dir)
+{
+  signop sign = TYPE_SIGN (type);
 
-  if (!init || !step)
-    return false;
+  if (begin.undefined_p () || end.undefined_p ())
+    r.set_varying (type);
+  else if (dir == EV_DIR_GROWS)
+    {
+      if (wi::gt_p (begin.lower_bound (), end.upper_bound (), sign))
+	r.set_varying (type);
+      else
+	r = int_range<1> (type, begin.lower_bound (), end.upper_bound ());
+    }
+  else
+    {
+      if (wi::gt_p (end.lower_bound (), begin.upper_bound (), sign))
+	r.set_varying (type);
+      else
+	r = int_range<1> (type, end.lower_bound (), begin.upper_bound ());
+    }
+}
 
-  Value_Range rinit (TREE_TYPE (init));
-  Value_Range rstep (TREE_TYPE (step));
-  /* If INIT is an SSA with a singleton range, set INIT to said
-     singleton, otherwise leave INIT alone.  */
-  if (TREE_CODE (init) == SSA_NAME
-      && query->range_of_expr (rinit, init, stmt))
-    rinit.singleton_p (&init);
-  /* Likewise for step.  */
-  if (TREE_CODE (step) == SSA_NAME
-      && query->range_of_expr (rstep, step, stmt))
-    rstep.singleton_p (&step);
-
-  /* If STEP is symbolic, we can't know whether INIT will be the
-     minimum or maximum value in the range.  Also, unless INIT is
-     a simple expression, compare_values and possibly other functions
-     in tree-vrp won't be able to handle it.  */
-  if (step == NULL_TREE
-      || !is_gimple_min_invariant (step)
-      || !valid_value_p (init))
-    return false;
+/* Set V to the range of NAME in STMT within LOOP.  Return TRUE if a
+   range was found.  */
 
-  dir = scev_direction (chrec);
-  if (/* Do not adjust ranges if we do not know whether the iv increases
-	 or decreases,  ... */
-      dir == EV_DIR_UNKNOWN
-      /* ... or if it may wrap.  */
-      || scev_probably_wraps_p (NULL_TREE, init, step, stmt,
-				get_chrec_loop (chrec), true))
+bool
+range_of_var_in_loop (vrange &v, tree name, class loop *l, gimple *stmt,
+		      range_query *query)
+{
+  tree init, step;
+  enum ev_direction dir;
+  if (!get_scev_info (v, name, stmt, l, init, step, dir))
+    return true;
+
+  // Calculate ranges for the values from SCEV.
+  irange &r = as_a <irange> (v);
+  tree type = TREE_TYPE (init);
+  int_range<2> rinit (type), rstep (type), max_init (type);
+  if (!query->range_of_expr (rinit, init, stmt)
+      || !query->range_of_expr (rstep, step, stmt))
     return false;
 
-  if (POINTER_TYPE_P (type) || !TYPE_MIN_VALUE (type))
-    tmin = lower_bound_in_type (type, type);
-  else
-    tmin = TYPE_MIN_VALUE (type);
-  if (POINTER_TYPE_P (type) || !TYPE_MAX_VALUE (type))
-    tmax = upper_bound_in_type (type, type);
-  else
-    tmax = TYPE_MAX_VALUE (type);
-
-  /* Try to use estimated number of iterations for the loop to constrain the
-     final value in the evolution.  */
-  if (TREE_CODE (step) == INTEGER_CST
-      && is_gimple_val (init)
-      && (TREE_CODE (init) != SSA_NAME
-	  || (query->range_of_expr (r, init, stmt)
-	      && !r.varying_p ()
-	      && !r.undefined_p ())))
+  // Calculate the final range of NAME if possible.
+  if (rinit.singleton_p () && rstep.singleton_p ())
     {
       widest_int nit;
+      if (!max_loop_iterations (l, &nit))
+	return false;
 
-      /* We are only entering here for loop header PHI nodes, so using
-	 the number of latch executions is the correct thing to use.  */
-      if (max_loop_iterations (loop, &nit))
+      if (!induction_variable_may_overflow_p (type, rstep.lower_bound (), nit))
 	{
-	  signop sgn = TYPE_SIGN (TREE_TYPE (step));
-	  wi::overflow_type overflow;
-
-	  widest_int wtmp = wi::mul (wi::to_widest (step), nit, sgn,
-				     &overflow);
-	  /* If the multiplication overflowed we can't do a meaningful
-	     adjustment.  Likewise if the result doesn't fit in the type
-	     of the induction variable.  For a signed type we have to
-	     check whether the result has the expected signedness which
-	     is that of the step as number of iterations is unsigned.  */
-	  if (!overflow
-	      && wi::fits_to_tree_p (wtmp, TREE_TYPE (init))
-	      && (sgn == UNSIGNED
-		  || wi::gts_p (wtmp, 0) == wi::gts_p (wi::to_wide (step), 0)))
-	    {
-	      value_range maxvr, vr0, vr1;
-	      if (!query->range_of_expr (vr0, init, stmt))
-		vr0.set_varying (TREE_TYPE (init));
-	      tree tinit = TREE_TYPE (init);
-	      wide_int winit = wide_int::from (wtmp,
-					       TYPE_PRECISION (tinit),
-					       TYPE_SIGN (tinit));
-	      vr1.set (TREE_TYPE (init), winit, winit);
-
-	      range_op_handler handler (PLUS_EXPR, TREE_TYPE (init));
-	      if (!handler.fold_range (maxvr, TREE_TYPE (init), vr0, vr1))
-		maxvr.set_varying (TREE_TYPE (init));
-
-	      /* Likewise if the addition did.  */
-	      if (!maxvr.varying_p () && !maxvr.undefined_p ())
-		{
-		  int_range<2> initvr;
-
-		  if (!query->range_of_expr (initvr, init, stmt)
-		      || initvr.undefined_p ())
-		    return false;
-
-		  tree initvr_type = initvr.type ();
-		  tree initvr_min = wide_int_to_tree (initvr_type,
-						      initvr.lower_bound ());
-		  tree initvr_max = wide_int_to_tree (initvr_type,
-						      initvr.upper_bound ());
-		  tree maxvr_type = maxvr.type ();
-		  tree maxvr_min = wide_int_to_tree (maxvr_type,
-						     maxvr.lower_bound ());
-		  tree maxvr_max = wide_int_to_tree (maxvr_type,
-						     maxvr.upper_bound ());
-
-		  /* Check if init + nit * step overflows.  Though we checked
-		     scev {init, step}_loop doesn't wrap, it is not enough
-		     because the loop may exit immediately.  Overflow could
-		     happen in the plus expression in this case.  */
-		  if ((dir == EV_DIR_DECREASES
-		       && compare_values (maxvr_min, initvr_min) != -1)
-		      || (dir == EV_DIR_GROWS
-			  && compare_values (maxvr_max, initvr_max) != 1))
-		    return false;
-
-		  tmin = maxvr_min;
-		  tmax = maxvr_max;
-		}
-	    }
+	  // Calculate the max bounds for init (init + niter * step).
+	  wide_int w = wide_int::from (nit, TYPE_PRECISION (type), TYPE_SIGN (type));
+	  int_range<1> niter (type, w, w);
+	  int_range_max max_step;
+	  range_op_handler mult_handler (MULT_EXPR, type);
+	  range_op_handler plus_handler (PLUS_EXPR, type);
+	  if (!mult_handler.fold_range (max_step, type, niter, rstep)
+	      || !plus_handler.fold_range (max_init, type, rinit, max_step))
+	    return false;
 	}
     }
-
-  *min = tmin;
-  *max = tmax;
-  if (dir == EV_DIR_DECREASES)
-    *max = init;
-  else
-    *min = init;
-
-  fix_overflow (min, max);
+  range_from_loop_direction (r, type, rinit, max_init, dir);
   return true;
 }
 
diff --git a/gcc/vr-values.h b/gcc/vr-values.h
index dc0c22df4d8..df79a3a570b 100644
--- a/gcc/vr-values.h
+++ b/gcc/vr-values.h
@@ -74,7 +74,7 @@  private:
 
 extern bool range_fits_type_p (const irange *vr,
 			       unsigned dest_precision, signop dest_sgn);
-extern bool bounds_of_var_in_loop (tree *min, tree *max, range_query *,
-				   class loop *loop, gimple *stmt, tree var);
+extern bool range_of_var_in_loop (vrange &, tree var, class loop *, gimple *,
+				  range_query *);
 
 #endif /* GCC_VR_VALUES_H */