This patch take the PHI analyzer, which was operating like SCEV, and turns it into a prepass.

Instead of having on demand queries from fold_using_range::range_of_phi,  this turns the phi analyzer into a simple:

  * loop through all the phis
  * try to recognize groups and calculate their initial values,
  * write those initial values out to the specified query's global cache

This simplifies the phi analyzer to some degree, and removes the existing limitation of only using a globals value query.   Up until now, it couldnt invoke a full ranger search on edges becaue it was usually invoked in the middle of a query, andthis would cause cycles.

This meant we missed a lot of opportunities where we couldnt figure out the range bounds on a back edge because GORI was not being invoked.

With this change, and running the anaylzer  immediately following creation of a ranger in VRP, we can use the full power of the query, and use the new global update API, it can initialize all those PHIs it finds with values.

This also required tweaking one test case which was testing for a specific global being set to [1,1]..  when in fact, it is unreachable and is eliminated.. the new code hits this earlier, and the global is never updated from the intial [0,1] to [1,1]. Test conditions changed to see that the if statements are eliminated instead.


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

Andrew
From 337ebeccfdaaea5549324e749ad503ac6008d860 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Fri, 14 Nov 2025 16:06:42 -0500
Subject: [PATCH 3/4] Turn PHI analyzer to a simple pre-pass

Rather than having a dynamic analyzer around that is handcuffed by
only global values, invoke it as a prepass in VRP and put all values it finds
in the query's global cache via update_range_info.

	gcc/
	* gimple-range-fold.cc (fold_using_range::range_of_phi): Remove
	the PHI analysis query.
	* gimple-range-phi.cc (phi_analysis_object): Delete.
	(phi_analysis_initialize): Delete.
	(phi_analysis_finalize): Delete.
	(phi_analysis_available_p): Delete.
	(phi_analysis): Invoke a phi analyzer.
	(phi_analyzer::phi_analyzer): Preprocess all phi nodes and set
	global values for them in a query.
	(phi_analyzer::process_phi): Use query, and export any inital
	values found to the query.
	* gimple-range-phi.h (m_global): Delete.
	(phi_analysis_initialize): Delete.
	(phi_analysis_finalize): Delete.
	(phi_analysis_available_p): Delete.
	(phi_analysis): Change prototype.
	* tree-vrp.cc (execute_ranger_vrp): Call phi_analysis.

	gcc/testsuite/
	* gcc.dg/pr102983.c: Adjust final check.
---
 gcc/gimple-range-fold.cc        |  28 +--------
 gcc/gimple-range-phi.cc         | 101 ++++++++++++++------------------
 gcc/gimple-range-phi.h          |  17 ++----
 gcc/testsuite/gcc.dg/pr102983.c |   2 +-
 gcc/tree-vrp.cc                 |   3 +-
 5 files changed, 55 insertions(+), 96 deletions(-)

diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index d4481770d76..63e114e4d04 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -1008,31 +1008,9 @@ fold_using_range::range_of_phi (vrange &r, gphi *phi, fur_source &src)
 	}
     }
 
