On Thu, Aug 7, 2025 at 4:36 AM Richard Biener
<richard.guent...@gmail.com> wrote:
>
> On Wed, Aug 6, 2025 at 7:34 PM Andrew Pinski <quic_apin...@quicinc.com> wrote:
> >
> > This implements the simple copy prop of aggregates into
> > arguments of function calls. This can reduce the number of copies
> > done. Just like removing of an extra copy in general, this can and
> > will help out SRA; since we might not need to do a full scalarization
> > of the aggregate now.
> >
> > This is the simpliest form of this copy prop of aggregates.
>
> Hmm, I wonder about
>
>    x = foo (x);
>
> so cannot both cases apply?  In particular ...

Make sense, I moved checks back into optimize_agr_copyprop.
I will be submitting a new version of the patch in a few. Just want to
do some more testing.

Thanks,
Andrew



>
> > gcc/ChangeLog:
> >
> >         * tree-ssa-forwprop.cc (optimize_agr_copyprop_1): New function 
> > split out of ...
> >         (optimize_agr_copyprop): Here. Also try calling 
> > optimize_agr_copyprop_arg.
> >         (optimize_agr_copyprop_arg): New function.
> >
> > gcc/testsuite/ChangeLog:
> >
> >         * gcc.dg/tree-ssa/copy-prop-aggregate-arg-1.c: New test.
> >
> > Signed-off-by: Andrew Pinski <quic_apin...@quicinc.com>
> > ---
> >  .../tree-ssa/copy-prop-aggregate-arg-1.c      |  21 +++
> >  gcc/tree-ssa-forwprop.cc                      | 163 ++++++++++++------
> >  2 files changed, 134 insertions(+), 50 deletions(-)
> >  create mode 100644 
> > gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-arg-1.c
> >
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-arg-1.c 
> > b/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-arg-1.c
> > new file mode 100644
> > index 00000000000..2ce68918e57
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-arg-1.c
> > @@ -0,0 +1,21 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O1 -fdump-tree-optimized -fdump-tree-forwprop1-details" 
> > } */
> > +
> > +struct s1
> > +{
> > +  int t[1024];
> > +};
> > +
> > +void f(struct s1);
> > +
> > +void g(struct s1 a)
> > +{
> > +  struct s1 temp_struct0 = a;
> > +  f(temp_struct0);
> > +}
> > +
> > +/* There should be no references to any of "temp_struct*"
> > +   temporaries.  */
> > +/* { dg-final { scan-tree-dump-times "temp_struct" 0 "optimized" } } */
> > +/* Also check that forwprop pass did the copy prop. */
> > +/* { dg-final { scan-tree-dump-times "after previous" 1 "forwprop1" } } */
> > diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
> > index 1cde5f85150..d6f867bae7c 100644
> > --- a/gcc/tree-ssa-forwprop.cc
> > +++ b/gcc/tree-ssa-forwprop.cc
> > @@ -1416,6 +1416,107 @@ optimize_aggr_zeroprop (gimple_stmt_iterator *gsip)
> >
> >    return changed;
> >  }
> > +
> > +/* Helper function for optimize_agr_copyprop.
> > +   For aggregate copies in USE_STMT, see if DEST
> > +   is on the lhs of USE_STMT and replace it with SRC. */
> > +static bool
> > +optimize_agr_copyprop_1 (gimple *stmt, gimple *use_stmt,
> > +                        tree dest, tree src)
> > +{
> > +  if (!gimple_assign_load_p (use_stmt)
> > +      || !gimple_store_p (use_stmt))
> > +    return false;
> > +  if (gimple_has_volatile_ops (use_stmt))
> > +    return false;
> > +  tree dest2 = gimple_assign_lhs (use_stmt);
> > +  tree src2 = gimple_assign_rhs1 (use_stmt);
> > +  /* If the new store is `src2 = src2;` skip over it. */
> > +  if (operand_equal_p (src2, dest2, 0))
> > +    return false;
> > +  if (!operand_equal_p (dest, src2, 0))
> > +    return false;
> > +  /* For 2 memory refences and using a temporary to do the copy,
> > +     don't remove the temporary as the 2 memory references might overlap.
> > +     Note t does not need to be decl as it could be field.
> > +     See PR 22237 for full details.
> > +     E.g.
> > +     t = *a; #DEST = SRC;
> > +     *b = t; #DEST2 = SRC2;
> > +     Cannot be convert into
> > +     t = *a;
> > +     *b = *a;
> > +     Though the following is allowed to be done:
> > +     t = *a;
> > +     *a = t;
> > +     And convert it into:
> > +     t = *a;
> > +     *a = *a;
> > +     */
> > +  if (!operand_equal_p (dest2, src, 0)
> > +      && !DECL_P (dest2) && !DECL_P (src))
> > +    return false;
> > +
> > +  if (dump_file && (dump_flags & TDF_DETAILS))
> > +    {
> > +      fprintf (dump_file, "Simplified\n  ");
> > +      print_gimple_stmt (dump_file, use_stmt, 0, dump_flags);
> > +      fprintf (dump_file, "after previous\n  ");
> > +      print_gimple_stmt (dump_file, stmt, 0, dump_flags);
> > +    }
> > +  gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
> > +  gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (src));
> > +  update_stmt (use_stmt);
> > +
> > +  if (dump_file && (dump_flags & TDF_DETAILS))
> > +    {
> > +      fprintf (dump_file, "into\n  ");
> > +      print_gimple_stmt (dump_file, use_stmt, 0, dump_flags);
> > +    }
> > +  statistics_counter_event (cfun, "copy prop for aggregate", 1);
> > +  return true;
> > +}
> > +
> > +/* Helper function for optimize_agr_copyprop_1, propagate aggregates
> > +   into the arguments of USE_STMT if the argument matches with DEST;
> > +   replacing it with SRC.  */
> > +static bool
> > +optimize_agr_copyprop_arg (gimple *defstmt, gimple *use_stmt,
> > +                          tree dest, tree src)
> > +{
> > +  gcall *call = dyn_cast<gcall*>(use_stmt);
> > +  if (!call)
> > +    return false;
> > +  bool changed = false;
> > +  for (unsigned arg = 0; arg < gimple_call_num_args (call); arg++)
> > +    {
> > +      tree *argptr = gimple_call_arg_ptr (call, arg);
> > +      if (TREE_CODE (*argptr) == SSA_NAME
> > +         || is_gimple_min_invariant (*argptr)
> > +         || TYPE_VOLATILE (TREE_TYPE (*argptr)))
> > +       continue;
> > +      if (!operand_equal_p (*argptr, dest, 0))
> > +       continue;
> > +      if (dump_file && (dump_flags & TDF_DETAILS))
> > +       {
> > +         fprintf (dump_file, "Simplified\n  ");
> > +         print_gimple_stmt (dump_file, call, 0, dump_flags);
> > +         fprintf (dump_file, "after previous\n  ");
> > +         print_gimple_stmt (dump_file, defstmt, 0, dump_flags);
> > +       }
> > +      *argptr = unshare_expr (src);
> > +      changed = true;
> > +      if (dump_file && (dump_flags & TDF_DETAILS))
> > +       {
> > +         fprintf (dump_file, "into\n  ");
> > +         print_gimple_stmt (dump_file, call, 0, dump_flags);
> > +       }
> > +    }
> > +  if (changed)
> > +    update_stmt (call);
> > +  return changed;
> > +}
> > +
> >  /* Optimizes
> >     DEST = SRC;
> >     DEST2 = DEST; # DEST2 = SRC2;
> > @@ -1424,6 +1525,14 @@ optimize_aggr_zeroprop (gimple_stmt_iterator *gsip)
> >     DEST2 = SRC;
> >     GSIP is the first statement and SRC is the common
> >     between the statements.
> > +
> > +   Also optimizes:
> > +   DEST = SRC;
> > +   call_func(..., DEST, ...);
> > +   into:
> > +   DEST = SRC;
> > +   call_func(..., SRC, ...);
> > +
> >  */
> >  static bool
> >  optimize_agr_copyprop (gimple_stmt_iterator *gsip)
> > @@ -1448,56 +1557,10 @@ optimize_agr_copyprop (gimple_stmt_iterator *gsip)
> >    bool changed = false;
> >    FOR_EACH_IMM_USE_STMT (use_stmt, iter, vdef)
> >      {
> > -      if (!gimple_assign_load_p (use_stmt)
> > -         || !gimple_store_p (use_stmt))
> > -       continue;
> > -      if (gimple_has_volatile_ops (use_stmt))
> > -       continue;
> > -      tree dest2 = gimple_assign_lhs (use_stmt);
> > -      tree src2 = gimple_assign_rhs1 (use_stmt);
> > -      /* If the new store is `src2 = src2;` skip over it. */
> > -      if (operand_equal_p (src2, dest2, 0))
> > -       continue;
> > -      if (!operand_equal_p (dest, src2, 0))
> > -       continue;
> > -      /* For 2 memory refences and using a temporary to do the copy,
> > -        don't remove the temporary as the 2 memory references might 
> > overlap.
> > -        Note t does not need to be decl as it could be field.
> > -        See PR 22237 for full details.
> > -        E.g.
> > -        t = *a; #DEST = SRC;
> > -        *b = t; #DEST2 = SRC2;
> > -        Cannot be convert into
> > -        t = *a;
> > -        *b = *a;
> > -        Though the following is allowed to be done:
> > -        t = *a;
> > -        *a = t;
> > -        And convert it into:
> > -        t = *a;
> > -        *a = *a;
> > -       */
> > -      if (!operand_equal_p (dest2, src, 0)
> > -         && !DECL_P (dest2) && !DECL_P (src))
> > -       continue;
> > -      if (dump_file && (dump_flags & TDF_DETAILS))
> > -       {
> > -         fprintf (dump_file, "Simplified\n  ");
> > -         print_gimple_stmt (dump_file, use_stmt, 0, dump_flags);
> > -         fprintf (dump_file, "after previous\n  ");
> > -         print_gimple_stmt (dump_file, stmt, 0, dump_flags);
> > -       }
> > -      gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
> > -      gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (src));
> > -      update_stmt (use_stmt);
> > -
> > -      if (dump_file && (dump_flags & TDF_DETAILS))
> > -       {
> > -         fprintf (dump_file, "into\n  ");
> > -         print_gimple_stmt (dump_file, use_stmt, 0, dump_flags);
> > -       }
> > -      statistics_counter_event (cfun, "copy prop for aggregate", 1);
> > -      changed = true;
> > +      if (optimize_agr_copyprop_1 (stmt, use_stmt, dest, src))
>
> ... it's not obvious this only applies to gassign use_stmt and
>
> > +       changed = true;
> > +      else if (optimize_agr_copyprop_arg (stmt, use_stmt, dest, src))
>
> ... this to gcall use_stmt.  Having those checks here would be better IMO.
>
> Richard.
>
> > +       changed = true;
> >      }
> >    return changed;
> >  }
> > --
> > 2.43.0
> >

Reply via email to