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"),

Reply via email to