-  // If PHI analysis is available, see if there is an iniital range.
-  if (phi_analysis_available_p ()
-      && irange::supports_p (TREE_TYPE (phi_def)))
-    {
-      phi_group *g = (phi_analysis())[phi_def];
-      if (g && !(g->range ().varying_p ()))
-	{
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    {
-	      fprintf (dump_file, "PHI GROUP query for ");
-	      print_generic_expr (dump_file, phi_def, TDF_SLIM);
-	      fprintf (dump_file, " found : ");
-	      g->range ().dump (dump_file);
-	      fprintf (dump_file, " and adjusted original range from :");
-	      r.dump (dump_file);
-	    }
-	  r.intersect (g->range ());
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    {
-	      fprintf (dump_file, " to :");
-	      r.dump (dump_file);
-	      fprintf (dump_file, "\n");
-	    }
-	}
-    }
+  // Incorporate any global value.  If a PHI analysis phase was run, there may
+  // be a restricted global range already.  Query the range with no context
+  // to get a global range.
 
   // If SCEV is available, query if this PHI has any known values.
   if (scev_initialized_p ()
diff --git a/gcc/gimple-range-phi.cc b/gcc/gimple-range-phi.cc
index d9b583fe4fb..45895209600 100644
--- a/gcc/gimple-range-phi.cc
+++ b/gcc/gimple-range-phi.cc
@@ -37,41 +37,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-walk.h"
 #include "cfganal.h"
 
-// There can be only one running at a time.
-static phi_analyzer *phi_analysis_object = NULL;
-
-// Initialize a PHI analyzer with range query Q.
+// Invoke a phi analyzer.  It will process all the current PHI groups
+// and export any ranges found to set_range_info.
+// When finished, it will simply dispose of itself.
 
 void
-phi_analysis_initialize (range_query &q)
-{
-  gcc_checking_assert (!phi_analysis_object);
-  phi_analysis_object = new phi_analyzer (q);
-}
-
-// Terminate the current PHI analyzer.  if F is non-null, dump the tables
-
-void
-phi_analysis_finalize ()
-{
-  gcc_checking_assert (phi_analysis_object);
-  delete phi_analysis_object;
-  phi_analysis_object = NULL;
-}
-
-// Return TRUE is there is a PHI analyzer operating.
-bool
-phi_analysis_available_p ()
-{
-  return phi_analysis_object != NULL;
-}
-
-// Return the phi analyzer object.
-
-phi_analyzer &phi_analysis ()
+phi_analysis (range_query &q)
 {
-  gcc_checking_assert (phi_analysis_object);
-  return *phi_analysis_object;
+  phi_analyzer analyze (q);
 }
 
 // Initialize a phi_group from another group G.
@@ -254,16 +227,35 @@ phi_group::dump (FILE *f)
 
 // Construct a phi analyzer which uses range_query G to pick up values.
 
-phi_analyzer::phi_analyzer (range_query &g) : m_global (g), m_phi_groups (vNULL)
+phi_analyzer::phi_analyzer (range_query &query) : m_phi_groups (vNULL)
 {
   m_work.create (0);
   m_work.safe_grow (20);
 
   m_tab.create (0);
-//   m_tab.safe_grow_cleared (num_ssa_names + 100);
+  m_tab.safe_grow_cleared (num_ssa_names + 10);
+
   bitmap_obstack_initialize (&m_bitmaps);
   m_simple = BITMAP_ALLOC (&m_bitmaps);
   m_current = BITMAP_ALLOC (&m_bitmaps);
+
+  basic_block bb;
+  gphi_iterator gphi;
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "PHI ANALYZER : processing PHIS.\n");
+
+  // Process each PHI node to see if it belongs in a group with others.
+  // Then calculate an initial value for the group.
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      for (gphi = gsi_start_nonvirtual_phis (bb);
+	   !gsi_end_p (gphi);
+	   gsi_next_nonvirtual_phi (&gphi))
+	process_phi (gphi.phi (), query);
+    }
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "PHI ANALYZER : Finished processing PHIS.\n");
 }
 
 // Destruct a PHI analyzer.
@@ -308,35 +300,33 @@ phi_analyzer::operator[] (tree name)
     return NULL;
 
   unsigned v = SSA_NAME_VERSION (name);
-  // Already been processed and not part of a group.
-  if (bitmap_bit_p (m_simple, v))
+  if (v >= m_tab.length ())
     return NULL;
-
-  if (v >= m_tab.length () || !m_tab[v])
-    {
-      process_phi (as_a<gphi *> (SSA_NAME_DEF_STMT (name)));
-      if (bitmap_bit_p (m_simple, v))
-	return  NULL;
-     // If m_simple bit isn't set, and process_phi didn't allocated the table
-     // no group was created, so return NULL.
-     if (v >= m_tab.length ())
-      return NULL;
-    }
   return m_tab[v];
 }
 
-// Process phi node PHI to see if it is part of a group.
+// Process phi node PHI to see if it is part of a group.  Use QUERY
+// to deteremine ranges.
 
 void
