On 24 November 2016 at 14:07, Richard Biener <rguent...@suse.de> wrote: > 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. Well I was trying to copy-propagate function's argument into uses of it's return value if function returned that argument, so the assignment to lhs of call could be made redundant.
eg: void *f(void *a1, void *a2, __SIZE_TYPE__ a3) { void *t1 = __builtin_memcpy (a1, a2, a3); return t1; } After patch, copyprop transformed it into: t1 = __builtin_memcpy (a1, a2, a3); return a1; But this now interferes with tail-call optimization, because it is not able to emit memcpy as tail-call anymore due to which the patch regressed 20050503-1.c. I am not sure how to workaround this. Thanks, Prathamesh > > Richard.
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-copyprop-3.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-copyprop-3.c new file mode 100644 index 0000000..8319e9f --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-copyprop-3.c @@ -0,0 +1,35 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-copyprop-slim" } */ + +void f(char *dest, const char *src, __SIZE_TYPE__ n) +{ + char *t1 = __builtin_strcpy (dest, src); + if (t1 != dest) + __builtin_abort (); + + char *t2 = __builtin_strncpy (dest, src, n); + if (t2 != dest) + __builtin_abort (); + + char *t3 = __builtin_strcat (dest, src); + if (t3 != dest) + __builtin_abort (); + + char *t4 = __builtin_strncat (dest, src, n); + if (t4 != dest) + __builtin_abort (); + + void *t5 = __builtin_memmove (dest, src, n); + if (t5 != dest) + __builtin_abort (); + + void *t6 = __builtin_memcpy (dest, src, n); + if (t6 != dest) + __builtin_abort (); + + void *t7 = __builtin_memset (dest, 0, n); + if (t7 != dest) + __builtin_abort (); +} + +/* { dg-final { scan-tree-dump-not "__builtin_abort" "copyprop1" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-copyprop-4.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-copyprop-4.c new file mode 100644 index 0000000..199074d --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-copyprop-4.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-copyprop" } */ + +char *f(char *dest, const char *src) +{ + char *t1 = __builtin_strcpy (dest, src); + return t1; +} + +/* { dg-final { scan-tree-dump "return dest_\[0-9\]*\\(D\\);" "copyprop1" } } */ diff --git a/gcc/tree-ssa-copy.c b/gcc/tree-ssa-copy.c index abc6205..77f4ad7 100644 --- a/gcc/tree-ssa-copy.c +++ b/gcc/tree-ssa-copy.c @@ -80,6 +80,9 @@ stmt_may_generate_copy (gimple *stmt) if (gimple_code (stmt) == GIMPLE_PHI) return !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_phi_result (stmt)); + if (gimple_code (stmt) == GIMPLE_CALL) + return true; + if (gimple_code (stmt) != GIMPLE_ASSIGN) return false; @@ -252,6 +255,40 @@ copy_prop_visit_cond_stmt (gimple *stmt, edge *taken_edge_p) return retval; } +/* Check if call returns one of it's arguments, and in that + case set copy-of (lhs) to the returned argument. */ + +static enum ssa_prop_result +copy_prop_visit_call (gcall *call_stmt, tree *result_p) +{ + tree lhs = gimple_call_lhs (call_stmt); + if (!lhs || TREE_CODE (lhs) != SSA_NAME) + return SSA_PROP_VARYING; + + 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); + if (TREE_CODE (arg) == SSA_NAME) + { + arg = valueize_val (arg); + if (!may_propagate_copy (lhs, arg)) + return SSA_PROP_VARYING; + + *result_p = lhs; + if (set_copy_of_val (*result_p, arg)) + return SSA_PROP_INTERESTING; + else + return SSA_PROP_NOT_INTERESTING; + } + } + } + + return SSA_PROP_VARYING; +} /* Evaluate statement STMT. If the statement produces a new output value, return SSA_PROP_INTERESTING and store the SSA_NAME holding @@ -290,6 +327,8 @@ copy_prop_visit_stmt (gimple *stmt, edge *taken_edge_p, tree *result_p) jump. */ retval = copy_prop_visit_cond_stmt (stmt, taken_edge_p); } + else if (gcall *call_stmt = dyn_cast<gcall *> (stmt)) + retval = copy_prop_visit_call (call_stmt, result_p); else retval = SSA_PROP_VARYING;