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.
Note this is very similar to https://gcc.gnu.org/legacy-ml/gcc-patches/2016-11/msg02485.html except it has a few more checks. Also on the question of expand vs tail call here is that this path is also used by the IPA-VRP return value path and yes we get a tail call. Note in the review in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83142#c2 mentions about re-instantiate a LHS on the call & propagate to dominating uses. Even though that can be done for the ERF_RETURNS_ARG case, it is not done for the IPA-VRP return value case already so I don't think there is anything to be done there. Changes since v1: * v2: Add an useless_type_conversion_p check as suggested by Jakub and add a testcase for that. 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. * gcc.dg/tree-ssa/tailcall-15.c: New test. Signed-off-by: Andrew Pinski <quic_apin...@quicinc.com> --- gcc/testsuite/gcc.dg/tree-ssa/tailcall-14.c | 25 +++++ gcc/testsuite/gcc.dg/tree-ssa/tailcall-15.c | 16 +++ gcc/tree-tailcall.cc | 109 +++++++++++--------- 3 files changed, 104 insertions(+), 46 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/tailcall-14.c create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/tailcall-15.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/testsuite/gcc.dg/tree-ssa/tailcall-15.c b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-15.c new file mode 100644 index 00000000000..bf24fd8562f --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/tailcall-15.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-tailc-details" } */ + +/* PR tree-optimization/67797 */ + +/* We should not get a tail call here since the + types don't match and we don't know how the argument + truncation will work. */ + +unsigned char my_func(int n) +{ + __builtin_memset((void*)0, 0, n); + return 0; +} + +/* { dg-final { scan-tree-dump-not "Found tail call" "tailc"} } */ diff --git a/gcc/tree-tailcall.cc b/gcc/tree-tailcall.cc index f593363dae4..db3010f67c4 100644 --- a/gcc/tree-tailcall.cc +++ b/gcc/tree-tailcall.cc @@ -1083,57 +1083,74 @@ 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)) + { + tree arg = gimple_call_arg (call, flags & ERF_RETURN_ARG_MASK); + if (useless_type_conversion_p (TREE_TYPE (arg), TREE_TYPE (ret_var))) + other_value = arg; + } + /* 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