On Wed, Nov 19, 2025 at 1:38 PM Andrew Pinski <[email protected]> wrote: > > 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.
Now I wonder if we should do this for more than __builtin_unreachable functions and handle all noreturn functions. The reason is because that is not the header part that matters. In the C testcase replace __builtin_unreachable with say __builtin_abort and you end up with similar bad code generation. I am going to test a patch on top of this that allows for all noreturn functions (and do some simple rename). This should allow for GCC's vec to be optimized too. Thanks, Andrew > > 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 >
