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

Reply via email to