-phi_analyzer::process_phi (gphi *phi)
+phi_analyzer::process_phi (gphi *phi, range_query &query)
 {
-  gcc_checking_assert (!group (gimple_phi_result (phi)));
+  tree def = gimple_phi_result (phi);
+  unsigned v = SSA_NAME_VERSION (def);
+
+  gcc_checking_assert (v < m_tab.length ());
+  // If this is already on a group, or identified as a simple phi, or
+  // not an irange, do not process it.
+  if (m_tab[v] || bitmap_bit_p (m_simple, v)
+      || !irange::supports_p (TREE_TYPE (def)))
+    return;
+
   bool cycle_p = true;
 
   // Start with the LHS of the PHI in the worklist.
   unsigned x;
   m_work.truncate (0);
-  m_work.safe_push (gimple_phi_result (phi));
+  m_work.safe_push (def);
   unsigned phi_count = 1;
   bitmap_clear (m_current);
 
@@ -447,7 +437,7 @@ phi_analyzer::process_phi (gphi *phi)
       // If there is an symbolic initializer as well, include it here.
       if (valid && init_idx != -1)
 	{
-	  if (m_global.range_on_edge (init_sym, m_ext_edge[init_idx],
+	  if (query.range_on_edge (init_sym, m_ext_edge[init_idx],
 				      m_external[init_idx]))
 	    init_range.union_ (init_sym);
 	  else
@@ -457,7 +447,7 @@ phi_analyzer::process_phi (gphi *phi)
 	{
 	  // Try to create a group based on m_current. If a result comes back
 	  // with a range that isn't varying, create the group.
-	  phi_group cyc (m_current, init_range, mod, &m_global);
+	  phi_group cyc (m_current, init_range, mod, &query);
 	  if (!cyc.range ().varying_p ())
 	    {
 	      g = new phi_group (cyc);
@@ -490,9 +480,6 @@ phi_analyzer::process_phi (gphi *phi)
       return;
     }
 
-  if (num_ssa_names >= m_tab.length ())
-    m_tab.safe_grow_cleared (num_ssa_names + 100);
-
   // Now set all entries in the group to this record.
   unsigned i;
   bitmap_iterator bi;
@@ -501,6 +488,8 @@ phi_analyzer::process_phi (gphi *phi)
       // Can't be in more than one group.
       gcc_checking_assert (m_tab[i] == NULL);
       m_tab[i] = g;
+      // Set the new range in the range query.
+      query.update_range_info (ssa_name (i), g->range ());
     }
   // Allocate a new bitmap for the next time as the original one is now part
   // of the new phi group.
diff --git a/gcc/gimple-range-phi.h b/gcc/gimple-range-phi.h
index ea5750520c2..34001f738da 100644
--- a/gcc/gimple-range-phi.h
+++ b/gcc/gimple-range-phi.h
@@ -81,8 +81,7 @@ public:
   void dump (FILE *f);
 protected:
   phi_group *group (tree name) const;
-  void process_phi (gphi *phi);
-  range_query &m_global;
+  void process_phi (gphi *phi, range_query &query);
   vec<tree> m_work;
 
   bitmap m_simple;       // Processed, not part of a group.
@@ -92,16 +91,10 @@ protected:
   bitmap_obstack m_bitmaps;
 };
 
-// These are the APIs to start and stop a phi analyzerin a SCEV like manner.
-// There can only be one operating at any given time.
-// When initialized, a range-query if provided to do lookups of values for
-// PHIs and to evaluate modifier and initial value statements.
-// To avoid problems, this should be some form of constant query, like
-// global_range_query or better yet a const_query from a functioning ranger.
+// Invoke a phi analyzer.  It will process all the current PHI nodes and try
+// to form groups with initial values. Then export any ranges found
+// to set_range_info.  When finished, it will simply dispose of itself.
 
-bool phi_analysis_available_p ();
-phi_analyzer &phi_analysis ();
-void phi_analysis_initialize (range_query &);
-void phi_analysis_finalize ();
+void phi_analysis (range_query &q);
 
 #endif // GCC_SSA_RANGE_PHI_H
diff --git a/gcc/testsuite/gcc.dg/pr102983.c b/gcc/testsuite/gcc.dg/pr102983.c
index ded748af08a..1b0e5c7587d 100644
--- a/gcc/testsuite/gcc.dg/pr102983.c
+++ b/gcc/testsuite/gcc.dg/pr102983.c
@@ -18,4 +18,4 @@ int main() {
   }
 }
 
-/* { dg-final { scan-tree-dump-times "Global Exported: c_.*1, 1" 1 "evrp" } } */
+/* { dg-final { scan-tree-dump-not "if \\(c_"  "evrp" } } */
diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
index cad1a24ab33..b173ed26386 100644
--- a/gcc/tree-vrp.cc
+++ b/gcc/tree-vrp.cc
@@ -1094,8 +1094,8 @@ execute_ranger_vrp (struct function *fun, bool final_p)
 
   set_all_edges_as_executable (fun);
   gimple_ranger *ranger = enable_ranger (fun, false);
+  phi_analysis (*ranger);
   rvrp_folder folder (ranger, final_p);
-  phi_analysis_initialize (ranger->const_query ());
   folder.substitute_and_fold ();
   // Ensure the cache in SCEV has been cleared before processing
   // globals to be removed.
@@ -1149,7 +1149,6 @@ execute_ranger_vrp (struct function *fun, bool final_p)
 	}
     }
 
-  phi_analysis_finalize ();
   disable_ranger (fun);
   scev_finalize ();
   loop_optimizer_finalize ();
-- 
2.45.0

Reply via email to