diff --git a/gcc/calls.cc b/gcc/calls.cc index fa538df33e0..b3dccd85ec8 100644 --- a/gcc/calls.cc +++ b/gcc/calls.cc @@ -2568,7 +2568,7 @@ can_implement_as_sibling_call_p (tree exp, maybe_complain_about_tail_call (exp, _("callee returns twice")); return false; } - if (flags & ECF_NORETURN) + if ((flags & ECF_NORETURN) && !CALL_EXPR_MUST_TAIL_CALL (exp)) { maybe_complain_about_tail_call (exp, _("callee does not return")); return false; diff --git a/gcc/testsuite/c-c++-common/pr119483-1.c b/gcc/testsuite/c-c++-common/pr119483-1.c new file mode 100644 index 00000000000..b2d7b57795a --- /dev/null +++ b/gcc/testsuite/c-c++-common/pr119483-1.c @@ -0,0 +1,29 @@ +/* PR tree-optimization/119483 */ +/* { dg-do compile { target musttail } } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ +/* { dg-final { scan-tree-dump-times "bar\[.a-z0-9]* \\\(\[^\n\r]*\\\); \\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "baz \\\(\[^\n\r]*\\\); \\\[tail call\\\] \\\[must tail call\\\]" 1 "optimized" } } */ + +[[gnu::noreturn]] extern void foo (void); + +[[gnu::noinline]] static int +bar (int x) +{ + (void) x; + foo (); + return 0; +} + +[[gnu::noipa]] int +baz (int x) +{ + return x + 42; +} + +int +qux (int x) +{ + if (x == 1) + [[gnu::musttail]] return bar (1); + [[gnu::musttail]] return baz (x); +} diff --git a/gcc/testsuite/c-c++-common/pr119483-2.c b/gcc/testsuite/c-c++-common/pr119483-2.c new file mode 100644 index 00000000000..e7b692dc80b --- /dev/null +++ b/gcc/testsuite/c-c++-common/pr119483-2.c @@ -0,0 +1,12 @@ +/* PR tree-optimization/119483 */ +/* { dg-do compile { target musttail } } */ +/* { dg-options "-O2" } */ + +[[noreturn]] int +foo (int x) +{ + if (x > 10) + [[gnu::musttail]] return foo (x - 1); /* { dg-warning "function declared 'noreturn' has a 'return' statement" } */ + for (;;) + ; +} diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc index 3efbe9042f2..8ba67522191 100644 --- a/gcc/tree-tailcall.cc +++ b/gcc/tree-tailcall.cc @@ -484,7 +484,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, size_t idx; tree var; - if (!single_succ_p (bb)) + if (!single_succ_p (bb) + && (EDGE_COUNT (bb->succs) || !cfun->has_musttail || !diag_musttail)) { /* If there is an abnormal edge assume it's the only extra one. Tolerate that case so that we can give better error messages @@ -605,7 +606,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, /* If the call might throw an exception that wouldn't propagate out of cfun, we can't transform to a tail or sibling call (82081). */ if ((stmt_could_throw_p (cfun, stmt) - && !stmt_can_throw_external (cfun, stmt)) || !single_succ_p (bb)) + && !stmt_can_throw_external (cfun, stmt)) || EDGE_COUNT (bb->succs) > 1) { if (stmt == last_stmt) maybe_error_musttail (call, @@ -760,10 +761,12 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, a = NULL_TREE; auto_bitmap to_move_defs; auto_vec to_move_stmts; + bool is_noreturn + = EDGE_COUNT (bb->succs) == 0 && gimple_call_noreturn_p (call); abb = bb; agsi = gsi; - while (1) + while (!is_noreturn) { tree tmp_a = NULL_TREE; tree tmp_m = NULL_TREE; @@ -844,7 +847,22 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, } /* See if this is a tail call we can handle. */ - ret_var = gimple_return_retval (as_a (stmt)); + if (is_noreturn) + { + tree rettype = TREE_TYPE (TREE_TYPE (current_function_decl)); + tree calltype = TREE_TYPE (gimple_call_fntype (call)); + if (!VOID_TYPE_P (rettype) + && !useless_type_conversion_p (rettype, calltype)) + { + maybe_error_musttail (call, + _("call and return value are different"), + diag_musttail); + return; + } + ret_var = NULL_TREE; + } + else + ret_var = gimple_return_retval (as_a (stmt)); /* We may proceed if there either is no return value, or the return value is identical to the call's return or if the return decl is an empty type @@ -1153,24 +1171,32 @@ eliminate_tail_call (struct tailcall *t, class loop *&new_loop) gsi_prev (&gsi2); } - /* Number of executions of function has reduced by the tailcall. */ - e = single_succ_edge (gsi_bb (t->call_gsi)); + if (gimple_call_noreturn_p (as_a (stmt))) + { + e = make_edge (gsi_bb (t->call_gsi), first, EDGE_FALLTHRU); + e->probability = profile_probability::always (); + } + else + { + /* Number of executions of function has reduced by the tailcall. */ + e = single_succ_edge (gsi_bb (t->call_gsi)); - profile_count count = e->count (); + profile_count count = e->count (); - /* When profile is inconsistent and the recursion edge is more frequent - than number of executions of functions, scale it down, so we do not end - up with 0 executions of entry block. */ - if (count >= ENTRY_BLOCK_PTR_FOR_FN (cfun)->count) - count = ENTRY_BLOCK_PTR_FOR_FN (cfun)->count.apply_scale (7, 8); - decrease_profile (EXIT_BLOCK_PTR_FOR_FN (cfun), count); - decrease_profile (ENTRY_BLOCK_PTR_FOR_FN (cfun), count); - if (e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun)) - decrease_profile (e->dest, count); + /* When profile is inconsistent and the recursion edge is more frequent + than number of executions of functions, scale it down, so we do not + end up with 0 executions of entry block. */ + if (count >= ENTRY_BLOCK_PTR_FOR_FN (cfun)->count) + count = ENTRY_BLOCK_PTR_FOR_FN (cfun)->count.apply_scale (7, 8); + decrease_profile (EXIT_BLOCK_PTR_FOR_FN (cfun), count); + decrease_profile (ENTRY_BLOCK_PTR_FOR_FN (cfun), count); + if (e->dest != EXIT_BLOCK_PTR_FOR_FN (cfun)) + decrease_profile (e->dest, count); - /* Replace the call by a jump to the start of function. */ - e = redirect_edge_and_branch (single_succ_edge (gsi_bb (t->call_gsi)), - first); + /* Replace the call by a jump to the start of function. */ + e = redirect_edge_and_branch (single_succ_edge (gsi_bb (t->call_gsi)), + first); + } gcc_assert (e); PENDING_STMT (e) = NULL; @@ -1295,6 +1321,18 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_musttail, find_tail_calls (e->src, &tailcalls, only_musttail, opt_tailcalls, diag_musttail); } + if (cfun->has_musttail && diag_musttail) + { + basic_block bb; + FOR_EACH_BB_FN (bb, cfun) + if (EDGE_COUNT (bb->succs) == 0) + if (gimple *c = last_nondebug_stmt (bb)) + if (is_gimple_call (c) + && gimple_call_must_tail_p (as_a (c)) + && gimple_call_noreturn_p (as_a (c))) + find_tail_calls (bb, &tailcalls, only_musttail, opt_tailcalls, + diag_musttail); + } if (live_vars) {