This is the delayed final patch for this PR that I mentioned would be coming. This reduces the performance loss of that patch set to something more acceptable.

The general format ifor the single PHI "groups" is

    ssa_1 = ssa_2 OP const.
    ssa_2 = PHI <initial, ssa_1>


with some sort of backedge leading to the definition of ssa_1, and a range restriction imposed on ssa_2 on that edge.

The iterative analysis utilizes that ssa_2 edge range restriction to ensure we don't generate ranges for ssa_1 that cannot feed into ssa_2.

I generated a lot of information about when we found and initial value and when we didn't as well as the input values.  Over a bootstrap, it generated 144,000 individual cases.  I fed this into an AI and asked it questions and had a discussion.

The first thing I did was check to see if we have a relation BEFORE doing the iteration..  that gives us an instant answer cheaper. Previously we did all the iteration work, and only checked relations after iterations failed.

The end result is that 98% of all modifying statements feeding a PHI are either PLUS or MINUS.  not surprising.       Also not surprising, it often didn't help.  Thios is , probably because the back edge often part of a loop and loop analsysis has already made some conclusions.

What did pop out is that if the  edge range feeding back into the definition of ssa_1 has mutliple subranges, there is a dramatically improved chance something good can happen.   This situation means its not a straighforward loop backedge, and comprised only about 3% of the plus and minus cases.    These 3% covered over 50% of all useful cases, so its a pretty cheap way to catch the majority.

And with all the other operations (shift, bitwise, etc) comprising only 2% fo cases over all, it is worthwhile letting them them all through.

With the improved speedup, I also adjusted the iterative count from the arbitrary 10 that is was before, to be the number of bits in the word +1 .   This can help shifyts and multiplies especially,  but seemed reasonable overall.

This take the 4.5% VRP slowdown pof the previous patch , and reduces it down to 1.4%.   If I left the iteration count at 10, that number drops to 0.8%,  but I feel that is reasonable.   Easy to change if anyone disagrees.

Bootstraps on x86_64-pc-linux-gnu with no regressions.  Pushed.

Andrew


From 862a5b00cfb8c3ef43358bc8f3d4a603496aa3e9 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Thu, 20 Nov 2025 12:10:25 -0500
Subject: [PATCH] Improve PHI analyzer performance

Only do iterative analysis if it might be worthwhile.

	PR tree-optimization/121345
	gcc/
	* gimple-range-phi.cc (phi_group::calculate_using_modifier): Restore
	performance loss by being more selective when iterating.
---
 gcc/gimple-range-phi.cc | 48 +++++++++++++++++++++++++++++++----------
 1 file changed, 37 insertions(+), 11 deletions(-)

diff --git a/gcc/gimple-range-phi.cc b/gcc/gimple-range-phi.cc
index 85d0f279592..656d749d48d 100644
--- a/gcc/gimple-range-phi.cc
+++ b/gcc/gimple-range-phi.cc
@@ -127,29 +127,59 @@ phi_group::calculate_using_modifier (range_query *q)
   else
     return false;
 
-  // Examine modifier and run 10 iterations to see if it convergences.
+  // If we can resolve the range using relations, use that range.
+  if (refine_using_relation (k))
+    return true;
+
+  // Examine modifier and run iteration evaluations  to see if it convergences.
   // The constructor initilaized m_vr to the initial value already.
-  const unsigned num_iter = 10;
   int_range_max nv;
   int_range_max iter_value = m_vr;
   int_range_max iter_reach;
-  // Determie the maximum range of ITER reaching here.  Often a loop
-  // bound restricts the range reaching here.  Default to VARYING.
+
+  // Determine the maximum range of the var reaching here.  The back edge
+  // usually has a guard restircting the range. Default to VARYING.
   if (!m_modifier_name
       || !q->range_of_expr (iter_reach, m_modifier_name, m_modifier))
     iter_reach.set_varying (m_vr.type ());
+
+  // Iterative evaluation can be expensive, so heuristically :
+  //  - PLUS and MINUS are 98% of the cases, and usually handled well enough
+  //    by loop analysis, The exception is if the reaching range has multiple
+  //    subranges, those are often worth examining.
+  //  - All other operations (2%) are worth checking.
+  bool do_iterative = false;
+  if (gimple_code (m_modifier) == GIMPLE_ASSIGN)
+    switch (gimple_assign_rhs_code (m_modifier))
+      {
+	case PLUS_EXPR:
+	case MINUS_EXPR:
+	  if (iter_reach.num_pairs () > 1)
+	    do_iterative = true;
+	  break;
+	default:
+	  do_iterative = true;
+      }
+
+  // Limit iterations to 1 more than the number of bits.
+  unsigned num_iter;
+  if (do_iterative)
+    num_iter = TYPE_PRECISION (m_vr.type ()) + 1;
+  else
+    num_iter = 0;
+
   for (unsigned x = 0; x < num_iter; x++)
     {
       if (!fold_range (nv, m_modifier, iter_value, q))
 	break;
-      // Ensure nothing calculate is outside outside the reaching range.
+      // Ensure nothing calculated is outside outside the reaching range.
       nv.intersect (iter_reach);
       // If union does nothing, then we have convergence.
       if (!iter_value.union_ (nv))
 	{
 	  if (iter_value.varying_p ())
 	    break;
-	  // The last iteration Will reach the PHI node.
+	  // The last iteration Will also reach the PHI node, add it in.
 	  fold_range (nv, m_modifier, iter_value, q);
 	  iter_value.union_ (nv);
 	  m_vr = iter_value;
@@ -157,17 +187,13 @@ phi_group::calculate_using_modifier (range_query *q)
 	}
     }
 
-  // If we can resolve the range using relations, use that range.
-  if (refine_using_relation (k))
-    return true;
-
   // Never converged, so bail for now. we could examine the pattern
   // from m_initial to m_vr as an extension  Especially if we had a way
   // to project the actual number of iterations (SCEV?)
   //
   //  We can also try to identify "parallel" phis to get loop counts and
   //  determine the number of iterations of these parallel PHIs.
-  //
+
   return false;
 }
 
-- 
2.45.0

Reply via email to