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;
}
}