Handle sibcalls with aggregate returns
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. 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. From-SVN: r242668
This commit is contained in:
parent
67b5d0b2fe
commit
9713daa015
@ -1,3 +1,14 @@
|
|||||||
|
2016-11-21 Richard Sandiford <richard.sandiford@arm.com>
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
2016-11-21 David Malcolm <dmalcolm@redhat.com>
|
2016-11-21 David Malcolm <dmalcolm@redhat.com>
|
||||||
|
|
||||||
PR preprocessor/78324
|
PR preprocessor/78324
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
2016-11-21 Richard Sandiford <richard.sandiford@arm.com>
|
||||||
|
|
||||||
|
* gcc.dg/tree-ssa/tailcall-7.c: New test.
|
||||||
|
|
||||||
2016-11-21 David Malcolm <dmalcolm@redhat.com>
|
2016-11-21 David Malcolm <dmalcolm@redhat.com>
|
||||||
|
|
||||||
PR preprocessor/78324
|
PR preprocessor/78324
|
||||||
|
89
gcc/testsuite/gcc.dg/tree-ssa/tailcall-7.c
Normal file
89
gcc/testsuite/gcc.dg/tree-ssa/tailcall-7.c
Normal file
@ -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" } } */
|
@ -269,7 +269,7 @@ process_assignment (gassign *stmt, gimple_stmt_iterator call, tree *m,
|
|||||||
conversions that can never produce extra code between the function
|
conversions that can never produce extra code between the function
|
||||||
call and the function return. */
|
call and the function return. */
|
||||||
if ((rhs_class == GIMPLE_SINGLE_RHS || gimple_assign_cast_p (stmt))
|
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
|
/* Reject a tailcall if the type conversion might need
|
||||||
additional code. */
|
additional code. */
|
||||||
@ -287,9 +287,6 @@ process_assignment (gassign *stmt, gimple_stmt_iterator call, tree *m,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src_var != *ass_var)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*ass_var = dest;
|
*ass_var = dest;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -428,6 +425,13 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
|
|||||||
break;
|
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 the statement references memory or volatile operands, fail. */
|
||||||
if (gimple_references_memory_p (stmt)
|
if (gimple_references_memory_p (stmt)
|
||||||
|| gimple_has_volatile_ops (stmt))
|
|| gimple_has_volatile_ops (stmt))
|
||||||
@ -444,18 +448,20 @@ find_tail_calls (basic_block bb, struct tailcall **ret)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the LHS of our call is not just a simple register, we can't
|
/* If the LHS of our call is not just a simple register or local
|
||||||
transform this into a tail or sibling call. This situation happens,
|
variable, we can't transform this into a tail or sibling call.
|
||||||
in (e.g.) "*p = foo()" where foo returns a struct. In this case
|
This situation happens, in (e.g.) "*p = foo()" where foo returns a
|
||||||
we won't have a temporary here, but we need to carry out the side
|
struct. In this case we won't have a temporary here, but we need
|
||||||
effect anyway, so tailcall is impossible.
|
to carry out the side effect anyway, so tailcall is impossible.
|
||||||
|
|
||||||
??? In some situations (when the struct is returned in memory via
|
??? In some situations (when the struct is returned in memory via
|
||||||
invisible argument) we could deal with this, e.g. by passing 'p'
|
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,
|
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
|
and expand_call() will not handle it anyway. If it ever can, then
|
||||||
we need to revisit this here, to allow that situation. */
|
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;
|
return;
|
||||||
|
|
||||||
/* We found the call, check whether it is suitable. */
|
/* 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);
|
call = gsi_stmt (t->call_gsi);
|
||||||
rslt = gimple_call_lhs (call);
|
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
|
/* Result of the call will no longer be defined. So adjust the
|
||||||
SSA_NAME_DEF_STMT accordingly. */
|
SSA_NAME_DEF_STMT accordingly. */
|
||||||
|
Loading…
Reference in New Issue
Block a user