PR tree-optimization/126942- Disallow path ranger from current_range_query.
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap |
success
|
Build passed
|
Commit Message
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?
Bootstrapped on x86_64-pc-linux-gnu with no regressions. OK?
Andrew
Comments
> Am 01.09.2026 um 14:53 schrieb Andrew MacLeod <amacleod@redhat.com>:
>
> 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?
Richard
> Bootstrapped on x86_64-pc-linux-gnu with no regressions. OK?
>
> Andrew
>
> <0002-Disallow-path-ranger-from-current_range_query.patch>
On 9/1/26 11:06 AM, Richard Biener wrote:
>
>> Am 01.09.2026 um 14:53 schrieb Andrew MacLeod <amacleod@redhat.com>:
>>
>> 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 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
@@ -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;
@@ -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;
}
@@ -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 ();
@@ -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;
}
// ---------------------------------------------------------------------------
new file mode 100644
@@ -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 ();
+}
@@ -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;
}
@@ -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
@@ -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