[RFC,1/2] scev: Make scev_dfs a public API and able to handle arbirary stmts.

Message ID 20261001152634.162479-1-alfie.richards@arm.com
State New
Headers
Series [RFC,1/2] scev: Make scev_dfs a public API and able to handle arbirary stmts. |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm warning Skipped because it is an RFC

Commit Message

Alfie Richards Oct. 1, 2026, 3:26 p.m. UTC
  This is required for the change to ivopts.

This sort of acts as a signifficantly less powerful scev, but one that is
able to handle loop variant values, for the API consumer to deal with.

gcc/ChangeLog:

	* tree-scalar-evolution.cc (enum t_bool): Move to
	tree-scalar-evolution.h.
	(class scev_dfs): Likewise.
	(scev_dfs::get_ev): New overload.
	(scev_dfs::add_to_evolution_1): Update to handle analyzing
	statements, and updating the base.
	(scev_dfs::add_to_evolution): Move type conversion into this
	function.
	(scev_dfs::follow_ssa_edge_binary): Update calls to
	add_to_evolution to move type conversion into it.
	(scev_dfs::follow_ssa_edge_expr): Likewise.
	(follow_copies_to_constant): Likewise.
	* tree-scalar-evolution.h (analyze_initial_condition):
	Make public.
	(enum t_bool): Move to here from tree-scalar-evolution.cc.
	(class scev_dfs): Likewise.
---
 gcc/tree-scalar-evolution.cc | 202 +++++++++++++++++++++++------------
 gcc/tree-scalar-evolution.h  |  43 ++++++++
 2 files changed, 177 insertions(+), 68 deletions(-)
  

Patch

diff --git a/gcc/tree-scalar-evolution.cc b/gcc/tree-scalar-evolution.cc
index 4386e288de1..c818f765c1f 100644
--- a/gcc/tree-scalar-evolution.cc
+++ b/gcc/tree-scalar-evolution.cc
@@ -581,40 +581,6 @@  get_scalar_evolution (basic_block instantiated_below, tree scalar)
 
 /* Depth first search algorithm.  */
 
-enum t_bool {
-  t_false,
-  t_true,
-  t_dont_know
-};
-
-class scev_dfs
-{
-public:
-  scev_dfs (class loop *loop_, gphi *phi_, tree init_cond_)
-      : loop (loop_), loop_phi_node (phi_), init_cond (init_cond_) {}
-  t_bool get_ev (tree *, tree);
-
-private:
-  t_bool follow_ssa_edge_expr (gimple *, tree, tree *, int);
-  t_bool follow_ssa_edge_binary (gimple *at_stmt,
-				 tree type, tree rhs0, enum tree_code code,
-				 tree rhs1, tree *evolution_of_loop, int limit);
-  t_bool follow_ssa_edge_in_condition_phi_branch (int i,
-						  gphi *condition_phi,
-						  tree *evolution_of_branch,
-						  tree init_cond, int limit);
-  t_bool follow_ssa_edge_in_condition_phi (gphi *condition_phi,
-					   tree *evolution_of_loop, int limit);
-  t_bool follow_ssa_edge_inner_loop_phi (gphi *loop_phi_node,
-					 tree *evolution_of_loop, int limit);
-  tree add_to_evolution (tree chrec_before, tree to_add, gimple *at_stmt);
-  tree add_to_evolution_1 (tree chrec_before, tree to_add, gimple *at_stmt);
-
-  class loop *loop;
-  gphi *loop_phi_node;
-  tree init_cond;
-};
-
 t_bool
 scev_dfs::get_ev (tree *ev_fn, tree arg)
 {
@@ -622,6 +588,18 @@  scev_dfs::get_ev (tree *ev_fn, tree arg)
   return follow_ssa_edge_expr (loop_phi_node, arg, ev_fn, 0);
 }
 
