@@ -430,6 +430,9 @@ struct GTY(()) function {
/* Nonzero when the tail call has been identified. */
unsigned int tail_call_marked : 1;
+ /* Has musttail marked calls. */
+ unsigned int has_musttail : 1;
+
/* Nonzero if the current function contains a #pragma GCC unroll. */
unsigned int has_unroll : 1;
@@ -1325,6 +1325,7 @@ input_struct_function_base (struct function *fn, class data_in *data_in,
fn->calls_eh_return = bp_unpack_value (&bp, 1);
fn->has_force_vectorize_loops = bp_unpack_value (&bp, 1);
fn->has_simduid_loops = bp_unpack_value (&bp, 1);
+ fn->has_musttail = bp_unpack_value (&bp, 1);
fn->assume_function = bp_unpack_value (&bp, 1);
fn->va_list_fpr_size = bp_unpack_value (&bp, 8);
fn->va_list_gpr_size = bp_unpack_value (&bp, 8);
@@ -2290,6 +2290,7 @@ output_struct_function_base (struct output_block *ob, struct function *fn)
bp_pack_value (&bp, fn->calls_eh_return, 1);
bp_pack_value (&bp, fn->has_force_vectorize_loops, 1);
bp_pack_value (&bp, fn->has_simduid_loops, 1);
+ bp_pack_value (&bp, fn->has_musttail, 1);
bp_pack_value (&bp, fn->assume_function, 1);
bp_pack_value (&bp, fn->va_list_fpr_size, 8);
bp_pack_value (&bp, fn->va_list_gpr_size, 8);
@@ -444,6 +444,7 @@ along with GCC; see the file COPYING3. If not see
NEXT_PASS (pass_tsan_O0);
NEXT_PASS (pass_sanopt);
NEXT_PASS (pass_cleanup_eh);
+ NEXT_PASS (pass_musttail);
NEXT_PASS (pass_lower_resx);
NEXT_PASS (pass_nrv);
NEXT_PASS (pass_gimple_isel);
@@ -2290,6 +2290,8 @@ notice_special_calls (gcall *call)
cfun->calls_alloca = true;
if (flags & ECF_RETURNS_TWICE)
cfun->calls_setjmp = true;
+ if (gimple_call_must_tail_p (call))
+ cfun->has_musttail = true;
}
@@ -2301,6 +2303,7 @@ clear_special_calls (void)
{
cfun->calls_alloca = false;
cfun->calls_setjmp = false;
+ cfun->has_musttail = false;
}
/* Remove PHI nodes associated with basic block BB and all edges out of BB. */
@@ -368,6 +368,7 @@ extern gimple_opt_pass *make_pass_sra (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_sra_early (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_tail_recursion (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_tail_calls (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_musttail (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_fix_loops (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_tree_loop (gcc::context *ctxt);
extern gimple_opt_pass *make_pass_tree_no_loop (gcc::context *ctxt);
@@ -408,10 +408,10 @@ 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. */
+ added to the start of RET. When ONLY_MUSTTAIL is set only handle musttail. */
static void
-find_tail_calls (basic_block bb, struct tailcall **ret)
+find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail)
{
tree ass_var = NULL_TREE, ret_var, func, param;
gimple *stmt;
@@ -445,6 +445,9 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
if (is_gimple_call (stmt))
{
call = as_a <gcall *> (stmt);
+ /* Handle only musttail calls when not optimizing. */
+ if (only_musttail && !gimple_call_must_tail_p (call))
+ return;
ass_var = gimple_call_lhs (call);
break;
}
@@ -467,7 +470,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
edge_iterator ei;
/* Recurse to the predecessors. */
FOR_EACH_EDGE (e, ei, bb->preds)
- find_tail_calls (e->src, ret);
+ find_tail_calls (e->src, ret, only_musttail);
return;
}
@@ -528,7 +531,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
func = gimple_call_fndecl (call);
if (func
&& !fndecl_built_in_p (func)
- && recursive_call_p (current_function_decl, func))
+ && recursive_call_p (current_function_decl, func)
+ && !only_musttail)
{
tree arg;
@@ -1094,10 +1098,11 @@ create_tailcall_accumulator (const char *label, basic_block bb, tree init)
}
/* Optimizes tail calls in the function, turning the tail recursion
- into iteration. */
+ into iteration. When ONLY_MUSTCALL is true only optimize mustcall
+ marked calls. */
static unsigned int
-tree_optimize_tail_calls_1 (bool opt_tailcalls)
+tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_mustcall)
{
edge e;
bool phis_constructed = false;
@@ -1117,7 +1122,7 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls)
/* 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);
+ find_tail_calls (e->src, &tailcalls, only_mustcall);
}
if (live_vars)
@@ -1228,7 +1233,7 @@ gate_tail_calls (void)
static unsigned int
execute_tail_calls (void)
{
- return tree_optimize_tail_calls_1 (true);
+ return tree_optimize_tail_calls_1 (true, false);
}
namespace {
@@ -1261,7 +1266,7 @@ public:
bool gate (function *) final override { return gate_tail_calls (); }
unsigned int execute (function *) final override
{
- return tree_optimize_tail_calls_1 (false);
+ return tree_optimize_tail_calls_1 (false, false);
}
}; // class pass_tail_recursion
@@ -1312,3 +1317,48 @@ make_pass_tail_calls (gcc::context *ctxt)
{
return new pass_tail_calls (ctxt);
}
+
+namespace {
+
+const pass_data pass_data_musttail =
+{
+ GIMPLE_PASS, /* type */
+ "musttail", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ ( PROP_cfg | PROP_ssa ), /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0, /* todo_flags_finish */
+};
+
+class pass_musttail : public gimple_opt_pass
+{
+public:
+ pass_musttail (gcc::context *ctxt)
+ : gimple_opt_pass (pass_data_musttail, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ /* This pass is only used when the other tail call pass
+ 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;
+ }
+ unsigned int execute (function *) final override
+ {
+ return tree_optimize_tail_calls_1 (true, true);
+ }
+
+}; // class pass_musttail
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_musttail (gcc::context *ctxt)
+{
+ return new pass_musttail (ctxt);
+}