On Tue, 1 Apr 2025, Jakub Jelinek wrote:

> On Tue, Apr 01, 2025 at 10:59:36AM +0200, Jakub Jelinek wrote:
> > On Tue, Apr 01, 2025 at 10:46:15AM +0200, Richard Biener wrote:
> > > This looks OK, but I wonder if ...
> > > > -             /* The parameter should be a real operand, so that phi 
> > > > node
> > > > -                created for it at the start of the function has the 
> > > > meaning
> > > > -                of copying the value.  This test implies 
> > > > is_gimple_reg_type
> > > > -                from the previous condition, however this one could be
> > > > -                relaxed by being more careful with copying the new 
> > > > value
> > > > -                of the parameter (emitting appropriate GIMPLE_ASSIGN 
> > > > and
> > > > -                updating the virtual operands).  */
> > > > -             if (!is_gimple_reg (param))
> > > > +             if (is_gimple_reg_type (TREE_TYPE (param))
> > > > +                 ? !is_gimple_reg (param)
> > > 
> > > ... we want to restrict this to musttail calls at this point and
> > > relax for stage1 only?
> > 
> > I can do that, sure.
> 
> Here it is, ok if it passes bootstrap/regtest?  I'll queue the interdiff
> between this patch and the previous one for GCC 16.

OK.

Thanks,
Richard.

> 2025-04-01  Jakub Jelinek  <ja...@redhat.com>
> 
>       PR tree-optimization/119493
>       * tree-tailcall.cc (find_tail_calls): Don't punt on tail recusion
>       if some arguments don't have is_gimple_reg_type, only punt if they
>       have non-POD types, or volatile, or addressable or (for now) it is
>       not a musttail call.  Set tailr_arg_needs_copy in those cases too.
>       (eliminate_tail_call): Copy call arguments to params if they don't
>       have is_gimple_reg_type, use temporaries if the argument is used
>       later.
>       (tree_optimize_tail_calls_1): Skip !is_gimple_reg_type
>       tailr_arg_needs_copy parameters.  Formatting fix.
> 
>       * gcc.dg/pr119493-1.c: New test.
> 
> --- gcc/tree-tailcall.cc.jj   2025-03-28 10:49:30.000000000 +0100
> +++ gcc/tree-tailcall.cc      2025-04-01 11:47:31.417637335 +0200
> @@ -676,19 +676,17 @@ find_tail_calls (basic_block bb, struct
>                have a copyable type and the two arguments must have reasonably
>                equivalent types.  The latter requirement could be relaxed if
>                we emitted a suitable type conversion statement.  */
> -           if (!is_gimple_reg_type (TREE_TYPE (param))
> +           if (TREE_ADDRESSABLE (TREE_TYPE (param))
>                 || !useless_type_conversion_p (TREE_TYPE (param),
>                                                TREE_TYPE (arg)))
>               break;
>  
> -           /* The parameter should be a real operand, so that phi node
> -              created for it at the start of the function has the meaning
> -              of copying the value.  This test implies is_gimple_reg_type
> -              from the previous condition, however this one could be
> -              relaxed by being more careful with copying the new value
> -              of the parameter (emitting appropriate GIMPLE_ASSIGN and
> -              updating the virtual operands).  */
> -           if (!is_gimple_reg (param))
> +           if (is_gimple_reg_type (TREE_TYPE (param))
> +               ? !is_gimple_reg (param)
> +               : (!is_gimple_variable (param)
> +                  || TREE_THIS_VOLATILE (param)
> +                  || may_be_aliased (param)
> +                  || !gimple_call_must_tail_p (call)))
>               break;
>           }
>       }
> @@ -938,9 +936,9 @@ find_tail_calls (basic_block bb, struct
>          param = DECL_CHAIN (param), idx++)
>       {
>         tree ddef, arg = gimple_call_arg (call, idx);
> -       if (is_gimple_reg (param)
> -           && (ddef = ssa_default_def (cfun, param))
> -           && (arg != ddef))
> +       if (!is_gimple_reg (param)
> +           || ((ddef = ssa_default_def (cfun, param))
> +               && arg != ddef))
>           bitmap_set_bit (tailr_arg_needs_copy, idx);
>       }
>      }
> @@ -1216,6 +1214,7 @@ eliminate_tail_call (struct tailcall *t,
>  
>    /* Add phi node entries for arguments.  The ordering of the phi nodes 
> should
>       be the same as the ordering of the arguments.  */
> +  auto_vec<tree> copies;
>    for (param = DECL_ARGUMENTS (current_function_decl),
>        idx = 0, gpi = gsi_start_phis (first);
>         param;
> @@ -1224,6 +1223,35 @@ eliminate_tail_call (struct tailcall *t,
>        if (!bitmap_bit_p (tailr_arg_needs_copy, idx))
>       continue;
>  
> +      if (!is_gimple_reg_type (TREE_TYPE (param)))
> +     {
> +       if (param == gimple_call_arg (stmt, idx))
> +         continue;
> +       /* First check if param isn't used by any of the following
> +          call arguments.  If it is, we need to copy first to
> +          a temporary and only after doing all the assignments copy it
> +          to param.  */
> +       size_t idx2 = idx + 1;
> +       tree param2 = DECL_CHAIN (param);
> +       for (; param2; param2 = DECL_CHAIN (param2), idx2++)
> +         if (!is_gimple_reg_type (TREE_TYPE (param)))
> +           {
> +             tree base = get_base_address (gimple_call_arg (stmt, idx2));
> +             if (base == param)
> +               break;
> +           }
> +       tree tmp = param;
> +       if (param2)
> +         {
> +           tmp = create_tmp_var (TREE_TYPE (param));
> +           copies.safe_push (param);
> +           copies.safe_push (tmp);
> +         }
> +       gimple *g = gimple_build_assign (tmp, gimple_call_arg (stmt, idx));
> +       gsi_insert_before (&t->call_gsi, g, GSI_SAME_STMT);
> +       continue;
> +     }
> +
>        arg = gimple_call_arg (stmt, idx);
>        phi = gpi.phi ();
>        gcc_assert (param == SSA_NAME_VAR (PHI_RESULT (phi)));
> @@ -1231,6 +1259,11 @@ eliminate_tail_call (struct tailcall *t,
>        add_phi_arg (phi, arg, e, gimple_location (stmt));
>        gsi_next (&gpi);
>      }
> +  for (unsigned i = 0; i < copies.length (); i += 2)
> +    {
> +      gimple *g = gimple_build_assign (copies[i], copies[i + 1]);
> +      gsi_insert_before (&t->call_gsi, g, GSI_SAME_STMT);
> +    }
>  
>    /* Update the values of accumulators.  */
>    adjust_accumulator_values (t->call_gsi, t->mult, t->add, e);
> @@ -1390,8 +1423,8 @@ tree_optimize_tail_calls_1 (bool opt_tai
>            or if there are existing degenerate PHI nodes.  */
>         if (!single_pred_p (first)
>             || !gimple_seq_empty_p (phi_nodes (first)))
> -         first =
> -           split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
> +         first
> +           = split_edge (single_succ_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
>  
>         /* Copy the args if needed.  */
>         unsigned idx;
> @@ -1400,6 +1433,8 @@ tree_optimize_tail_calls_1 (bool opt_tai
>              param = DECL_CHAIN (param), idx++)
>           if (bitmap_bit_p (tailr_arg_needs_copy, idx))
>             {
> +             if (!is_gimple_reg_type (TREE_TYPE (param)))
> +               continue;
>               tree name = ssa_default_def (cfun, param);
>               tree new_name = make_ssa_name (param, SSA_NAME_DEF_STMT (name));
>               gphi *phi;
> --- gcc/testsuite/gcc.dg/pr119493-1.c.jj      2025-03-31 12:37:08.194529478 
> +0200
> +++ gcc/testsuite/gcc.dg/pr119493-1.c 2025-03-31 12:45:10.583848794 +0200
> @@ -0,0 +1,55 @@
> +/* PR tree-optimization/119493 */
> +/* { dg-do run } */
> +/* { dg-options "-O2 -fdump-tree-tailr1" } */
> +/* { dg-final { scan-tree-dump-times " = foo \\\(\[^\n\r;]*\\\);" 4 "tailr1" 
> } } */
> +/* { dg-final { scan-tree-dump-times " = bar \\\(\[^\n\r;]*\\\);" 4 "tailr1" 
> } } */
> +/* { dg-final { scan-tree-dump-not " = foo \\\(\[^\n\r;]*\\\); \\\[must tail 
> call\\\]" "tailr1" } } */
> +/* { dg-final { scan-tree-dump-not " = bar \\\(\[^\n\r;]*\\\); \\\[must tail 
> call\\\]" "tailr1" } } */
> +
> +struct S { unsigned s; };
> +struct T { struct S t[2]; };
> +
> +[[gnu::noinline, gnu::noclone]] struct S
> +foo (struct S m)
> +{
> +  if (m.s == 0 || m.s == 42)
> +    return m;
> +  [[gnu::musttail]] return foo ((struct S) { m.s - 1 });
> +}
> +
> +[[gnu::noinline, gnu::noclone]] struct S
> +bar (struct T m, struct S n, int o, int p, int q)
> +{
> +  struct T r;
> +  if (m.t[1].s != o || n.s != o)
> +    __builtin_abort ();
> +  if (o == 0 || o == 42)
> +    return n;
> +  r = m;
> +  m.t[1].s -= p;
> +  r.t[1].s -= q;
> +  [[gnu::musttail]] return bar (r, m.t[1], o - 1, p, q);
> +}
> +
> +int
> +main ()
> +{
> +  if (foo ((struct S) { 0 }).s != 0)
> +    __builtin_abort ();
> +  if (foo ((struct S) { 4 }).s != 0)
> +    __builtin_abort ();
> +  if (foo ((struct S) { 42 }).s != 42)
> +    __builtin_abort ();
> +  if (foo ((struct S) { 51 }).s != 42)
> +    __builtin_abort ();
> +  if (bar ((struct T) { { { 0 }, { 0 } } }, (struct S) { 0 }, 0, 1, 1).s != 
> 0)
> +    __builtin_abort ();
> +  if (bar ((struct T) { { { 7 }, { 7 } } }, (struct S) { 7 }, 7, 1, 1).s != 
> 0)
> +    __builtin_abort ();
> +  if (bar ((struct T) { { { 42 }, { 42 } } },
> +        (struct S) { 42 }, 42, 1, 1).s != 42)
> +    __builtin_abort ();
> +  if (bar ((struct T) { { { 48 }, { 48 } } },
> +        (struct S) { 48 }, 48, 1, 1).s != 42)
> +    __builtin_abort ();
> +}
> 
> 
>       Jakub
> 
> 

-- 
Richard Biener <rguent...@suse.de>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)

Reply via email to