+/* Alternative form that takes any assign stmt.  The stmt is explored until any
+   phi node is found, which is explored normally for a evolution.  The evolution
+   between the stmt and phi is added to the left of the evolution.  */
+t_bool
+scev_dfs::get_ev (tree *ev_fn, tree arg, gimple *stmt)
+{
+  gcc_assert (loop_phi_node == NULL);
+  gcc_assert (init_cond == NULL);
+  *ev_fn = chrec_dont_know;
+  return follow_ssa_edge_expr (stmt, arg, ev_fn, 0);
+}
+
 /* Helper function for add_to_evolution.  Returns the evolution
    function for an assignment of the form "a = b + c", where "a" and
    "b" are on the strongly connected component.  CHREC_BEFORE is the
@@ -639,12 +617,68 @@  scev_dfs::add_to_evolution_1 (tree chrec_before, tree to_add, gimple *at_stmt)
   unsigned loop_nb = loop->num;
   class loop *chloop;
 
+  /* Otherwise, add it to the base.  */
+  if (!loop_phi_node)
+    {
+      switch (TREE_CODE (chrec_before))
+	{
+	case POLYNOMIAL_CHREC:
+	  {
+	    chloop = get_chrec_loop (chrec_before);
+	    gcc_assert (chloop == loop);
+	    unsigned var;
+
+	    type = chrec_type (chrec_before);
+
+	    var = CHREC_VARIABLE (chrec_before);
+	    left = CHREC_LEFT (chrec_before);
+	    right = CHREC_RIGHT (chrec_before);
+
+	    if (POINTER_TYPE_P (TREE_TYPE (to_add)))
+	      {
+		/* Pointers should not be added to pointers.  */
+		gcc_assert (!POINTER_TYPE_P (type));
+		type = TREE_TYPE (to_add);
+
+		tree step = convert_to_ptrofftype (left);
+
+		left = to_add;
+		to_add = step;
+
+		right = chrec_convert (sizetype, right, at_stmt);
+	      }
+
+	    left = chrec_convert (type, left, at_stmt);
+	    to_add = chrec_convert_rhs (type, to_add, at_stmt);
+	    left = chrec_fold_plus (chrec_type (left), left, to_add);
+
+	    /* When we have an evolution in a non-wrapping type and
+	       in the process of accumulating CHREC_RIGHT there was
+	       overflow this indicates in the association that happened
+	       in building the CHREC clearly involved UB.  Avoid this.
+	       In building a CHREC we basically turn (a + INCR1) + INCR2
+	       into a + (INCR1 + INCR2) which is not always valid.
+	       Note this check only catches few invalid cases.  */
+	    if ((INTEGRAL_TYPE_P (type) && !TYPE_OVERFLOW_WRAPS (type))
+		&& TREE_CODE (left) == INTEGER_CST && TREE_OVERFLOW (left))
+	      return chrec_dont_know;
+
+	    /* NOTE: This allow loop variant vars on the LEFT of the chrec.
+	       This will need to be dealt with by the consumer.  */
+	    return build_polynomial_chrec (var, left, right, true);
+	  }
+
+	default:
+	  return chrec_dont_know;
+	}
+    }
+
+  /* If we are in a loop phi node, then add this to the evolution.  */
   switch (TREE_CODE (chrec_before))
     {
     case POLYNOMIAL_CHREC:
       chloop = get_chrec_loop (chrec_before);
-      if (chloop == loop
-	  || flow_loop_nested_p (chloop, loop))
+      if (chloop == loop || flow_loop_nested_p (chloop, loop))
 	{
 	  unsigned var;
 
@@ -655,9 +689,8 @@  scev_dfs::add_to_evolution_1 (tree chrec_before, tree to_add, gimple *at_stmt)
 	    {
 	      var = loop_nb;
 	      left = chrec_before;
-	      right = SCALAR_FLOAT_TYPE_P (type)
-		? build_real (type, dconst0)
-		: build_int_cst (type, 0);
+	      right = SCALAR_FLOAT_TYPE_P (type) ? build_real (type, dconst0)
+						 : build_int_cst (type, 0);
 	    }
 	  else
 	    {
@@ -676,9 +709,8 @@  scev_dfs::add_to_evolution_1 (tree chrec_before, tree to_add, gimple *at_stmt)
 	     In building a CHREC we basically turn (a + INCR1) + INCR2
 	     into a + (INCR1 + INCR2) which is not always valid.
 	     Note this check only catches few invalid cases.  */
-	  if ((INTEGRAL_TYPE_P (type) && ! TYPE_OVERFLOW_WRAPS (type))
-	      && TREE_CODE (right) == INTEGER_CST
-	      && TREE_OVERFLOW (right))
+	  if ((INTEGRAL_TYPE_P (type) && !TYPE_OVERFLOW_WRAPS (type))
+	      && TREE_CODE (right) == INTEGER_CST && TREE_OVERFLOW (right))
 	    return chrec_dont_know;
 	  return build_polynomial_chrec (var, left, right);
 	}
@@ -687,12 +719,12 @@  scev_dfs::add_to_evolution_1 (tree chrec_before, tree to_add, gimple *at_stmt)
 	  gcc_assert (flow_loop_nested_p (loop, chloop));
 
 	  /* Search the evolution in LOOP_NB.  */
-	  left = add_to_evolution_1 (CHREC_LEFT (chrec_before),
-				     to_add, at_stmt);
+	  left
+	    = add_to_evolution_1 (CHREC_LEFT (chrec_before), to_add, at_stmt);
 	  right = CHREC_RIGHT (chrec_before);
 	  right = chrec_convert_rhs (chrec_type (left), right, at_stmt);
-	  return build_polynomial_chrec (CHREC_VARIABLE (chrec_before),
-					 left, right);
+	  return build_polynomial_chrec (CHREC_VARIABLE (chrec_before), left,
+					 right, true);
 	}
 
     default:
@@ -849,16 +881,24 @@  scev_dfs::add_to_evolution_1 (tree chrec_before, tree to_add, gimple *at_stmt)
 
    | a  ->  {inita, +, 2 + initb, +, 1}_1
 
+   If TYPE is not NULL, the to_add is converted to the required type in order
+   to be added to a chrec of type TYPE.
 */
 
 tree
-scev_dfs::add_to_evolution (tree chrec_before, tree to_add, gimple *at_stmt)
+scev_dfs::add_to_evolution (tree chrec_before, tree type, tree to_add,
+			    gimple *at_stmt)
 {
   tree res = NULL_TREE;
 
   if (to_add == NULL_TREE)
     return chrec_before;
 
+  if (!loop_phi_node && POINTER_TYPE_P (TREE_TYPE (to_add)))
+    chrec_before = chrec_convert_rhs (type, chrec_before, at_stmt);
+  else
+    chrec_before = chrec_convert (type, chrec_before, at_stmt);
+
   /* TO_ADD is either a scalar, or a parameter.  TO_ADD is not
      instantiated at this point.  */
   if (TREE_CODE (to_add) == POLYNOMIAL_CHREC)
@@ -888,7 +928,6 @@  scev_dfs::add_to_evolution (tree chrec_before, tree to_add, gimple *at_stmt)
   return res;
 }
 
-
 /* Follow the ssa edge into the binary expression RHS0 CODE RHS1.
    Return true if the strongly connected component has been found.  */
 
@@ -919,16 +958,15 @@  scev_dfs::follow_ssa_edge_binary (gimple *at_stmt, tree type, tree rhs0,
 	      evol = *evolution_of_loop;
 	      res = follow_ssa_edge_expr (at_stmt, rhs0, &evol, limit);
 	      if (res == t_true)
-		*evolution_of_loop = add_to_evolution
-		    (chrec_convert (type, evol, at_stmt), rhs1, at_stmt);
+		*evolution_of_loop = add_to_evolution (evol, type, rhs1,
+						       at_stmt);
 	      else if (res == t_false)
 		{
 		  res = follow_ssa_edge_expr
 		    (at_stmt, rhs1, evolution_of_loop, limit);
 		  if (res == t_true)
-		    *evolution_of_loop = add_to_evolution
-			(chrec_convert (type, *evolution_of_loop, at_stmt),
-			 rhs0, at_stmt);
+		    *evolution_of_loop = add_to_evolution (*evolution_of_loop,
+							   type, rhs0, at_stmt);
 		}
 	    }
 
@@ -942,9 +980,8 @@  scev_dfs::follow_ssa_edge_binary (gimple *at_stmt, tree type, tree rhs0,
 	     "a = ... + c".  */
 	  res = follow_ssa_edge_expr (at_stmt, rhs1, evolution_of_loop, limit);
 	  if (res == t_true)
-	    *evolution_of_loop = add_to_evolution
-		(chrec_convert (type, *evolution_of_loop, at_stmt),
-		 rhs0, at_stmt);
+	    *evolution_of_loop
+	      = add_to_evolution (*evolution_of_loop, type, rhs0, at_stmt);
 	}
 
       else
@@ -1153,12 +1190,46 @@  scev_dfs::follow_ssa_edge_expr (gimple *at_stmt, tree expr,
 	      return t_true;
 	    }
 
+	  class loop *def_loop = loop_containing_stmt (def);
 	  /* Otherwise, the evolution of the HALTING_PHI depends
 	     on the evolution of another loop-phi-node, i.e. the
 	     evolution function is a higher degree polynomial.  */
-	  class loop *def_loop = loop_containing_stmt (def);
 	  if (def_loop == loop)
-	    return t_false;
+	    {
+	      /* If we are in a situation where halting phi is defined and this
+		 is not it, then we should abort.  */
+	      if (halting_phi)
+		return t_false;
+
+	      /* Otherwise, we should see if halting phi has a valid evolution.
+		 and then base our evolution off that.  */
+	      loop_phi_node = phi;
+	      init_cond = analyze_initial_condition (phi);
+
+	      int i, n = gimple_phi_num_args (phi);
+	      for (i = 0; i < n; i++)
+		{
+		  tree arg = PHI_ARG_DEF (phi, i);
+
+		  /* Select the edges that enter the loop body.  */
+		  basic_block bb = gimple_phi_arg_edge (phi, i)->src;
+		  if (!flow_bb_inside_loop_p (loop, bb))
+		    continue;
+
+		  /* This phi defines an IV in our loop that we append to.  */
+		  t_bool res = follow_ssa_edge_expr (phi, arg,
+						     evolution_of_loop, limit);
+		  if (res == t_true)
+		    {
+		      loop_phi_node = NULL;
+		      init_cond = NULL;
+		      return t_true;
+		    }
+		}
+	      init_cond = NULL;
+	      loop_phi_node = NULL;
+	      return t_false;
+	    }
 
 	  /* Inner loop.  */
 	  if (flow_loop_nested_p (loop, def_loop))
@@ -1258,9 +1329,8 @@  scev_dfs::follow_ssa_edge_expr (gimple *at_stmt, tree expr,
 	  t_bool res = follow_ssa_edge_expr (at_stmt, rhs0,
 					     evolution_of_loop, limit);
 	  if (res == t_true)
-	    *evolution_of_loop = add_to_evolution
-		(chrec_convert (type, *evolution_of_loop, at_stmt),
-		 rhs1, at_stmt);
+	    *evolution_of_loop
+	      = add_to_evolution (*evolution_of_loop, type, rhs1, at_stmt);
 	  return res;
 	}
       /* Else search for the SCC in both rhs0 and rhs1.  */
@@ -1289,10 +1359,8 @@  scev_dfs::follow_ssa_edge_expr (gimple *at_stmt, tree expr,
 	      tree to_add = chrec_convert_rhs (utype, rhs1);
 	      to_add = chrec_fold_multiply (utype, to_add,
 					    build_int_cst_type (utype, -1));
-	      *evolution_of_loop
-		= chrec_convert (utype, *evolution_of_loop, at_stmt);
 	      *evolution_of_loop = add_to_evolution (*evolution_of_loop,
-						     to_add, at_stmt);
+						     utype, to_add, at_stmt);
 	      *evolution_of_loop
 		= chrec_convert (type, *evolution_of_loop, at_stmt);
 	    }
