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 <[email protected]>
* 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;
}