With the addition of some `if (a) __builtin_unreachable();` in libstdc++,
the loop ch might not be copying the full header and only copy the bb
which contains the condition that leads to the `__builtin_unreachable()`.
We want to continue if there is such a condition on to the next bb and copy
that too. There is code already to do the copying, just the costing part
needs to be improved.
In the C testcase we have:
```
<bb2>:
goto <bb4>;
<bb3>:
....
``````
<bb 4> [local count: 1073741824]:
# i_4 = PHI <0(2), i_11(3)>
t_8 = *l_7(D);
if (t_8 < 0)
goto <bb 5>; [0.04%]
else
goto <bb 6>; [99.96%]
<bb 5> [local count: 429496]:
__builtin_unreachable ();
<bb 6> [local count: 1073312328]:
if (i_4 < t_8)
goto <bb 3>; [94.50%]
else
goto <bb 7>; [5.50%]
So we want to duplicate bb 4 and bb 6 here and nothing else. Before this
we would duplicate only bb 4 which caused problems later on for other loop
optimizations. We remove the unreachable before the loop optimizations but don't
have another ch until right before vect so LIM, ldist and others mess up.
Bootstrapped and tested on x86_64-linux-gnu.
PR tree-optimization/122734
gcc/ChangeLog:
* tree-ssa-loop-ch.cc (should_duplicate_loop_header_p): New argument
canbe_unreachable. Test canbe_unreachable and treat a conditional which
is leading to a __builtin_unreachable as being an "invariant" one.
(ch_base::copy_headers): Update call to should_duplicate_loop_header_p.
gcc/testsuite/ChangeLog:
* gcc.dg/tree-ssa/copy-headers-10.c: New test.
Signed-off-by: Andrew Pinski <[email protected]>
---
.../gcc.dg/tree-ssa/copy-headers-10.c | 25 ++++++++++++++
gcc/tree-ssa-loop-ch.cc | 34 +++++++++++++++++--
2 files changed, 56 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/copy-headers-10.c
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-10.c
b/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-10.c
new file mode 100644
index 00000000000..5641a9a1d3a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/copy-headers-10.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-ch-details -fdump-tree-ldist-details" } */
+
+/* PR tree-optimization/122734 */
+/* We want to duplicate the block after the one containing the condition going
to unreachable.
+ Since later on we will be removing the condition going to unreachable
anyways. */
+/* So in the end ldist can generate a memset. */
+
+static inline int size(int *a)
+{
+ int t = *a;
+ if (t < 0) __builtin_unreachable();
+ return t;
+}
+
+void f(int *l, short *d)
+{
+ for(int i = 0; i < size(l); i++)
+ d[i] = 0;
+}
+
+/* { dg-final { scan-tree-dump-times "Duplicating bb . is a win" 1 "ch2" } } */
+/* { dg-final { scan-tree-dump-times "Will duplicate bb" 2 "ch2" } } */
+/* { dg-final { scan-tree-dump "is now do-while loop" "ch2" } } */
+/* { dg-final { scan-tree-dump "generated memset zero" "ldist" } } */
diff --git a/gcc/tree-ssa-loop-ch.cc b/gcc/tree-ssa-loop-ch.cc
index 91a61dd4e80..1b38dcf8189 100644
--- a/gcc/tree-ssa-loop-ch.cc
+++ b/gcc/tree-ssa-loop-ch.cc
@@ -190,14 +190,15 @@ enum ch_decision
/* Check whether we should duplicate HEADER of LOOP. At most *LIMIT
instructions should be duplicated, limit is decreased by the actual
- amount. */
+ amount. In the case of *CANBE_UNREACHBABLE, if there is a exit edge of the
HEADER, that goes directly to unreachable, then consider that as invariant and
continue. Set *CANBE_UNREACHBABLE to false otherwise. */
static ch_decision
should_duplicate_loop_header_p (basic_block header, class loop *loop,
gimple_ranger *ranger,
int *limit,
hash_set <edge> *invariant_exits,
- hash_set <edge> *static_exits)
+ hash_set <edge> *static_exits,
+ bool *canbe_unreachable)
{
gimple_stmt_iterator bsi;
@@ -460,6 +461,31 @@ should_duplicate_loop_header_p (basic_block header, class
loop *loop,
}
return ch_win_invariant_exit;
}
+ if (*canbe_unreachable)
+ {
+ /* See if one of the edges are an edge to __builtin_unreachable().
+ If so treat it as invariant exit win. */
+ edge e;
+ edge_iterator ei;
+ bool hasone = false;
+ FOR_EACH_EDGE (e, ei, header->succs)
+ if (loop_exit_edge_p (loop, e))
+ {
+ auto gsi = gsi_start_nondebug_after_labels_bb (e->dest);
+ if (!gsi_end_p (gsi)
+ && gimple_call_builtin_p (*gsi, BUILT_IN_UNREACHABLE))
+ {
+ hasone = true;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ " `unreachable` exit %i->%i\n",
+ e->src->index, e->dest->index);
+ }
+ }
+ if (hasone)
+ return ch_win_invariant_exit;
+ *canbe_unreachable = false;
+ }
/* If the static exit fully optimize out, it is win to "duplicate"
it.
@@ -846,10 +872,12 @@ ch_base::copy_headers (function *fun)
auto_vec <ch_decision, 32> decision;
hash_set <edge> *invariant_exits = new hash_set <edge>;
hash_set <edge> *static_exits = new hash_set <edge>;
+ bool canbe_unreachable = true;
while ((ret = should_duplicate_loop_header_p (header, loop, ranger,
&remaining_limit,
invariant_exits,
- static_exits))
+ static_exits,
+ &canbe_unreachable))
!= ch_impossible)
{
nheaders++;
--
2.43.0