[tree-tailcall] Check if function returns it's argument

Message ID CAAgBjMkcer2PZkJNBE10eAqWt2RXT7vOp+ERmp+FL1QQhmVZGA@mail.gmail.com
State New
Headers show

Commit Message

Prathamesh Kulkarni Nov. 24, 2016, 7:18 a.m.
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.kulkarni@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.

Comments

Richard Biener Nov. 24, 2016, 8:37 a.m. | #1
On Thu, 24 Nov 2016, Prathamesh Kulkarni wrote:

> 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 ?


+/* Return arg, if function returns it's argument or NULL if it doesn't.  
*/
+tree
+gimple_call_return_arg (gcall *call_stmt)
+{


Please just inline it at the single use - the name is not terribly
informative.

I'm not sure you can rely on code-generation working if you not
effectively change the IL to

  a1 = __builtin_memcpy (a1, a2, a3);
  return a1;

someone more familiar with RTL expansion plus tail call emission on
RTL needs to chime in.

Richard.

Patch hide | download patch | download mbox

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;
 	}