aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-tailcall.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-tailcall.cc')
-rw-r--r--gcc/tree-tailcall.cc179
1 files changed, 118 insertions, 61 deletions
diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc
index f97df31..8ba6752 100644
--- a/gcc/tree-tailcall.cc
+++ b/gcc/tree-tailcall.cc
@@ -139,18 +139,18 @@ static tree m_acc, a_acc;
static bitmap tailr_arg_needs_copy;
-static void maybe_error_musttail (gcall *call, const char *err);
+static void maybe_error_musttail (gcall *call, const char *err, bool);
/* Returns false when the function is not suitable for tail call optimization
from some reason (e.g. if it takes variable number of arguments). CALL
is call to report for. */
static bool
-suitable_for_tail_opt_p (gcall *call)
+suitable_for_tail_opt_p (gcall *call, bool diag_musttail)
{
if (cfun->stdarg)
{
- maybe_error_musttail (call, _("caller uses stdargs"));
+ maybe_error_musttail (call, _("caller uses stdargs"), diag_musttail);
return false;
}
@@ -163,7 +163,7 @@ suitable_for_tail_opt_p (gcall *call)
tail call discovery happen. CALL is call to report error for. */
static bool
-suitable_for_tail_call_opt_p (gcall *call)
+suitable_for_tail_call_opt_p (gcall *call, bool diag_musttail)
{
tree param;
@@ -171,7 +171,7 @@ suitable_for_tail_call_opt_p (gcall *call)
sibling call optimizations, but not tail recursion. */
if (cfun->calls_alloca)
{
- maybe_error_musttail (call, _("caller uses alloca"));
+ maybe_error_musttail (call, _("caller uses alloca"), diag_musttail);
return false;
}
@@ -181,7 +181,8 @@ suitable_for_tail_call_opt_p (gcall *call)
if (targetm_common.except_unwind_info (&global_options) == UI_SJLJ
&& current_function_has_exception_handlers ())
{
- maybe_error_musttail (call, _("caller uses sjlj exceptions"));
+ maybe_error_musttail (call, _("caller uses sjlj exceptions"),
+ diag_musttail);
return false;
}
@@ -190,7 +191,7 @@ suitable_for_tail_call_opt_p (gcall *call)
properly in the CFG so that this needn't be special cased. */
if (cfun->calls_setjmp)
{
- maybe_error_musttail (call, _("caller uses setjmp"));
+ maybe_error_musttail (call, _("caller uses setjmp"), diag_musttail);
return false;
}
@@ -198,7 +199,8 @@ suitable_for_tail_call_opt_p (gcall *call)
that call __builtin_eh_return. */
if (cfun->calls_eh_return)
{
- maybe_error_musttail (call, _("caller uses __builtin_eh_return"));
+ maybe_error_musttail (call, _("caller uses __builtin_eh_return"),
+ diag_musttail);
return false;
}
@@ -209,7 +211,8 @@ suitable_for_tail_call_opt_p (gcall *call)
param = DECL_CHAIN (param))
if (TREE_ADDRESSABLE (param))
{
- maybe_error_musttail (call, _("address of caller arguments taken"));
+ maybe_error_musttail (call, _("address of caller arguments taken"),
+ diag_musttail);
return false;
}
@@ -436,9 +439,9 @@ propagate_through_phis (tree var, edge e)
errors. */
static void
-maybe_error_musttail (gcall *call, const char *err)
+maybe_error_musttail (gcall *call, const char *err, bool diag_musttail)
{
- if (gimple_call_must_tail_p (call))
+ if (gimple_call_must_tail_p (call) && diag_musttail)
{
error_at (call->location, "cannot tail-call: %s", err);
/* Avoid another error. ??? If there are multiple reasons why tail
@@ -461,12 +464,13 @@ static live_vars_map *live_vars;
static vec<bitmap_head> live_vars_vec;
/* Finds tailcalls falling into basic block BB. The list of found tailcalls is
- added to the start of RET. When ONLY_MUSTTAIL is set only handle musttail.
- Update OPT_TAILCALLS as output parameter. */
+ added to the start of RET. When ONLY_MUSTTAIL is set only handle musttail.
+ Update OPT_TAILCALLS as output parameter. If DIAG_MUSTTAIL, diagnose
+ failures for musttail calls. */
static void
find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
- bool &opt_tailcalls)
+ bool &opt_tailcalls, bool diag_musttail)
{
tree ass_var = NULL_TREE, ret_var, func, param;
gimple *stmt;
@@ -480,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
@@ -524,7 +529,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
{
maybe_error_musttail (call,
_("memory reference or volatile after "
- "call"));
+ "call"), diag_musttail);
return;
}
ass_var = gimple_call_lhs (call);
@@ -561,15 +566,16 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
edge_iterator ei;
/* Recurse to the predecessors. */
FOR_EACH_EDGE (e, ei, bb->preds)
- find_tail_calls (e->src, ret, only_musttail, opt_tailcalls);
+ find_tail_calls (e->src, ret, only_musttail, opt_tailcalls,
+ diag_musttail);
return;
}
- if (!suitable_for_tail_opt_p (call))
+ if (!suitable_for_tail_opt_p (call, diag_musttail))
return;
- if (!suitable_for_tail_call_opt_p (call))
+ if (!suitable_for_tail_call_opt_p (call, diag_musttail))
opt_tailcalls = false;
/* If the LHS of our call is not just a simple register or local
@@ -587,27 +593,28 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
&& !is_gimple_reg (ass_var)
&& !auto_var_in_fn_p (ass_var, cfun->decl))
{
- maybe_error_musttail (call, _("return value in memory"));
+ maybe_error_musttail (call, _("return value in memory"), diag_musttail);
return;
}
if (cfun->calls_setjmp)
{
- maybe_error_musttail (call, _("caller uses setjmp"));
+ maybe_error_musttail (call, _("caller uses setjmp"), diag_musttail);
return;
}
/* 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,
_("call may throw exception that does not "
- "propagate"));
+ "propagate"), diag_musttail);
else
- maybe_error_musttail (call, _("code between call and return"));
+ maybe_error_musttail (call, _("code between call and return"),
+ diag_musttail);
return;
}
@@ -639,7 +646,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
&& may_be_aliased (result_decl)
&& ref_maybe_used_by_stmt_p (call, result_decl, false))
{
- maybe_error_musttail (call, _("return value used after call"));
+ maybe_error_musttail (call, _("return value used after call"),
+ diag_musttail);
return;
}
@@ -723,7 +731,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
if (local_live_vars)
BITMAP_FREE (local_live_vars);
maybe_error_musttail (call,
- _("call invocation refers to locals"));
+ _("call invocation refers to locals"),
+ diag_musttail);
return;
}
else
@@ -733,7 +742,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
{
BITMAP_FREE (local_live_vars);
maybe_error_musttail (call,
- _("call invocation refers to locals"));
+ _("call invocation refers to locals"),
+ diag_musttail);
return;
}
}
@@ -751,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<gimple *> 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;
@@ -780,7 +792,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
if (gimple_code (stmt) != GIMPLE_ASSIGN)
{
- maybe_error_musttail (call, _("unhandled code after call"));
+ maybe_error_musttail (call, _("unhandled code after call"),
+ diag_musttail);
return;
}
@@ -789,7 +802,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
&tmp_m, &tmp_a, &ass_var, to_move_defs);
if (ret == FAIL || (ret == TRY_MOVE && !tail_recursion))
{
- maybe_error_musttail (call, _("return value changed after call"));
+ maybe_error_musttail (call, _("return value changed after call"),
+ diag_musttail);
return;
}
else if (ret == TRY_MOVE)
@@ -833,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 <greturn *> (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 <greturn *> (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
@@ -864,7 +893,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
if (!ok)
{
maybe_error_musttail (call,
- _("call and return value are different"));
+ _("call and return value are different"),
+ diag_musttail);
return;
}
}
@@ -874,7 +904,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
if (!tail_recursion && (m || a))
{
maybe_error_musttail (call,
- _("operations after non tail recursive call"));
+ _("operations after non tail recursive call"),
+ diag_musttail);
return;
}
@@ -883,7 +914,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
{
maybe_error_musttail (call,
_("tail recursion with pointers can only use "
- "additions"));
+ "additions"), diag_musttail);
return;
}
@@ -1140,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));
-
- 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);
-
- /* 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);
+ if (gimple_call_noreturn_p (as_a <gcall *> (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 ();
+
+ /* 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);
+ }
gcc_assert (e);
PENDING_STMT (e) = NULL;
@@ -1258,11 +1297,13 @@ create_tailcall_accumulator (const char *label, basic_block bb, tree init)
}
/* Optimizes tail calls in the function, turning the tail recursion
- into iteration. When ONLY_MUSTCALL is true only optimize mustcall
- marked calls. */
+ into iteration. When ONLY_MUSTTAIL is true only optimize musttail
+ marked calls. When DIAG_MUSTTAIL, diagnose if musttail calls can't
+ be tail call optimized. */
static unsigned int
-tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_mustcall)
+tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_musttail,
+ bool diag_musttail)
{
edge e;
bool phis_constructed = false;
@@ -1277,7 +1318,20 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_mustcall)
/* Only traverse the normal exits, i.e. those that end with return
statement. */
if (safe_is_a <greturn *> (*gsi_last_bb (e->src)))
- find_tail_calls (e->src, &tailcalls, only_mustcall, opt_tailcalls);
+ 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 <gcall *> (c))
+ && gimple_call_noreturn_p (as_a <gcall *> (c)))
+ find_tail_calls (bb, &tailcalls, only_musttail, opt_tailcalls,
+ diag_musttail);
}
if (live_vars)
@@ -1374,6 +1428,9 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_mustcall)
if (tailr_arg_needs_copy)
BITMAP_FREE (tailr_arg_needs_copy);
+ if (diag_musttail)
+ cfun->has_musttail = false;
+
if (changed)
return TODO_cleanup_cfg | TODO_update_ssa_only_virtuals;
return 0;
@@ -1388,7 +1445,7 @@ gate_tail_calls (void)
static unsigned int
execute_tail_calls (void)
{
- return tree_optimize_tail_calls_1 (true, false);
+ return tree_optimize_tail_calls_1 (true, false, true);
}
namespace {
@@ -1421,7 +1478,7 @@ public:
bool gate (function *) final override { return gate_tail_calls (); }
unsigned int execute (function *) final override
{
- return tree_optimize_tail_calls_1 (false, false);
+ return tree_optimize_tail_calls_1 (false, false, false);
}
}; // class pass_tail_recursion
@@ -1497,15 +1554,15 @@ public:
/* opt_pass methods: */
/* This pass is only used when the other tail call pass
- doesn't run to make [[musttail]] still work. But only
+ doesn't run to make [[musttail]] still work. But only
run it when there is actually a musttail in the function. */
bool gate (function *f) final override
{
- return !flag_optimize_sibling_calls && f->has_musttail;
+ return f->has_musttail;
}
unsigned int execute (function *) final override
{
- return tree_optimize_tail_calls_1 (true, true);
+ return tree_optimize_tail_calls_1 (true, true, true);
}
}; // class pass_musttail