https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119376
--- Comment #20 from andi at firstfloor dot org --- > Anyway, working now on the local vars and warnings for that. Thanks. I tried it, but so far it doesn't work correctly. I guess I don't fully understand the subtleties of the alias machinery. BTW I also noticed that only clang 20 warns, clang 19 doesn't. diff --git a/gcc/testsuite/c-c++-common/musttail26.c b/gcc/testsuite/c-c++-common/musttail26.c new file mode 100644 index 000000000000..a8774aca9471 --- /dev/null +++ b/gcc/testsuite/c-c++-common/musttail26.c @@ -0,0 +1,22 @@ +/* { dg-do compile { target external_musttail } } */ +/* Allow escaping local to match clang. */ + +extern int foo (void); +extern int foo2 (int *); +extern void bar (int *); + +int +baz (void) +{ + int a = 1; + bar (&a); + [[clang::musttail]] return foo (); /* { dg-warning "Address of local variable.*may be used by musttail call" } */ +} + +int +bur (int *x) +{ + *x = 42; + int a = 1; + [[clang::musttail]] return foo2 (&a); /* dg-warning "Address of local variable.*may be used by musttail call" } */ +} diff --git a/gcc/tree-ssa-alias.cc b/gcc/tree-ssa-alias.cc index e93d5187d509..521c7e8707b1 100644 --- a/gcc/tree-ssa-alias.cc +++ b/gcc/tree-ssa-alias.cc @@ -2833,7 +2833,8 @@ check_fnspec (gcall *call, ao_ref *ref, bool clobber) otherwise return false. */ static bool -ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) +ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p, + ref_type *ref_type) { tree base, callee; unsigned i; @@ -2932,7 +2933,11 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) base = ao_ref_base (ref); if (!base) - return true; + { + if (ref_type) + *ref_type = REF_ESCAPE; + return true; + } /* If the reference is based on a decl that is not aliased the call cannot possibly use it. */ @@ -2968,11 +2973,18 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) goto process_args; } + if (ref_type) + *ref_type = REF_ESCAPE; + /* Check if the base variable is call-used. */ if (DECL_P (base)) { if (pt_solution_includes (gimple_call_use_set (call), base)) - return true; + { + if (ref_type) + *ref_type = REF_ARGUMENT; + return true; + } } else if ((TREE_CODE (base) == MEM_REF || TREE_CODE (base) == TARGET_MEM_REF) @@ -2988,6 +3000,9 @@ ref_maybe_used_by_call_p_1 (gcall *call, ao_ref *ref, bool tbaa_p) else return true; + if (ref_type) + *ref_type = REF_MISC; + /* Inspect call arguments for passed-by-value aliases. */ process_args: for (i = 0; i < gimple_call_num_args (call); ++i) @@ -3007,7 +3022,11 @@ process_args: ao_ref r; ao_ref_init (&r, op); if (refs_may_alias_p_1 (&r, ref, tbaa_p)) - return true; + { + if (ref_type) + *ref_type = REF_ARGUMENT; + return true; + } } } @@ -3015,10 +3034,11 @@ process_args: } static bool -ref_maybe_used_by_call_p (gcall *call, ao_ref *ref, bool tbaa_p) +ref_maybe_used_by_call_p (gcall *call, ao_ref *ref, bool tbaa_p, + ref_type *ref_type) { bool res; - res = ref_maybe_used_by_call_p_1 (call, ref, tbaa_p); + res = ref_maybe_used_by_call_p_1 (call, ref, tbaa_p, ref_type); if (res) ++alias_stats.ref_maybe_used_by_call_p_may_alias; else @@ -3028,11 +3048,15 @@ ref_maybe_used_by_call_p (gcall *call, ao_ref *ref, bool tbaa_p) /* If the statement STMT may use the memory reference REF return - true, otherwise return false. */ + true, otherwise return false. Optionally REF_TYPE describes + the type of reference. */ bool -ref_maybe_used_by_stmt_p (gimple *stmt, ao_ref *ref, bool tbaa_p) +ref_maybe_used_by_stmt_p (gimple *stmt, ao_ref *ref, bool tbaa_p, + ref_type *ref_type) { + if (ref_type) + *ref_type = REF_MISC; if (is_gimple_assign (stmt)) { tree rhs; @@ -3050,7 +3074,8 @@ ref_maybe_used_by_stmt_p (gimple *stmt, ao_ref *ref, bool tbaa_p) return refs_may_alias_p (rhs, ref, tbaa_p); } else if (is_gimple_call (stmt)) - return ref_maybe_used_by_call_p (as_a <gcall *> (stmt), ref, tbaa_p); + return ref_maybe_used_by_call_p (as_a <gcall *> (stmt), ref, tbaa_p, + ref_type); else if (greturn *return_stmt = dyn_cast <greturn *> (stmt)) { tree retval = gimple_return_retval (return_stmt); @@ -3075,11 +3100,12 @@ ref_maybe_used_by_stmt_p (gimple *stmt, ao_ref *ref, bool tbaa_p) } bool -ref_maybe_used_by_stmt_p (gimple *stmt, tree ref, bool tbaa_p) +ref_maybe_used_by_stmt_p (gimple *stmt, tree ref, bool tbaa_p, + ref_type *ref_type) { ao_ref r; ao_ref_init (&r, ref); - return ref_maybe_used_by_stmt_p (stmt, &r, tbaa_p); + return ref_maybe_used_by_stmt_p (stmt, &r, tbaa_p, ref_type); } /* If the call in statement CALL may clobber the memory reference REF @@ -3573,7 +3599,7 @@ stmt_kills_ref_p (gimple *stmt, ao_ref *ref) /* For store to be killed it needs to not be used earlier. */ if (ref_maybe_used_by_call_p_1 (as_a <gcall *> (stmt), ref, - true) + true, NULL) || !dbg_cnt (ipa_mod_ref)) break; if (dump_file && (dump_flags & TDF_DETAILS)) diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h index 6537608df7db..71d0e109dafa 100644 --- a/gcc/tree-ssa-alias.h +++ b/gcc/tree-ssa-alias.h @@ -135,8 +135,18 @@ extern bool refs_may_alias_p (tree, tree, bool = true); extern bool refs_may_alias_p_1 (ao_ref *, ao_ref *, bool); extern bool refs_anti_dependent_p (tree, tree); extern bool refs_output_dependent_p (tree, tree); -extern bool ref_maybe_used_by_stmt_p (gimple *, tree, bool = true); -extern bool ref_maybe_used_by_stmt_p (gimple *, ao_ref *, bool = true); + +enum ref_type +{ + REF_MISC, /* Unclassified reference. */ + REF_ARGUMENT, /* Reference through an argument. */ + REF_ESCAPE, /* Reference due to a earlier escape. */ +}; + +extern bool ref_maybe_used_by_stmt_p (gimple *, tree, bool = true, + ref_type * = NULL); +extern bool ref_maybe_used_by_stmt_p (gimple *, ao_ref *, bool = true, + ref_type * = NULL); extern bool stmt_may_clobber_global_p (gimple *, bool); extern bool stmt_may_clobber_ref_p (gimple *, tree, bool = true); extern bool stmt_may_clobber_ref_p_1 (gimple *, ao_ref *, bool = true); diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc index f97df31eb3cf..abf665e1ac48 100644 --- a/gcc/tree-tailcall.cc +++ b/gcc/tree-tailcall.cc @@ -709,34 +709,43 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, /* Make sure the tail invocation of this function does not indirectly refer to local variables. (Passing variables directly by value - is OK.) */ + is OK.). But allow it for musttail with some warnings. */ FOR_EACH_LOCAL_DECL (cfun, idx, var) { if (TREE_CODE (var) != PARM_DECL && auto_var_in_fn_p (var, cfun->decl) - && may_be_aliased (var) - && (ref_maybe_used_by_stmt_p (call, var, false) - || call_may_clobber_ref_p (call, var, false))) + && may_be_aliased (var)) { - if (!VAR_P (var)) + if (VAR_P (var) + && !bitmap_bit_p (local_live_vars, + *live_vars->get (DECL_UID (var)))) + continue; + ref_type ref; + if (ref_maybe_used_by_stmt_p (call, var, false, &ref)) { - if (local_live_vars) - BITMAP_FREE (local_live_vars); - maybe_error_musttail (call, - _("call invocation refers to locals")); - return; - } - else - { - unsigned int *v = live_vars->get (DECL_UID (var)); - if (bitmap_bit_p (local_live_vars, *v)) + if (gimple_call_must_tail_p (call)) { - BITMAP_FREE (local_live_vars); - maybe_error_musttail (call, - _("call invocation refers to locals")); - return; + if (opt_tailcalls) + { + if (ref == REF_ARGUMENT) + warning_at (call->location, 0, + "Address of local variable %qE passed to %<musttail%> call", + var); + else if (ref == REF_ESCAPE || ref == REF_MISC) + warning_at (call->location, 0, + "Address of local variable %qE may be used by %<musttail%> call", + var); + } + continue; } } + else if (call_may_clobber_ref_p (call, var, false)) + ; /* Return below. ??? should this be special cased for musttail? */ + else + continue; + if (local_live_vars) + BITMAP_FREE (local_live_vars); + return; } }