@@ -1301,9 +1369,7 @@  scev_dfs::follow_ssa_edge_expr (gimple *at_stmt, tree expr,
 	      tree to_add = chrec_fold_multiply (type, rhs1,
 						 build_minus_one_cst (type));
 	      *evolution_of_loop
-		= add_to_evolution (chrec_convert (type, *evolution_of_loop,
-						   at_stmt),
-				    to_add, at_stmt);
+		= add_to_evolution (*evolution_of_loop, type, to_add, at_stmt);
 	    }
 	  return res;
 	}
@@ -1598,7 +1664,7 @@  follow_copies_to_constant (tree var)
    This analyzer does not analyze the evolution outside the current
    loop, and leaves this task to the on-demand tree reconstructor.  */
 
-static tree
+tree
 analyze_initial_condition (gphi *loop_phi_node)
 {
   int i, n;
diff --git a/gcc/tree-scalar-evolution.h b/gcc/tree-scalar-evolution.h
index 0c15956de3f..4b70a5055dc 100644
--- a/gcc/tree-scalar-evolution.h
+++ b/gcc/tree-scalar-evolution.h
@@ -74,4 +74,47 @@  get_chrec_loop (const_tree chrec)
   return get_loop (cfun, CHREC_VARIABLE (chrec));
 }
 
