On Sat, Jun 22, 2024 at 9:00 PM Andi Kleen <a...@linux.intel.com> wrote:
>
> Move the error reporting for caller attributes to be
> after the tail call discovery, so that we can give proper
> error messages tagged to the calls.

Hmm.  This all gets a bit awkward.  I realize that early checking
gets us less compile-time unnecessarily spent for searching for
a tail call - but at least for the musttail case parsing constraints
should put a practical limit on how far to look?

So what I wonder is whether it would be better to separate
searching for a (musttail) candidate separate from validation?

We could for example invoke find_tail_calls twice, once to
find a musttail candidate (can there be multiple ones?) and once
to validate and error?  Would that make the delaying less awkward?

> gcc/ChangeLog:
>
>         * tree-tailcall.cc (maybe_error_musttail): Declare.
>         (suitable_for_tail_opt_p): Take call and report errors.
>         (suitable_for_tail_call_opt_p): Take call and report errors.
>         (find_tail_calls): Report caller errors after discovery.
>         (tree_optimize_tail_calls_1): Remove caller suitableness check.
> ---
>  gcc/tree-tailcall.cc | 62 ++++++++++++++++++++++++++++++--------------
>  1 file changed, 43 insertions(+), 19 deletions(-)
>
> diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc
> index 4687e20e61d0..a77fa1511415 100644
> --- a/gcc/tree-tailcall.cc
> +++ b/gcc/tree-tailcall.cc
> @@ -133,14 +133,20 @@ static tree m_acc, a_acc;
>
>  static bitmap tailr_arg_needs_copy;
>
> +static void maybe_error_musttail (gcall *call, const char *err);
> +
>  /* Returns false when the function is not suitable for tail call optimization
> -   from some reason (e.g. if it takes variable number of arguments).  */
> +   from some reason (e.g. if it takes variable number of arguments). CALL
> +   is call to report for.  */
>
>  static bool
> -suitable_for_tail_opt_p (void)
> +suitable_for_tail_opt_p (gcall *call)
>  {
>    if (cfun->stdarg)
> -    return false;
> +    {
> +      maybe_error_musttail (call, _("caller uses stdargs"));
> +      return false;
> +    }
>
>    return true;
>  }
> @@ -148,35 +154,47 @@ suitable_for_tail_opt_p (void)
>  /* Returns false when the function is not suitable for tail call optimization
>     for some reason (e.g. if it takes variable number of arguments).
>     This test must pass in addition to suitable_for_tail_opt_p in order to 
> make
> -   tail call discovery happen.  */
> +   tail call discovery happen. CALL is call to report error for.  */
>
>  static bool
> -suitable_for_tail_call_opt_p (void)
> +suitable_for_tail_call_opt_p (gcall *call)
>  {
>    tree param;
>
>    /* alloca (until we have stack slot life analysis) inhibits
>       sibling call optimizations, but not tail recursion.  */
>    if (cfun->calls_alloca)
> -    return false;
> +    {
> +      maybe_error_musttail (call, _("caller uses alloca"));
> +      return false;
> +    }
>
>    /* If we are using sjlj exceptions, we may need to add a call to
>       _Unwind_SjLj_Unregister at exit of the function.  Which means
>       that we cannot do any sibcall transformations.  */
>    if (targetm_common.except_unwind_info (&global_options) == UI_SJLJ
>        && current_function_has_exception_handlers ())
> -    return false;
> +    {
> +      maybe_error_musttail (call, _("caller uses sjlj exceptions"));
> +      return false;
> +    }
>
>    /* Any function that calls setjmp might have longjmp called from
>       any called function.  ??? We really should represent this
>       properly in the CFG so that this needn't be special cased.  */
>    if (cfun->calls_setjmp)
> -    return false;
> +    {
> +      maybe_error_musttail (call, _("caller uses setjmp"));
> +      return false;
> +    }
>
>    /* Various targets don't handle tail calls correctly in functions
>       that call __builtin_eh_return.  */
>    if (cfun->calls_eh_return)
> -    return false;
> +    {
> +      maybe_error_musttail (call, _("caller uses __builtin_eh_return"));
> +      return false;
> +    }
>
>    /* ??? It is OK if the argument of a function is taken in some cases,
>       but not in all cases.  See PR15387 and PR19616.  Revisit for 4.1.  */
> @@ -184,7 +202,10 @@ suitable_for_tail_call_opt_p (void)
>         param;
>         param = DECL_CHAIN (param))
>      if (TREE_ADDRESSABLE (param))
> -      return false;
> +      {
> +       maybe_error_musttail (call, _("address of caller arguments taken"));
> +       return false;
> +      }
>
>    return true;
>  }
> @@ -445,10 +466,12 @@ 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. When ONLY_MUSTTAIL is set only handle 
> musttail.  */
> +   added to the start of RET. When ONLY_MUSTTAIL is set only handle musttail.
> +   Update OPT_TAILCALLS as output parameter.  */
>
>  static void
> -find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail)
> +find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail,
> +                bool &opt_tailcalls)
>  {
>    tree ass_var = NULL_TREE, ret_var, func, param;
>    gimple *stmt;
> @@ -526,11 +549,17 @@ find_tail_calls (basic_block bb, struct tailcall **ret, 
> bool only_musttail)
>        edge_iterator ei;
>        /* Recurse to the predecessors.  */
>        FOR_EACH_EDGE (e, ei, bb->preds)
> -       find_tail_calls (e->src, ret, only_musttail);
> +       find_tail_calls (e->src, ret, only_musttail, opt_tailcalls);
>
>        return;
>      }
>
> +  if (!suitable_for_tail_opt_p (call))
> +    return;
> +
> +  if (!suitable_for_tail_call_opt_p (call))
> +    opt_tailcalls = false;
> +
>    /* If the LHS of our call is not just a simple register or local
>       variable, we can't transform this into a tail or sibling call.
>       This situation happens, in (e.g.) "*p = foo()" where foo returns a
> @@ -1200,17 +1229,12 @@ tree_optimize_tail_calls_1 (bool opt_tailcalls, bool 
> only_mustcall)
>    tree param;
>    edge_iterator ei;
>
> -  if (!suitable_for_tail_opt_p ())
> -    return 0;
> -  if (opt_tailcalls)
> -    opt_tailcalls = suitable_for_tail_call_opt_p ();
> -
>    FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
>      {
>        /* 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_mustcall);
> +       find_tail_calls (e->src, &tailcalls, only_mustcall, opt_tailcalls);
>      }
>
>    if (live_vars)
> --
> 2.45.2
>

Reply via email to