From c3f9e9bc841b969e18a113bf2a60d9bdba7c4ebc Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacleod@redhat.com>
Date: Mon, 31 Aug 2026 14:25:21 -0400
Subject: [PATCH 2/2] Disallow path ranger from current_range_query.

Provide a generic way to push/pop new range_queries, and disable
path_ranger from being pushed..

	PR tree-optimization/126942.c
	gcc/
	* function.h (X_RANGE_QUERY_LIMIT): New.
	(x_range_query_stack): New.
	(x_range_query_index): New.
	* gimple-range-fold.cc (fold_using_range::fold_stmt): Use new push
	and pop interface.
	* gimple-range-path.h (path_ranger::active_query_compatible_p): New.
	* gimple-range.cc (enable_ranger): Use new interface.
	(disable_ranger): Likewise.
	* tree-vrp.cc (execute_fast_vrp): Likewise.
	* value-query.cc (push_range_query): New.
	(pop_range_query): New.
	* value-query.h (range_query::active_query_compatible_p): New.
	(push_range_query): Declare.
	(pop_range_query): Declare.

	gcc/testsuite/
	* gcc.dg/pr126942.c: New.
---
 gcc/function.h                  |  7 ++++++
 gcc/gimple-range-fold.cc        | 17 ++++++++------
 gcc/gimple-range-path.h         |  2 ++
 gcc/gimple-range.cc             | 12 +++++-----
 gcc/testsuite/gcc.dg/pr126942.c | 35 +++++++++++++++++++++++++++++
 gcc/tree-vrp.cc                 | 13 ++++++-----
 gcc/value-query.cc              | 40 +++++++++++++++++++++++++++++++++
 gcc/value-query.h               |  7 ++++++
 8 files changed, 115 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr126942.c

diff --git a/gcc/function.h b/gcc/function.h
index 765c71309fa..1701eae67a9 100644
--- a/gcc/function.h
+++ b/gcc/function.h
@@ -318,6 +318,13 @@ struct GTY(()) function {
      should be queried by calling get_range_query().  */
   range_query * GTY ((skip)) x_range_query;
 
+  /* push_range_query and pop_range_query can temporarily change the
+     current query.  This stack allows for simple restoration.
+     This stack should never be very large.*/
+#define X_RANGE_QUERY_LIMIT	5
+  range_query * GTY ((skip)) x_range_query_stack[X_RANGE_QUERY_LIMIT];
+  unsigned x_range_query_index;
+
   /* Last statement uid.  */
   int last_stmt_uid;
 
diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index a64529603f8..ad4180023e9 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -694,9 +694,9 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, fur_source &src, tree name)
   // If the specified query is different, make it the current one.
   // PR 125854 - The fold machinery may make a query call.
   // PR 126814 - tree_expr_nonnegative_p may make a call.
-  range_query *save = cfun->x_range_query;
-  if (src.query () != get_range_query (cfun))
-    cfun->x_range_query = src.query ();
+  // PR 126942 - path_ranger queries should never be the current query.
+  //             push_range_query will revert to a global query for this.
+  bool tmp_query_p = push_range_query (cfun, src.query ());
 
   gimple_range_op_handler handler (s);
   if (gimple_code (s) == GIMPLE_ASSIGN
@@ -731,7 +731,8 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, fur_source &src, tree name)
   if (!res)
     {
       // Restore the original query.
-      cfun->x_range_query = save;
+      if (tmp_query_p)
+	pop_range_query (cfun);
       // If no name specified or range is unsupported, bail.
       if (!name || !gimple_range_ssa_p (name))
 	return false;
@@ -743,7 +744,8 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, fur_source &src, tree name)
   if (r.undefined_p ())
     {
       // Restore the original query.
-      cfun->x_range_query = save;
+      if (tmp_query_p)
+	pop_range_query (cfun);
       return true;
     }
 
@@ -771,7 +773,7 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, fur_source &src, tree name)
 	  else
 	    {
 	      // If we couldn't find anything, try fold.
-	      x_fold_context = { s, src.query () };
+	      x_fold_context = { s, get_range_query (cfun) };
 	      rhs = gimple_fold_stmt_to_constant_1 (s, pta_valueize,
 						    pta_valueize);
 	      if (rhs && TREE_CODE (rhs) == ADDR_EXPR)
@@ -780,7 +782,8 @@ fold_using_range::fold_stmt (vrange &r, gimple *s, fur_source &src, tree name)
 	}
     }
   // Restore the original query.
-  cfun->x_range_query = save;
+  if (tmp_query_p)
+    pop_range_query (cfun);
   return true;
 }
 
diff --git a/gcc/gimple-range-path.h b/gcc/gimple-range-path.h
index 4c162ef11f6..b6a37638a33 100644
--- a/gcc/gimple-range-path.h
+++ b/gcc/gimple-range-path.h
@@ -42,6 +42,8 @@ public:
   bool range_of_expr (vrange &r, tree name, gimple * = NULL) override;
   bool range_of_stmt (vrange &r, gimple *, tree name = NULL) override;
   bool unreachable_path_p ();
+  // Path ranger should not be an active query.
+  virtual bool active_query_compatible_p () { return false; }
   void dump (FILE *) override;
   void debug ();
 
diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc
index b221cfc5616..8fca11555e9 100644
--- a/gcc/gimple-range.cc
+++ b/gcc/gimple-range.cc
@@ -748,10 +748,11 @@ enable_ranger (struct function *fun, bool use_imm_uses)
 {
   gimple_ranger *r;
 
-  gcc_checking_assert (!fun->x_range_query);
   r = new gimple_ranger (use_imm_uses);
-  fun->x_range_query = r;
+  gcc_assert (push_range_query (fun, r));
 
+  // Ranger should be the first and only instance.
+  gcc_checking_assert (fun->x_range_query_index == 1);
   return r;
 }
 
