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

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

Reply via email to