r15-6943-g9c4397cafc5ded added support to undo IPA-VRP return value optimization for tail calls, using the same code ERF_RETURNS_ARG can be supported for functions which return one of their arguments. This allows for tail calling of memset/memcpy in some cases which were not handled before.
Bootstrapped and tested on x86_64-linux-gnu. PR tree-optimization/67797 gcc/ChangeLog: * tree-tailcall.cc (find_tail_calls): Add support for ERF_RETURNS_ARG. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/tailcall-14.c: New test. Signed-off-by: Andrew Pinski <quic_apin...@quicinc.com> --- gcc/testsuite/gcc.dg/tree-ssa/tailcall-14.c | 25 +++++ gcc/tree-tailcall.cc | 105 +++++++++++--------- 2 files changed, 84 insertions(+), 46 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/tailcall-14.c diff --git a/gcc/testsuite/gcc.dg/tree-ssa/tailcall-14.c b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-14.c new file mode 100644 index 00000000000..6fadff8ea00 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-14.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-tailc-details" } */ + +/* PR tree-optimization/67797 */ + +void *my_func(void *s, int n) +{ + __builtin_memset(s, 0, n); + return s; +} +void *my_func1(void *d, void *s, int n) +{ + __builtin_memcpy(d, s, n); + return d; +} +void *my_func2(void *s, void *p1, int n) +{ + if (p1) + __builtin_memcpy(s, p1, n); + else + __builtin_memset(s, 0, n); + return s; +} + +/* { dg-final { scan-tree-dump-times "Found tail call" 4 "tailc"} } */ diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc index f593363dae4..1371454406f 100644 --- a/gcc/tree-tailcall.cc +++ b/gcc/tree-tailcall.cc @@ -1083,57 +1083,70 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, { bool ok = false; value_range val; - tree valr; - /* If IPA-VRP proves called function always returns a singleton range, - the return value is replaced by the only value in that range. - For tail call purposes, pretend such replacement didn't happen. */ if (ass_var == NULL_TREE && !tail_recursion) - if (tree type = gimple_range_type (call)) - if (tree callee = gimple_call_fndecl (call)) - if ((INTEGRAL_TYPE_P (type) - || SCALAR_FLOAT_TYPE_P (type) - || POINTER_TYPE_P (type)) - && useless_type_conversion_p (TREE_TYPE (TREE_TYPE (callee)), - type) - && useless_type_conversion_p (TREE_TYPE (ret_var), type) - && ipa_return_value_range (val, callee) - && val.singleton_p (&valr)) + { + tree other_value = NULL_TREE; + /* If we have a function call that we know the return value is the same + as the argument, try the argument too. */ + int flags = gimple_call_return_flags (call); + if ((flags & ERF_RETURNS_ARG) != 0 + && (flags & ERF_RETURN_ARG_MASK) < gimple_call_num_args (call)) + other_value = gimple_call_arg (call, flags & ERF_RETURN_ARG_MASK); + /* If IPA-VRP proves called function always returns a singleton range, + the return value is replaced by the only value in that range. + For tail call purposes, pretend such replacement didn't happen. */ + else if (tree type = gimple_range_type (call)) + if (tree callee = gimple_call_fndecl (call)) { - tree rv = ret_var; - unsigned int i = edges.length (); - /* If ret_var is equal to valr, we can tail optimize. */ - if (operand_equal_p (ret_var, valr, 0)) - ok = true; - else - /* Otherwise, if ret_var is a PHI result, try to find out - if valr isn't propagated through PHIs on the path from - call's bb to SSA_NAME_DEF_STMT (ret_var)'s bb. */ - while (TREE_CODE (rv) == SSA_NAME - && gimple_code (SSA_NAME_DEF_STMT (rv)) == GIMPLE_PHI) - { - tree nrv = NULL_TREE; - gimple *g = SSA_NAME_DEF_STMT (rv); - for (; i; --i) - { - if (edges[i - 1]->dest == gimple_bb (g)) - { - nrv - = gimple_phi_arg_def_from_edge (g, + tree valr; + if ((INTEGRAL_TYPE_P (type) + || SCALAR_FLOAT_TYPE_P (type) + || POINTER_TYPE_P (type)) + && useless_type_conversion_p (TREE_TYPE (TREE_TYPE (callee)), + type) + && useless_type_conversion_p (TREE_TYPE (ret_var), type) + && ipa_return_value_range (val, callee) + && val.singleton_p (&valr)) + other_value = valr; + } + + if (other_value) + { + tree rv = ret_var; + unsigned int i = edges.length (); + /* If ret_var is equal to other_value, we can tail optimize. */ + if (operand_equal_p (ret_var, other_value, 0)) + ok = true; + else + /* Otherwise, if ret_var is a PHI result, try to find out + if other_value isn't propagated through PHIs on the path from + call's bb to SSA_NAME_DEF_STMT (ret_var)'s bb. */ + while (TREE_CODE (rv) == SSA_NAME + && gimple_code (SSA_NAME_DEF_STMT (rv)) == GIMPLE_PHI) + { + tree nrv = NULL_TREE; + gimple *g = SSA_NAME_DEF_STMT (rv); + for (; i; --i) + { + if (edges[i - 1]->dest == gimple_bb (g)) + { + nrv = gimple_phi_arg_def_from_edge (g, edges[i - 1]); - --i; - break; - } - } - if (nrv == NULL_TREE) + --i; + break; + } + } + if (nrv == NULL_TREE) + break; + if (operand_equal_p (nrv, other_value, 0)) + { + ok = true; break; - if (operand_equal_p (nrv, valr, 0)) - { - ok = true; - break; - } + } rv = nrv; - } - } + } + } + } if (!ok) { maybe_error_musttail (call, _("call and return value are different"), -- 2.43.0