On Mon, Nov 21, 2016 at 11:34 AM, Richard Sandiford
<richard.sandif...@arm.com> wrote:
> We treated this g as a sibling call to f:
>
>   int f (int);
>   int g (void) { return f (1); }
>
> but not this one:
>
>   struct s { int i; };
>   struct s f (int);
>   struct s g (void) { return f (1); }
>
> We treated them both as sibcalls on x86 before the first patch for PR36326,
> so I suppose this is a regression of sorts from 4.3.
>
> The patch allows function returns to be local aggregate variables as well
> as gimple registers.
>
> Tested on aarch64-linux-gnu and x86_64-linux-gnu.  OK to install?

Ok.

Richard.

> Thanks,
> Richard
>
>
> gcc/
>         * tree-tailcall.c (process_assignment): Simplify the check for
>         a valid copy, allowing the source to be a local variable as
>         well as an SSA name.
>         (find_tail_calls): Allow copies between local variables to follow
>         the call.  Allow the result to be stored in any local variable,
>         even if it's an aggregate.
>         (eliminate_tail_call): Check whether the result is an SSA name
>         before updating its SSA_NAME_DEF_STMT.
>
> gcc/testsuite/
>         * gcc.dg/tree-ssa/tailcall-7.c: New test.
>
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/tailcall-7.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-7.c
> new file mode 100644
> index 0000000..eabf1a8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-7.c
> @@ -0,0 +1,89 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-tailc-details" } */
> +
> +struct s { int x; };
> +struct s f (int);
> +struct s global;
> +void callit (void (*) (void));
> +
> +/* Tail call.  */
> +void
> +g1 (void)
> +{
> +  f (1);
> +}
> +
> +/* Not a tail call.  */
> +void
> +g2 (void)
> +{
> +  global = f (2);
> +}
> +
> +/* Not a tail call.  */
> +void
> +g3 (struct s *ptr)
> +{
> +  *ptr = f (3);
> +}
> +
> +/* Tail call.  */
> +struct s
> +g4 (struct s param)
> +{
> +  param = f (4);
> +  return param;
> +}
> +
> +/* Tail call.  */
> +struct s
> +g5 (void)
> +{
> +  struct s local = f (5);
> +  return local;
> +}
> +
> +/* Tail call.  */
> +struct s
> +g6 (void)
> +{
> +  return f (6);
> +}
> +
> +/* Not a tail call.  */
> +struct s
> +g7 (void)
> +{
> +  struct s local = f (7);
> +  global = local;
> +  return local;
> +}
> +
> +/* Not a tail call.  */
> +struct s
> +g8 (struct s *ptr)
> +{
> +  struct s local = f (8);
> +  *ptr = local;
> +  return local;
> +}
> +
> +/* Not a tail call.  */
> +int
> +g9 (struct s param)
> +{
> +  void inner (void) { param = f (9); }
> +  callit (inner);
> +  return 9;
> +}
> +
> +/* Tail call.  */
> +int
> +g10 (int param)
> +{
> +  void inner (void) { f (param); }
> +  callit (inner);
> +  return 10;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "Found tail call" 5 "tailc" } } */
> diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c
> index 0436f0f..f97541d 100644
> --- a/gcc/tree-tailcall.c
> +++ b/gcc/tree-tailcall.c
> @@ -269,7 +269,7 @@ process_assignment (gassign *stmt, gimple_stmt_iterator 
> call, tree *m,
>       conversions that can never produce extra code between the function
>       call and the function return.  */
>    if ((rhs_class == GIMPLE_SINGLE_RHS || gimple_assign_cast_p (stmt))
> -      && (TREE_CODE (src_var) == SSA_NAME))
> +      && src_var == *ass_var)
>      {
>        /* Reject a tailcall if the type conversion might need
>          additional code.  */
> @@ -287,9 +287,6 @@ process_assignment (gassign *stmt, gimple_stmt_iterator 
> call, tree *m,
>             return false;
>         }
>
> -      if (src_var != *ass_var)
> -       return false;
> -
>        *ass_var = dest;
>        return true;
>      }
> @@ -428,6 +425,13 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
>           break;
>         }
>
> +      /* Allow simple copies between local variables, even if they're
> +        aggregates.  */
> +      if (is_gimple_assign (stmt)
> +         && auto_var_in_fn_p (gimple_assign_lhs (stmt), cfun->decl)
> +         && auto_var_in_fn_p (gimple_assign_rhs1 (stmt), cfun->decl))
> +       continue;
> +
>        /* If the statement references memory or volatile operands, fail.  */
>        if (gimple_references_memory_p (stmt)
>           || gimple_has_volatile_ops (stmt))
> @@ -444,18 +448,20 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
>        return;
>      }
>
> -  /* If the LHS of our call is not just a simple register, we can't
> -     transform this into a tail or sibling call.  This situation happens,
> -     in (e.g.) "*p = foo()" where foo returns a struct.  In this case
> -     we won't have a temporary here, but we need to carry out the side
> -     effect anyway, so tailcall is impossible.
> +  /* 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
> +     struct.  In this case we won't have a temporary here, but we need
> +     to carry out the side effect anyway, so tailcall is impossible.
>
>       ??? In some situations (when the struct is returned in memory via
>       invisible argument) we could deal with this, e.g. by passing 'p'
>       itself as that argument to foo, but it's too early to do this here,
>       and expand_call() will not handle it anyway.  If it ever can, then
>       we need to revisit this here, to allow that situation.  */
> -  if (ass_var && !is_gimple_reg (ass_var))
> +  if (ass_var
> +      && !is_gimple_reg (ass_var)
> +      && !auto_var_in_fn_p (ass_var, cfun->decl))
>      return;
>
>    /* We found the call, check whether it is suitable.  */
> @@ -888,7 +894,7 @@ eliminate_tail_call (struct tailcall *t)
>
>    call = gsi_stmt (t->call_gsi);
>    rslt = gimple_call_lhs (call);
> -  if (rslt != NULL_TREE)
> +  if (rslt != NULL_TREE && TREE_CODE (rslt) == SSA_NAME)
>      {
>        /* Result of the call will no longer be defined.  So adjust the
>          SSA_NAME_DEF_STMT accordingly.  */
>

Reply via email to