When looking up valuesin rangers cache, we search the dominator tree and
propagate values into the cache. We also added any inferred ranges as
they are encountered in blocks. If the incoming edge to the block is
abnormal, we do not add this inferred range.
The PR demonstrates that this is insufficient. We need to check the
taken outgoing edge from each dominator for abnormality and not
propagate a value if the edge was abnormal.
Bootstrapped on x86_64-pc-linux-gnu with no regressions. Pushed.
Andrew
From de2d80d1101e1921d719918d57589ea2668dcc1f Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Thu, 6 Aug 2026 14:15:21 -0400
Subject: [PATCH 3/4] Check for abnoraml ranges on full dom search.
Inferred ranges are not adde dif the predecessor was an abnormal edge.
This is extended to finding a taken abnrmal edge anywhere in the dominator
search.
PR tree-optimization/126531
gcc/
* gimple-range-cache.cc (ranger_cache::range_from_dom): Track
abnormal edges throughout the dom search.
gcc/testsuite/
* g++.dg/pr126531.C: New.
---
gcc/gimple-range-cache.cc | 26 +++++++++++++----
gcc/testsuite/g++.dg/pr126531.C | 49 +++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+), 5 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/pr126531.C
diff --git a/gcc/gimple-range-cache.cc b/gcc/gimple-range-cache.cc
index c5a19c866fb..a540a10ce58 100644
--- a/gcc/gimple-range-cache.cc
+++ b/gcc/gimple-range-cache.cc
@@ -1813,11 +1813,28 @@ ranger_cache::range_from_dom (vrange &r, tree name, basic_block start_bb,
else
bb = get_immediate_dominator (CDI_DOMINATORS, start_bb);
+ bool abnormal_dominator = false;
// Search until a value is found, pushing blocks which may need calculating.
for ( ; bb; prev_bb = bb, bb = get_immediate_dominator (CDI_DOMINATORS, bb))
{
- // Accumulate any block exit inferred ranges.
- infer_oracle ().maybe_adjust_range (infer, name, bb);
+ if (has_abnormal_call_or_eh_pred_edge_p (prev_bb))
+ abnormal_dominator = true;
+
+ // find the taken outgoing edge and check if it is abnormal.
+ if (!abnormal_dominator)
+ {
+ edge e;
+ edge_iterator ei;
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ if (dominated_by_p (CDI_DOMINATORS, prev_bb, e->dest))
+ {
+ if (e->flags & (EDGE_ABNORMAL | EDGE_EH))
+ abnormal_dominator = true;
+ break;
+ }
+ // Accumulate any block exit inferred ranges.
+ infer_oracle ().maybe_adjust_range (infer, name, bb);
+ }
// This block has an outgoing range.
if (gori ().has_edge_range_p (name, bb))
@@ -1904,9 +1921,8 @@ ranger_cache::range_from_dom (vrange &r, tree name, basic_block start_bb,
}
}
- // Apply non-null if appropriate.
- if (!has_abnormal_call_or_eh_pred_edge_p (start_bb))
- r.intersect (infer);
+ // Apply any inferred ranges discovered.
+ r.intersect (infer);
if (DEBUG_RANGE_CACHE)
{
diff --git a/gcc/testsuite/g++.dg/pr126531.C b/gcc/testsuite/g++.dg/pr126531.C
new file mode 100644
index 00000000000..4401dbf8faf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr126531.C
@@ -0,0 +1,49 @@
+// { dg-do run }
+//
+/* More than 16 bytes, so S is returned in memory. *p becomes the return slot
+ of the call and GIMPLE keeps a single statement "*p_7(D) = h (k_8(D));",
+ a call that can throw whose store operand infers p != 0. */
+
+struct S { int a[8]; };
+
+__attribute__((noipa)) S
+h (int k)
+{
+ if (k)
+ throw 1; /* Thrown before anything is stored. */
+ S s = {};
+ s.a[0] = 5;
+ return s;
+}
+
+__attribute__((noipa)) int
+f (S *p, int k)
+{
+ int r = 0;
+ try
+ {
+ *p = h (k); /* Infers p != 0, but only if it completes. */
+ r = 1;
+ }
+ catch (...)
+ {
+ if (p == 0) /* Must not fold: the store never ran. */
+ r = 12;
+ else
+ r = 2;
+ }
+ return r;
+}
+
+S obj;
+
+int
+main (void)
+{
+ if (f (&obj, 0) != 1) /* Normal path, p is &obj. */
+ __builtin_abort ();
+ if (f (0, 1) != 12) /* h throws, no store, p is null. */
+ __builtin_abort ();
+ return 0;
+}
+
--
2.45.0