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. 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