On 9/1/26 11:06 AM, Richard Biener wrote:
Am 01.09.2026 um 14:53 schrieb Andrew MacLeod <[email protected]>:
I think this PR ends the debate about whether a path_range should become the
current range_query.
The problem is in tree-ssa-loop-ch.cc. It uses path_range_query to evaluate a
path, and during a range_of_stmt() call for a statement on that path, the path
ranger becomes the current range query. While servicing that query, a nested
query involving a PHI causes SCEV analysis to be invoked. SCEV then performs
loop analysis using the path ranger as the current query. This use to be a
query mismatch and we refused to invoke SCEV because it wouldn't work right
when picking up ranges from the path further down in the loop during general
analysis.
So. I slightly reworked the current ad-hoc "change the current query"
mechanism to allow for a small amount of organized push/pop. The current query always
starts as a global_range_query. Enable/disable_ranger() is altered to push/pop a new
ranger instance (as has fast_vrp), and gimple-range-fold() also temporarily changes to
current range-query IFF the incoming query is different than the current one.
I added an active_query_compatible_p () method to a range_query, and have it
return false for any range_query (ie path-ranger) which is not compatible with
this.
I put all the "smarts" into the push routine. There is a series of 3 PRs:
125854, 126814 and now 126942 which combined, demonstrate that the only safe course of
action is to revert to the global query if there is a mismatch which can't be replaced...
And this is now what push_range_query () does. If the current query is the same as the
requested one, it does nothing. If the incoming query to be used does not match the
current query, AND is not compatible to become the current query, it instead pushes the
global range query.
I submit this for approval because I added a small vector to "struct function"
which managed the push/pop list, and I wanted to make sure that is OK. Its currently set
to 5 pointer elements and an unsigned index counter. It might be possible to reduce it
to 3, but I figured 5 was future proof. Im not sure how sensitive the size of struct
function is, so wanted to check. If anything ever traps the vec size check, we''re doing
something wonky that is unexpected and needs to be examined. Is a vec of 5 plus a word
OK to add to struct function?
It is quite sensitive. Since we should never inherit the ranger stack state
between switching the current function (we could assert its empty) can you make
this a global stack instead please?
I feared that. Im not a big fan of the global stack either since the
current query keys off a function pointer.. so instead I just made the
caller deal with it. this isn't something a client pass really needs to
deal with anyway, enable/disable_ranger takes care of that.
Bootstrapped on x86_64-pc-linux-gnu with no regressions. pushed.
Andrew
From aa98124eff9bfe362d5446deacb7c2637f70fd3e Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Mon, 31 Aug 2026 14:25:21 -0400
Subject: [PATCH] Disallow path ranger from current_range_query.
Provide a generic way to set the current range_query, and disable
path_ranger from being pushed. It can provide incorrect ranges from
the path to requests not dominated by the path.
PR tree-optimization/126942
gcc/
* gimple-range-fold.cc (fold_using_range::fold_stmt): Use
set_range_query.
* gimple-range-path.h (path_ranger::active_query_compatible_p): New.
* gimple-range.cc (enable_ranger): Use set_range_query.
(disable_ranger): Likewise.
* tree-vrp.cc (execute_fast_vrp): Likewise.
* value-query.cc (set_range_query): New.
* value-query.h (range_query::active_query_compatible_p): New.
(set_range_query): Declare.
gcc/testsuite/
* gcc.dg/pr126942.c: New.
---
gcc/gimple-range-fold.cc | 17 +++++++++-------
gcc/gimple-range-path.h | 2 ++
gcc/gimple-range.cc | 11 ++++++-----
gcc/testsuite/gcc.dg/pr126942.c | 35 +++++++++++++++++++++++++++++++++
gcc/tree-vrp.cc | 13 ++++++------
gcc/value-query.cc | 26 ++++++++++++++++++++++++
gcc/value-query.h | 6 ++++++
7 files changed, 92 insertions(+), 18 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/pr126942.c
diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index a64529603f8..7dd9ddfd796 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.
+ // set_range_query will revert to a global query for this.
+ range_query *saved_query = set_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 (saved_query)
+ set_range_query (cfun, saved_query);
// 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 (saved_query)
+ set_range_query (cfun, saved_query);
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 (saved_query)
+ set_range_query (cfun, saved_query);
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..898eb6964b7 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;
+ range_query *q = set_range_query (fun, r);
+ // Ranger should be the first and only instance.
+ gcc_checking_assert (q == get_global_range_query ());
return r;
}
@@ -761,9 +762,9 @@ 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;
+ // Ensure this is the query being removed.
+ range_query *q = set_range_query (fun, get_global_range_query ());
+ 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..11af4f681c0 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.
+ range_query *saved = set_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 = set_range_query (fun, saved);
+ gcc_checking_assert (q == &dr);
return 0;
}
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 831dba9a39a..337f66d9a79 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -35,6 +35,32 @@ 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 NULL.
+// 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.
+
+range_query *
+set_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 ();
+
+ // 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 NULL;
+
+ range_query *orig = fun->x_range_query;
+ fun->x_range_query = q;
+ return orig;
+}
+
// range_query default methods.
bool
diff --git a/gcc/value-query.h b/gcc/value-query.h
index 9932d7d493b..85f877d9453 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,10 @@ get_range_query (const struct function *fun)
return (fun && fun->x_range_query) ? fun->x_range_query : &global_ranges;
}
+// Provide a way to change the current range query if it is valid, return
+// the previous query.
+range_query *set_range_query (struct function *fun, range_query *q);
+
// 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