@@ -761,9 +762,10 @@ enable_ranger (struct function *fun, bool use_imm_uses)
 void
 disable_ranger (struct function *fun)
 {
-  gcc_checking_assert (fun->x_range_query);
-  delete fun->x_range_query;
-  fun->x_range_query = NULL;
+  range_query *q = pop_range_query (fun);
+  // There should be no other range queries active.
+  gcc_checking_assert (fun->x_range_query_index == 0);
+  delete q;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/gcc/testsuite/gcc.dg/pr126942.c b/gcc/testsuite/gcc.dg/pr126942.c
new file mode 100644
index 00000000000..c2100c6a895
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr126942.c
@@ -0,0 +1,35 @@
+/* { dg-do run } */
+/* { dg-options "-O1" } */
+
+signed char a;
+int b, c;
+short d[1];
+
+short
+foo (short *f, short g)
+{
+  int i = 0, j, l;
+  long n = 1;
+  for (int m = 0; m < a; m++)
+    for (; i <= m; i++)
+      n = i;
+  for (unsigned k = 0; k < g; k++)
+    {
+      l = 0;
+      do
+	if (f)
+	  j = l + a;
+      while (++l <= k);
+    }
+  n ^= j;
+  while (a)
+    n ^= b;
+  return n;
+}
+
+int
+main ()
+{
+  if (foo (d, 2) != 0)
+    __builtin_abort ();
+}
diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
index dc8256dc33d..8ef6ae7ee13 100644
--- a/gcc/tree-vrp.cc
+++ b/gcc/tree-vrp.cc
@@ -1241,20 +1241,21 @@ execute_fast_vrp (struct function *fun, bool final_p)
 {
   calculate_dominance_info (CDI_DOMINATORS);
   dom_ranger dr;
+  // Create a relation oracle without transitives.  It will automatically
+  // be destroyed when the destructor for 'dr' runs.
+  dr.create_relation_oracle (false);
   fvrp_folder folder (&dr, final_p);
 
-  gcc_checking_assert (!fun->x_range_query);
   set_all_edges_as_executable (fun);
-  fun->x_range_query = &dr;
-  // Create a relation oracle without transitives.
-  get_range_query (fun)->create_relation_oracle (false);
+  // Make DR the current range_query.
+  gcc_assert (push_range_query (fun, &dr));
 
   folder.substitute_and_fold ();
   if (folder.m_unreachable)
     folder.m_unreachable->remove ();
 
-  get_range_query (fun)->destroy_relation_oracle ();
-  fun->x_range_query = NULL;
+  range_query *q = pop_range_query (fun);
+  gcc_checking_assert (q == &dr);
   return 0;
 }
 
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 831dba9a39a..5878c3e67dd 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -35,6 +35,46 @@ along with GCC; see the file COPYING3.  If not see
 #include "value-range-storage.h"
 #include "target.h"
 
+// Attempt to make Q the current range query for FUN.  If it is already the
+// current range query, return false.
+// If the specified query is not compatible with being the current query,
+// instead push the global query for safety.  This is most common when
+// the requested query is a path ranger, and it can be unstable to make
+// arbitrary queries which may be in the middle or after a path.
+
+bool
+push_range_query (struct function *fun, range_query *q)
+{
+  // Set the global query if it hasn't been set.
+  if (!fun->x_range_query)
+    fun->x_range_query = get_global_range_query ();
+  // Ensure we have't pushed too many, otherwise this is indicitive of
+  // a problem,
+  gcc_checking_assert (fun->x_range_query_index < X_RANGE_QUERY_LIMIT);
+
+  // If this is not a compatible range query, revert to the global query.
+  if (!q->active_query_compatible_p ())
+    q = get_global_range_query ();
+
+  if (q == fun->x_range_query)
+    return false;
+  fun->x_range_query_stack[(fun->x_range_query_index)++] = fun->x_range_query;
+  fun->x_range_query = q;
+  return true;
+}
+
+// Restore the previous range query, and return the current one.
+
+range_query *
+pop_range_query (struct function *fun)
+{
+  range_query *q = fun->x_range_query;
+  gcc_checking_assert (fun->x_range_query_index > 0);
+  fun->x_range_query = fun->x_range_query_stack[--(fun->x_range_query_index)];
+  gcc_checking_assert (fun->x_range_query);
+  return q;
+}
+
 // range_query default methods.
 
 bool
diff --git a/gcc/value-query.h b/gcc/value-query.h
index 9932d7d493b..7e1c7e7453d 100644
--- a/gcc/value-query.h
+++ b/gcc/value-query.h
@@ -95,6 +95,8 @@ public:
   void create_gori (int not_executable_flag = 0, int sw_max_edges = INT_MAX);
   void destroy_gori ();
 
+  // Return TRUE if this path query can be a current_range_query.
+  virtual bool active_query_compatible_p () { return true; }
   virtual void dump (FILE *);
 
 protected:
@@ -139,6 +141,11 @@ get_range_query (const struct function *fun)
   return (fun && fun->x_range_query) ? fun->x_range_query : &global_ranges;
 }
 
+// Provide a way to set and a new current range query and then restore the
+// previous range query.  Return FALSE if the current query didn't change.
+bool push_range_query (struct function *fun, range_query *q);
+range_query *pop_range_query (struct function *fun);
+
 // Query the global range of NAME in function F.  Default to cfun.
 extern void gimple_range_global (vrange &v, tree name,
 				 struct function *f = cfun);
-- 
2.55.0

