Hi, Consider following test-case: void *f(void *a1, void *a2, __SIZE_TYPE__ a3) { __builtin_memcpy (a1, a2, a3); return a1; }
return a1 can be considered equivalent to return value of memcpy, and the call could be emitted as a tail-call. gcc doesn't emit the above call to memcpy as a tail-call, but if it is changed to: void *t1 = __builtin_memcpy (a1, a2, a3); return t1; Then memcpy is emitted as a tail-call. The attached patch tries to handle the former case. Bootstrapped+tested on x86_64-unknown-linux-gnu. Cross tested on arm*-*-*, aarch64*-*-* Does this patch look OK ? Thanks, Prathamesh
2016-11-24 Prathamesh Kulkarni <prathamesh.kulka...@linaro.org> * gimple.c (gimple_call_return_arg): New function. * gimple.h (gimple_call_return_arg): Declare. * tree-tailcall.c (find_tail_calls): Call gimple_call_return_arg. testsuite/ * gcc.dg/tree-ssa/tailcall-8.c: New test. diff --git a/gcc/gimple.c b/gcc/gimple.c index 0a3dc72..ec460fc 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -2561,6 +2561,23 @@ gimple_call_combined_fn (const gimple *stmt) return CFN_LAST; } +/* Return arg, if function returns it's argument or NULL if it doesn't. */ +tree +gimple_call_return_arg (gcall *call_stmt) +{ + unsigned rf = gimple_call_return_flags (call_stmt); + if (rf & ERF_RETURNS_ARG) + { + unsigned argnum = rf & ERF_RETURN_ARG_MASK; + if (argnum < gimple_call_num_args (call_stmt)) + { + tree arg = gimple_call_arg (call_stmt, argnum); + return arg; + } + } + return NULL_TREE; +} + /* Return true if STMT clobbers memory. STMT is required to be a GIMPLE_ASM. */ diff --git a/gcc/gimple.h b/gcc/gimple.h index 0d0296e..ebccbe1 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -1520,6 +1520,7 @@ extern combined_fn gimple_call_combined_fn (const gimple *); extern bool gimple_call_builtin_p (const gimple *); extern bool gimple_call_builtin_p (const gimple *, enum built_in_class); extern bool gimple_call_builtin_p (const gimple *, enum built_in_function); +extern tree gimple_call_return_arg (gcall *); extern bool gimple_asm_clobbers_memory_p (const gasm *); extern void dump_decl_set (FILE *, bitmap); extern bool nonfreeing_call_p (gimple *); diff --git a/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8.c b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8.c new file mode 100644 index 0000000..b3fdc6c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-8.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-tailc-details" } */ + +void *f(void *a1, void *a2, __SIZE_TYPE__ a3) +{ + __builtin_memcpy (a1, a2, a3); + return a1; +} + +/* { dg-final { scan-tree-dump-times "Found tail call" 1 "tailc" } } */ diff --git a/gcc/tree-tailcall.c b/gcc/tree-tailcall.c index f97541d..3396473 100644 --- a/gcc/tree-tailcall.c +++ b/gcc/tree-tailcall.c @@ -422,6 +422,8 @@ find_tail_calls (basic_block bb, struct tailcall **ret) { call = as_a <gcall *> (stmt); ass_var = gimple_call_lhs (call); + if (!ass_var) + ass_var = gimple_call_return_arg (call); break; }