diff options
Diffstat (limited to 'gcc/tree-tailcall.cc')
-rw-r--r-- | gcc/tree-tailcall.cc | 157 |
1 files changed, 116 insertions, 41 deletions
diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc index f51bb97..f593363 100644 --- a/gcc/tree-tailcall.cc +++ b/gcc/tree-tailcall.cc @@ -51,6 +51,8 @@ along with GCC; see the file COPYING3. If not see #include "symbol-summary.h" #include "ipa-cp.h" #include "ipa-prop.h" +#include "attribs.h" +#include "asan.h" /* The file implements the tail recursion elimination. It is also used to analyze the tail calls in general, passing the results to the rtl level @@ -122,6 +124,9 @@ struct tailcall /* True if it is a call to the current function. */ bool tail_recursion; + /* True if there is __tsan_func_exit call after the call. */ + bool has_tsan_func_exit; + /* The return value of the caller is mult * f + add, where f is the return value of the call. */ tree mult, add; @@ -492,10 +497,10 @@ maybe_error_musttail (gcall *call, const char *err, bool diag_musttail) gimple_call_set_must_tail (call, false); /* Avoid another error. */ gimple_call_set_tail (call, false); } - if (dump_file) + if (dump_file && (dump_flags & TDF_DETAILS)) { + fprintf (dump_file, "Cannot tail-call: %s: ", err); print_gimple_stmt (dump_file, call, 0, TDF_SLIM); - fprintf (dump_file, "Cannot convert: %s\n", err); } } @@ -504,7 +509,7 @@ maybe_error_musttail (gcall *call, const char *err, bool diag_musttail) Search at most CNT basic blocks (so that we don't need to do trivial loop discovery). */ static bool -empty_eh_cleanup (basic_block bb, int cnt) +empty_eh_cleanup (basic_block bb, int *eh_has_tsan_func_exit, int cnt) { if (EDGE_COUNT (bb->succs) > 1) return false; @@ -515,6 +520,14 @@ empty_eh_cleanup (basic_block bb, int cnt) gimple *g = gsi_stmt (gsi); if (is_gimple_debug (g) || gimple_clobber_p (g)) continue; + if (eh_has_tsan_func_exit + && !*eh_has_tsan_func_exit + && sanitize_flags_p (SANITIZE_THREAD) + && gimple_call_builtin_p (g, BUILT_IN_TSAN_FUNC_EXIT)) + { + *eh_has_tsan_func_exit = 1; + continue; + } if (is_gimple_resx (g) && stmt_can_throw_external (cfun, g)) return true; return false; @@ -523,7 +536,7 @@ empty_eh_cleanup (basic_block bb, int cnt) return false; if (cnt == 1) return false; - return empty_eh_cleanup (single_succ (bb), cnt - 1); + return empty_eh_cleanup (single_succ (bb), eh_has_tsan_func_exit, cnt - 1); } /* Argument for compute_live_vars/live_vars_at_stmt and what compute_live_vars @@ -531,14 +544,22 @@ empty_eh_cleanup (basic_block bb, int cnt) 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 +/* 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. If DIAG_MUSTTAIL, diagnose - failures for musttail calls. */ + failures for musttail calls. RETRY_TSAN_FUNC_EXIT is initially 0 and + in that case the last call is attempted to be tail called, including + __tsan_func_exit with -fsanitize=thread. It is set to -1 if we + detect __tsan_func_exit call and in that case tree_optimize_tail_calls_1 + will retry with it set to 1 (regardless of whether turning the + __tsan_func_exit was successfully detected as tail call or not) and that + will allow turning musttail calls before that call into tail calls as well + by adding __tsan_func_exit call before the call. */ static void find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, - bool &opt_tailcalls, bool diag_musttail) + bool &opt_tailcalls, bool diag_musttail, + int &retry_tsan_func_exit) { tree ass_var = NULL_TREE, ret_var, func, param; gimple *stmt; @@ -552,6 +573,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, size_t idx; tree var; bool only_tailr = false; + bool has_tsan_func_exit = false; + int eh_has_tsan_func_exit = -1; if (!single_succ_p (bb) && (EDGE_COUNT (bb->succs) || !cfun->has_musttail || !diag_musttail)) @@ -585,6 +608,17 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, || is_gimple_debug (stmt)) continue; + if (cfun->has_musttail + && sanitize_flags_p (SANITIZE_THREAD) + && gimple_call_builtin_p (stmt, BUILT_IN_TSAN_FUNC_EXIT) + && diag_musttail) + { + if (retry_tsan_func_exit == 0) + retry_tsan_func_exit = -1; + else if (retry_tsan_func_exit == 1) + continue; + } + if (!last_stmt) last_stmt = stmt; /* Check for a call. */ @@ -596,9 +630,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, return; if (bad_stmt) { - maybe_error_musttail (call, - _("memory reference or volatile after " - "call"), diag_musttail); + maybe_error_musttail (call, _("memory reference or volatile " + "after call"), diag_musttail); return; } ass_var = gimple_call_lhs (call); @@ -636,7 +669,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, /* Recurse to the predecessors. */ FOR_EACH_EDGE (e, ei, bb->preds) find_tail_calls (e->src, ret, only_musttail, opt_tailcalls, - diag_musttail); + diag_musttail, retry_tsan_func_exit); return; } @@ -711,19 +744,22 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, if (!e) { - maybe_error_musttail (call, - _("call may throw exception that does not " - "propagate"), diag_musttail); + maybe_error_musttail (call, _("call may throw exception that does not " + "propagate"), diag_musttail); return; } + if (diag_musttail && gimple_call_must_tail_p (call)) + eh_has_tsan_func_exit = 0; if (!gimple_call_must_tail_p (call) - || !empty_eh_cleanup (e->dest, 20) + || !empty_eh_cleanup (e->dest, + eh_has_tsan_func_exit + ? NULL : &eh_has_tsan_func_exit, 20) || EDGE_COUNT (bb->succs) > 2) { - maybe_error_musttail (call, - _("call may throw exception caught locally " - "or perform cleanups"), diag_musttail); + maybe_error_musttail (call, _("call may throw exception caught " + "locally or perform cleanups"), + diag_musttail); return; } } @@ -854,9 +890,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"), - diag_musttail); + maybe_error_musttail (call, _("call invocation refers to " + "locals"), diag_musttail); return; } else @@ -883,9 +918,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, continue; } BITMAP_FREE (local_live_vars); - maybe_error_musttail (call, - _("call invocation refers to locals"), - diag_musttail); + maybe_error_musttail (call, _("call invocation refers to " + "locals"), diag_musttail); return; } } @@ -951,6 +985,17 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, || is_gimple_debug (stmt)) continue; + if (cfun->has_musttail + && sanitize_flags_p (SANITIZE_THREAD) + && retry_tsan_func_exit == 1 + && gimple_call_builtin_p (stmt, BUILT_IN_TSAN_FUNC_EXIT) + && !has_tsan_func_exit + && gimple_call_must_tail_p (call)) + { + has_tsan_func_exit = true; + continue; + } + if (gimple_code (stmt) != GIMPLE_ASSIGN) { maybe_error_musttail (call, _("unhandled code after call"), @@ -1020,8 +1065,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, if (!VOID_TYPE_P (rettype) && !useless_type_conversion_p (rettype, calltype)) { - maybe_error_musttail (call, - _("call and return value are different"), + maybe_error_musttail (call, _("call and return value are different"), diag_musttail); return; } @@ -1092,8 +1136,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, } if (!ok) { - maybe_error_musttail (call, - _("call and return value are different"), + maybe_error_musttail (call, _("call and return value are different"), diag_musttail); return; } @@ -1103,18 +1146,29 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, multiplicands. */ if (!tail_recursion && (m || a)) { - maybe_error_musttail (call, - _("operations after non tail recursive call"), - diag_musttail); + maybe_error_musttail (call, _("operations after non tail recursive " + "call"), diag_musttail); return; } /* For pointers only allow additions. */ if (m && POINTER_TYPE_P (TREE_TYPE (DECL_RESULT (current_function_decl)))) { - maybe_error_musttail (call, - _("tail recursion with pointers can only use " - "additions"), diag_musttail); + maybe_error_musttail (call, _("tail recursion with pointers can only " + "use additions"), diag_musttail); + return; + } + + if (eh_has_tsan_func_exit != -1 + && eh_has_tsan_func_exit != has_tsan_func_exit) + { + if (eh_has_tsan_func_exit) + maybe_error_musttail (call, _("call may throw exception caught " + "locally or perform cleanups"), + diag_musttail); + else + maybe_error_musttail (call, _("exception cleanups omit " + "__tsan_func_exit call"), diag_musttail); return; } @@ -1146,6 +1200,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, nw->call_gsi = gsi; nw->tail_recursion = tail_recursion; + nw->has_tsan_func_exit = has_tsan_func_exit; nw->mult = m; nw->add = a; @@ -1480,6 +1535,14 @@ static bool optimize_tail_call (struct tailcall *t, bool opt_tailcalls, class loop *&new_loop) { + if (t->has_tsan_func_exit && (t->tail_recursion || opt_tailcalls)) + { + tree builtin_decl = builtin_decl_implicit (BUILT_IN_TSAN_FUNC_EXIT); + gimple *g = gimple_build_call (builtin_decl, 0); + gimple_set_location (g, cfun->function_end_locus); + gsi_insert_before (&t->call_gsi, g, GSI_SAME_STMT); + } + if (t->tail_recursion) { eliminate_tail_call (t, new_loop); @@ -1498,6 +1561,7 @@ optimize_tail_call (struct tailcall *t, bool opt_tailcalls, print_gimple_stmt (dump_file, stmt, 0, dump_flags); fprintf (dump_file, " in bb %i\n", (gsi_bb (t->call_gsi))->index); } + return t->has_tsan_func_exit; } return false; @@ -1547,12 +1611,23 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_musttail, /* 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_musttail, opt_tailcalls, - diag_musttail); + { + int retry_tsan_func_exit = 0; + find_tail_calls (e->src, &tailcalls, only_musttail, opt_tailcalls, + diag_musttail, retry_tsan_func_exit); + if (retry_tsan_func_exit == -1) + { + retry_tsan_func_exit = 1; + find_tail_calls (e->src, &tailcalls, only_musttail, + opt_tailcalls, diag_musttail, + retry_tsan_func_exit); + } + } } if (cfun->has_musttail && diag_musttail) { basic_block bb; + int retry_tsan_func_exit = 0; FOR_EACH_BB_FN (bb, cfun) if (EDGE_COUNT (bb->succs) == 0 || (single_succ_p (bb) @@ -1562,7 +1637,7 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_musttail, && 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); + diag_musttail, retry_tsan_func_exit); } if (live_vars) @@ -1594,10 +1669,10 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls, bool only_musttail, struct tailcall *a = *p; *p = (*p)->next; gcall *call = as_a <gcall *> (gsi_stmt (a->call_gsi)); - maybe_error_musttail (call, - _("tail recursion with accumulation " - "mixed with musttail " - "non-recursive call"), diag_musttail); + maybe_error_musttail (call, _("tail recursion with " + "accumulation mixed with " + "musttail non-recursive call"), + diag_musttail); free (a); } else |