+/* Depth first search algorithm.  */
+
+tree analyze_initial_condition (gphi *);
+
+enum t_bool {
+  t_false,
+  t_true,
+  t_dont_know
+};
+
+/* If phi is NULL, then this is just a statement, and we will need to find
+   a relevant phi in the loop, and it's evolution from that.  */
+class scev_dfs
+{
+public:
+  scev_dfs (class loop *loop_, gphi *phi_, tree init_cond_)
+      : loop (loop_), loop_phi_node (phi_), init_cond (init_cond_) {}
+  t_bool get_ev (tree *, tree);
+  t_bool get_ev (tree *ev_fn, tree arg, gimple *stmt);
+
+private:
+  t_bool follow_ssa_edge_expr (gimple *, tree, tree *, int);
+  t_bool follow_ssa_edge_binary (gimple *at_stmt,
+				 tree type, tree rhs0, enum tree_code code,
+				 tree rhs1, tree *evolution_of_loop, int limit);
+  t_bool follow_ssa_edge_in_condition_phi_branch (int i,
+						  gphi *condition_phi,
+						  tree *evolution_of_branch,
+						  tree init_cond, int limit);
+  t_bool follow_ssa_edge_in_condition_phi (gphi *condition_phi,
+					   tree *evolution_of_loop, int limit);
+  t_bool follow_ssa_edge_inner_loop_phi (gphi *loop_phi_node,
+					 tree *evolution_of_loop, int limit);
+  tree add_to_evolution (tree, tree, tree, gimple *);
+  tree add_to_evolution_1 (tree, tree, gimple *);
+
+  tree add_to_base (tree chrec_before, tree to_add, gimple *at_stmt);
+
+  class loop *loop;
+  gphi *loop_phi_node;
+  tree init_cond;
+};
+
 #endif  /* GCC_TREE_SCALAR_EVOLUTION_H  */