On Sat, Apr 19, 2025 at 8:32 PM Andrew Pinski <quic_apin...@quicinc.com> wrote: > > 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.
For easier review, I also attached the diff ignoring the white spaces. Since this is mostly reindenting the code. Thanks, Andrew > > 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 >
commit 2fa5c50781b02ae40d6dac5f6eb43a9c9e42f072 Author: Andrew Pinski <quic_apin...@quicinc.com> Date: Sat Apr 19 16:41:32 2025 -0700 tailcall: Support ERF_RETURNS_ARG for tailcall [PR67797] 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> 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,13 +1083,22 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, { bool ok = false; value_range val; - tree valr; + if (ass_var == NULL_TREE && !tail_recursion) + { + 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. */ - if (ass_var == NULL_TREE && !tail_recursion) - if (tree type = gimple_range_type (call)) + else if (tree type = gimple_range_type (call)) if (tree callee = gimple_call_fndecl (call)) + { + tree valr; if ((INTEGRAL_TYPE_P (type) || SCALAR_FLOAT_TYPE_P (type) || POINTER_TYPE_P (type)) @@ -1098,15 +1107,19 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, && 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 valr, we can tail optimize. */ - if (operand_equal_p (ret_var, valr, 0)) + /* 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 valr isn't propagated through PHIs on the path from + 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) @@ -1117,8 +1130,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, { if (edges[i - 1]->dest == gimple_bb (g)) { - nrv - = gimple_phi_arg_def_from_edge (g, + nrv = gimple_phi_arg_def_from_edge (g, edges[i - 1]); --i; break; @@ -1126,7 +1138,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, } if (nrv == NULL_TREE) break; - if (operand_equal_p (nrv, valr, 0)) + if (operand_equal_p (nrv, other_value, 0)) { ok = true; break; @@ -1134,6 +1146,7 @@ find_tail_calls (basic_block bb, struct tailcall **ret, bool only_musttail, rv = nrv; } } + } if (!ok) { maybe_error_musttail (call, _("call and return value are different"),