On Wed, Apr 2, 2025 at 11:52 PM Richard Biener <richard.guent...@gmail.com> wrote: > > On Thu, Apr 3, 2025 at 7:10 AM Andrew Pinski <quic_apin...@quicinc.com> wrote: > > > > builtin_unreachable_bb_p exacts empty basic blocks to have only one > > successor edge but > > in the case after inliing a noreturn function that actually returns, we end > > up with an > > empty basic block with no successor edge. fixup_cfg fixes this up by > > placing a call to > > __builtin_unreachable but we don't do that before getting the function > > summary > > (after einiline). So the fix is to have the inliner insert the call > > to __builtin_unreachable (or the trap version) and not depend on the fixup > > if the return > > block is reachable after inlining a noreturn function. > > > > A small optimization is done not to insert the call if the block is NOT > > simplely reachable. > > This should prevent inserting the call just to remove it during cleanupcfg. > > > > Note this shows up only at -O0 with always_inline because when > > optimizations are turned on > > returns are turned into __builtin_unreachable inside noreturn functions. > > Where's that done? IMO we should do this at -O0 as well (possibly defaulting > to > use the trapping variant there).
It is done inside pass_warn_function_return::execute and that was added by r8-3988-g356fcc67fba52b . Since it already uses gimple_build_builtin_unreachable, BUILT_IN_UNREACHABLE_TRAP is already used when flag_unreachable_traps is on (which defaults being on for -O0 and -gg). So should we just change that to be always on instead of being blocked for !optimized? Thanks, Andrew Pinski > > > > > Bootstrapped and tested on x86_64-linux-gnu. > > > > PR ipa/119599 > > > > gcc/ChangeLog: > > > > * tree-inline.cc (expand_call_inline): Add a __builtin_unreachable > > call > > to the return block if it is still reachable and the call that was > > inlined > > was a noreturn function. > > > > gcc/testsuite/ChangeLog: > > > > * gcc.dg/torture/pr119599-1.c: New test. > > > > Signed-off-by: Andrew Pinski <quic_apin...@quicinc.com> > > --- > > gcc/testsuite/gcc.dg/torture/pr119599-1.c | 27 +++++++++++++++++++++++ > > gcc/tree-inline.cc | 15 +++++++++++++ > > 2 files changed, 42 insertions(+) > > create mode 100644 gcc/testsuite/gcc.dg/torture/pr119599-1.c > > > > diff --git a/gcc/testsuite/gcc.dg/torture/pr119599-1.c > > b/gcc/testsuite/gcc.dg/torture/pr119599-1.c > > new file mode 100644 > > index 00000000000..4fbd228a359 > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/torture/pr119599-1.c > > @@ -0,0 +1,27 @@ > > +/* { dg-do compile } */ > > +/* { dg-options "-fdump-tree-einline" } */ > > + > > +/* PR ipa/119599 */ > > +/* inlining a noreturn function which returns > > + can cause an ICE when dealing finding an unreachable block. > > + We should get a __builtin_unreachable after the inliing. */ > > + > > + > > +void baz (void); > > + > > +static inline __attribute__((always_inline, noreturn)) void > > +bar (void) > > +{ > > + static volatile int t = 0; > > + if (t == 0) > > + baz (); > > +} /* { dg-warning "function does return" } */ > > + > > +void > > +foo (void) > > +{ > > + bar (); > > +} > > + > > +/* After inlining, we should have call to __builtin_unreachable now. */ > > +/* { dg-final { scan-tree-dump "__builtin_unreachable " "einline" } } */ > > diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc > > index 05843b8ccf0..da403cd1456 100644 > > --- a/gcc/tree-inline.cc > > +++ b/gcc/tree-inline.cc > > @@ -4832,6 +4832,7 @@ expand_call_inline (basic_block bb, gimple *stmt, > > copy_body_data *id, > > gimple *simtenter_stmt = NULL; > > vec<tree> *simtvars_save; > > tree save_stack = NULL_TREE; > > + bool was_noreturn; > > > > /* The gimplifier uses input_location in too many places, such as > > internal_get_tmp_var (). */ > > @@ -4843,6 +4844,8 @@ expand_call_inline (basic_block bb, gimple *stmt, > > copy_body_data *id, > > if (!call_stmt) > > goto egress; > > > > + was_noreturn = gimple_call_noreturn_p (call_stmt); > > + > > cg_edge = id->dst_node->get_edge (stmt); > > gcc_checking_assert (cg_edge); > > /* First, see if we can figure out what function is being called. > > @@ -5376,6 +5379,18 @@ expand_call_inline (basic_block bb, gimple *stmt, > > copy_body_data *id, > > if (purge_dead_abnormal_edges) > > bitmap_set_bit (to_purge, return_block->index); > > > > + /* If this was a noreturn function and if the return block is still > > + reachable, then add a call to __builtin_unreachable(). */ > > + if (was_noreturn && EDGE_COUNT (return_block->preds) != 0) > > + { > > + gcall *call_stmt = gimple_build_builtin_unreachable (input_location); > > + gimple_stmt_iterator gsi = gsi_last_bb (return_block); > > + gsi_insert_after (&gsi, call_stmt, GSI_NEW_STMT); > > + tree fndecl = gimple_call_fndecl (call_stmt); > > + cg_edge->caller->create_edge (cgraph_node::get_create (fndecl), > > + call_stmt, return_block->count); > > + } > > + > > /* If the value of the new expression is ignored, that's OK. We > > don't warn about this for CALL_EXPRs, so we shouldn't warn about > > the equivalent inlined version either. */ > > -- > > 2.43.0 > >