On 1/12/26 11:37, Martin Jambor wrote:
Hello,
On Mon, Jan 12 2026, Andrew MacLeod wrote:
On 1/6/26 12:35, Andrew MacLeod wrote:
VRP1 is allowed to remove a __builtin_unreachable () if it determines
that the other edge in the branch dominates all uses of all exports
from the block.
ie
<bb 3>:
x_8 = 1 << i_7;
if (x_8 <= 0) <<-- this is the branch in BB3
goto <bb 4>; [INV]
else
goto <bb 5>; [INV]
<bb 4> :
__builtin_unreachable ();
we can remove the branch and edge to bb 4 if we can prove that there
are no uses of x_8 or i_7 in or dominated by the edge 3->5. We can
then set the global ranges of i_7 and x_8 and move along.
This PR demonstrates that this normally works, but with rangers
ability to recompute values, we also have to look at the dependencies
of all the exports.
IN this case i_7 is defined:
<bb 7> :
# i_2 = PHI <i_7(5), n_4(D)(2), i_7(6)>
i_7 = i_2 + -1;
if (i_2 > 0)
goto <bb 3>; [INV]
else
goto <bb 8>; [INV]
so althoug the uses of i_7 Are dominated by 3->5, it does NOT
dominate the use of i_2 in bb_7. When early removal changes the
global value of i_7, ranger happily recomputes i_2 in the branch and
decides that if i_7 is now [0, +INF], i_2 must always be > 0 and
removes the branch.
Which is clearly incorrect. This patch teaches the early removal code
that it can only remove the unreachable call if x_8, i_7 and ALL the
dependencies used to calculate either name are ALL dominated by the
edge. This information is all trivially available in GORI, so its
noly a minor tweak.
Performance impact over a build of GCC is minimal. a 0.03% slowdown in
VRP.
In the PR I mentioned not removing it if any of the dependencies had
more than a single use, but that turned out to be too limiting. This
solution works much better.
Bootstraps on x86_64-pc-linux-gnu with no regressions. OK?
Andrew
Patch applied to GCC 15. OK for branch or not?
I think you have attached a wrong patch? This one already is part of
GCC 15, I think.
Martin
Uhh, yeah.. how the heck did that happen.
Anyway, proper one here :-P. oh, and both bootstrap on
x86_64-pc-linux-gnu with no regressions.
From ea25665b2c97e48c2c99281dbb66d18a2edd1962 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Wed, 7 Jan 2026 10:55:49 -0500
Subject: [PATCH] Early builtin_unreachable removal must examine dependencies.
Even if all uses of a name are dominated by the unreachable branch,
recomputation of a value in the defintion of a name might be reachable.
PR tree-optimization/123300
gcc/
* gimple-range-gori.cc (gori_map::exports_and_deps): New.
* gimple-range-gori.h (exports_and_deps): New prototype.
(FOR_EACH_GORI_EXPORT_AND_DEP_NAME): New macro.
* tree-vrp.cc (remove_unreachable:remove_unreachable): Initialize
m_tmp bitmap.
(remove_unreachable:~remove_unreachable): Dispose of m_tmp bitmap.
(remove_unreachable:fully_replaceable): Move from static function
and check reachability of exports and dependencies.
gcc/testsuite/
* gcc.dg/pr123300.c: New.
---
gcc/gimple-range-gori.cc | 22 ++++++++++++++++++++++
gcc/gimple-range-gori.h | 9 ++++++++-
gcc/testsuite/gcc.dg/pr123300.c | 29 +++++++++++++++++++++++++++++
gcc/tree-vrp.cc | 16 ++++++++++------
4 files changed, 69 insertions(+), 7 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/pr123300.c
diff --git a/gcc/gimple-range-gori.cc b/gcc/gimple-range-gori.cc
index 9761abffc00..9ce06669232 100644
--- a/gcc/gimple-range-gori.cc
+++ b/gcc/gimple-range-gori.cc
@@ -383,6 +383,28 @@ gori_map::exports (basic_block bb)
return m_outgoing[bb->index];
}
+// Return the bitmap vector of all exports AND their dependencies from BB
+// in TMPBIT. Calculate if necessary. Return TMPBIT.
+
+bitmap
+gori_map::exports_and_deps (basic_block bb, bitmap tmpbit)
+{
+ if (bb->index >= (signed int)m_outgoing.length () || !m_outgoing[bb->index])
+ calculate_gori (bb);
+ bitmap_copy (tmpbit, m_outgoing[bb->index]);
+ if (!bitmap_empty_p (tmpbit))
+ {
+ tree name;
+ FOR_EACH_GORI_EXPORT_NAME (this, bb, name)
+ {
+ bitmap dep = get_def_chain (name);
+ if (dep)
+ bitmap_ior_into (tmpbit, dep);
+ }
+ }
+ return tmpbit;
+}
+
// Return the bitmap vector of all imports to BB. Calculate if necessary.
bitmap
diff --git a/gcc/gimple-range-gori.h b/gcc/gimple-range-gori.h
index 15eaa91d7ad..16a41bd882f 100644
--- a/gcc/gimple-range-gori.h
+++ b/gcc/gimple-range-gori.h
@@ -99,6 +99,7 @@ public:
bool is_export_p (tree name, basic_block bb = NULL);
bool is_import_p (tree name, basic_block bb);
bitmap exports (basic_block bb);
+ bitmap exports_and_deps (basic_block bb, bitmap tmpbit);
bitmap imports (basic_block bb);
void set_range_invariant (tree name, bool invariant = true);
@@ -223,7 +224,7 @@ bool gori_on_edge (class ssa_cache &r, edge e, range_query *query = NULL);
bool gori_name_on_edge (vrange &r, tree name, edge e, range_query *q = NULL);
// For each name that is an import into BB's exports..
-#define FOR_EACH_GORI_IMPORT_NAME(gorimap, bb, name) \
+#define FOR_EACH_GORI_IMPORT_NAME(gorimap, bb, name) \
for (gori_export_iterator iter ((gorimap)->imports ((bb))); \
((name) = iter.get_name ()); \
iter.next ())
@@ -234,6 +235,12 @@ bool gori_name_on_edge (vrange &r, tree name, edge e, range_query *q = NULL);
((name) = iter.get_name ()); \
iter.next ())
+// For each name and all their dependencies possibly exported from block BB.
+#define FOR_EACH_GORI_EXPORT_AND_DEP_NAME(gorimap, bb, name, bm) \
+ for (gori_export_iterator iter ((gorimap)->exports_and_deps ((bb),(bm))); \
+ ((name) = iter.get_name ()); \
+ iter.next ())
+
// Used to assist with iterating over the GORI export list in various ways
class gori_export_iterator {
public:
diff --git a/gcc/testsuite/gcc.dg/pr123300.c b/gcc/testsuite/gcc.dg/pr123300.c
new file mode 100644
index 00000000000..7309f3dd9c0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr123300.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-vrp1" } */
+[[gnu::noipa]] void
+bar (int a, int b)
+{
+ if (a < 0)
+ __builtin_abort ();
+}
+
+[[gnu::noipa]] void
+foo (int n, bool p)
+{
+ for (int i = n; i-- > 0;)
+ {
+ const int x = 1 << i;
+ if (x <= 0)
+ __builtin_unreachable ();
+ if (p)
+ bar (i, x);
+ }
+}
+
+int
+main ()
+{
+ foo (4, true);
+}
+/* { dg-final { scan-tree-dump "__builtin_unreachable" "vrp1" } } */
+
diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
index 5aeb1e066cf..9b2d3d708cc 100644
--- a/gcc/tree-vrp.cc
+++ b/gcc/tree-vrp.cc
@@ -87,15 +87,17 @@ along with GCC; see the file COPYING3. If not see
class remove_unreachable {
public:
remove_unreachable (range_query &r, bool all) : m_ranger (r), final_p (all)
- { m_list.create (30); }
- ~remove_unreachable () { m_list.release (); }
+ { m_list.create (30); m_tmp = BITMAP_ALLOC (NULL); }
+ ~remove_unreachable () { BITMAP_FREE (m_tmp); m_list.release (); }
void handle_early (gimple *s, edge e);
void maybe_register (gimple *s);
bool remove ();
bool remove_and_update_globals ();
+ bool fully_replaceable (tree name, basic_block bb);
vec<std::pair<int, int> > m_list;
range_query &m_ranger;
bool final_p;
+ bitmap m_tmp;
};
// Check if block BB has a __builtin_unreachable () call on one arm, and
@@ -141,8 +143,8 @@ remove_unreachable::maybe_register (gimple *s)
// goto <bb 3>; [0.00%]
// Any additional use of _1 or _2 in this block invalidates early replacement.
-static bool
-fully_replaceable (tree name, basic_block bb)
+bool
+remove_unreachable::fully_replaceable (tree name, basic_block bb)
{
use_operand_p use_p;
imm_use_iterator iter;
@@ -213,9 +215,11 @@ remove_unreachable::handle_early (gimple *s, edge e)
gcc_checking_assert (gimple_outgoing_range_stmt_p (e->src) == s);
gcc_checking_assert (!final_p);
- // Check if every export use is dominated by this branch.
+ // Check if every export and its dependencies are dominated by this branch.
+ // Dependencies are required as it needs to dominate potential
+ // recalculations. See PR 123300.
tree name;
- FOR_EACH_GORI_EXPORT_NAME (m_ranger.gori_ssa (), e->src, name)
+ FOR_EACH_GORI_EXPORT_AND_DEP_NAME (m_ranger.gori_ssa (), e->src, name, m_tmp)
{
if (!fully_replaceable (name, e->src))
return;
--
2.45.0