On Mon, Sep 15, 2025 at 7:20 PM Andrew Pinski <[email protected]> wrote: > > This moves the code used in optimize_agr_copyprop_1 (r16-3887-g597b50abb0d) > to handle this same case into its new function and use it inside > optimize_agr_copyprop_arg. This allows to remove more copies that show up only > in arguments. > > Bootstrapped and tested on x86_64-linux-gnu.
OK. > gcc/ChangeLog: > > * tree-ssa-forwprop.cc (optimize_agr_copyprop_1): Split out > the case where `operand_equal_p (dest, src2)` is false into ... > (new_src_based_on_copy): This. New function. > (optimize_agr_copyprop_arg): Use new_src_based_on_copy > instead of operand_equal_p to find the new src. > > gcc/testsuite/ChangeLog: > > * gcc.dg/tree-ssa/copy-prop-aggregate-arg-2.c: New test. > > Signed-off-by: Andrew Pinski <[email protected]> > --- > .../tree-ssa/copy-prop-aggregate-arg-2.c | 33 +++++ > gcc/tree-ssa-forwprop.cc | 135 ++++++++++-------- > 2 files changed, 106 insertions(+), 62 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-arg-2.c > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-arg-2.c > b/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-arg-2.c > new file mode 100644 > index 00000000000..11f0768c111 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-arg-2.c > @@ -0,0 +1,33 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O1 -fdump-tree-forwprop1-details -fdump-tree-optimized" } > */ > + > + > +struct s1 > +{ > + int t[1024]; > +}; > + > +struct s2 { > + struct s1 t; > +}; > + > +struct s3 > +{ > + struct s2 t; > +}; > + > +void g(struct s3); > + > +void f(struct s1 s) > +{ > + struct s2 removeme; > + removeme.t = s; > + struct s3 removeme2; > + removeme2.t = removeme; > + g(removeme2); > +} > + > + > +/* { dg-final { scan-tree-dump-times "after previous" 2 "forwprop1" } } */ > +/* { dg-final { scan-tree-dump-not "removeme " "optimized" } } */ > +/* { dg-final { scan-tree-dump-not "removeme2 " "optimized" } } */ > diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc > index 1eacff01587..f58a7b8c591 100644 > --- a/gcc/tree-ssa-forwprop.cc > +++ b/gcc/tree-ssa-forwprop.cc > @@ -1458,6 +1458,72 @@ split_core_and_offset_size (tree expr, > return core; > } > > +/* Returns a new src based on the > + copy `DEST = SRC` and for the old SRC2. > + Returns null if SRC2 is not related to DEST. */ > + > +static tree > +new_src_based_on_copy (tree src2, tree dest, tree src) > +{ > + /* If the second src is not exactly the same as dest, > + try to handle it seperately; see it is address/size equivalent. > + Handles `a` and `a.b` and `MEM<char[N]>(&a)` which all have > + the same size and offsets as address/size equivalent. > + This allows copying over a memcpy and also one for copying > + where one field is the same size as the whole struct. */ > + if (operand_equal_p (dest, src2)) > + return src; > + /* A VCE can't be used with imag/real or BFR so reject them early. */ > + if (TREE_CODE (src) == IMAGPART_EXPR > + || TREE_CODE (src) == REALPART_EXPR > + || TREE_CODE (src) == BIT_FIELD_REF) > + return NULL_TREE; > + tree core1, core2; > + poly_int64 bytepos1, bytepos2; > + poly_int64 bytesize1, bytesize2; > + tree toffset1, toffset2; > + int reversep1 = 0; > + int reversep2 = 0; > + poly_int64 diff = 0; > + core1 = split_core_and_offset_size (dest, &bytesize1, &bytepos1, > + &toffset1, &reversep1); > + core2 = split_core_and_offset_size (src2, &bytesize2, &bytepos2, > + &toffset2, &reversep2); > + if (!core1 || !core2) > + return NULL_TREE; > + if (reversep1 != reversep2) > + return NULL_TREE; > + /* The sizes of the 2 accesses need to be the same. */ > + if (!known_eq (bytesize1, bytesize2)) > + return NULL_TREE; > + if (!operand_equal_p (core1, core2, 0)) > + return NULL_TREE; > + > + if (toffset1 && toffset2) > + { > + tree type = TREE_TYPE (toffset1); > + if (type != TREE_TYPE (toffset2)) > + toffset2 = fold_convert (type, toffset2); > + > + tree tdiff = fold_build2 (MINUS_EXPR, type, toffset1, toffset2); > + if (!cst_and_fits_in_hwi (tdiff)) > + return NULL_TREE; > + > + diff = int_cst_value (tdiff); > + } > + else if (toffset1 || toffset2) > + { > + /* If only one of the offsets is non-constant, the difference cannot > + be a constant. */ > + return NULL_TREE; > + } > + diff += bytepos1 - bytepos2; > + /* The offset between the 2 need to be 0. */ > + if (!known_eq (diff, 0)) > + return NULL_TREE; > + return fold_build1 (VIEW_CONVERT_EXPR,TREE_TYPE (src2), src); > +} > + > /* 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. */ > @@ -1474,66 +1540,9 @@ optimize_agr_copyprop_1 (gimple *stmt, gimple > *use_stmt, > /* If the new store is `src2 = src2;` skip over it. */ > if (operand_equal_p (src2, dest2, 0)) > return false; > - /* If the second src is not exactly the same as dest, > - try to handle it seperately; see it is address/size equivalent. > - Handles `a` and `a.b` and `MEM<char[N]>(&a)` which all have > - the same size and offsets as address/size equivalent. > - This allows copying over a memcpy and also one for copying > - where one field is the same size as the whole struct. */ > - if (!operand_equal_p (dest, src2, 0)) > - { > - /* A VCE can't be used with imag/real or BFR so reject them early. */ > - if (TREE_CODE (src) == IMAGPART_EXPR > - || TREE_CODE (src) == REALPART_EXPR > - || TREE_CODE (src) == BIT_FIELD_REF) > - return false; > - tree core1, core2; > - poly_int64 bytepos1, bytepos2; > - poly_int64 bytesize1, bytesize2; > - tree toffset1, toffset2; > - int reversep1 = 0; > - int reversep2 = 0; > - poly_int64 diff = 0; > - core1 = split_core_and_offset_size (dest, &bytesize1, &bytepos1, > - &toffset1, &reversep1); > - core2 = split_core_and_offset_size (src2, &bytesize2, &bytepos2, > - &toffset2, &reversep2); > - if (!core1 || !core2) > - return false; > - if (reversep1 != reversep2) > - return false; > - /* The sizes of the 2 accesses need to be the same. */ > - if (!known_eq (bytesize1, bytesize2)) > - return false; > - if (!operand_equal_p (core1, core2, 0)) > - return false; > - > - if (toffset1 && toffset2) > - { > - tree type = TREE_TYPE (toffset1); > - if (type != TREE_TYPE (toffset2)) > - toffset2 = fold_convert (type, toffset2); > - > - tree tdiff = fold_build2 (MINUS_EXPR, type, toffset1, toffset2); > - if (!cst_and_fits_in_hwi (tdiff)) > - return false; > - > - diff = int_cst_value (tdiff); > - } > - else if (toffset1 || toffset2) > - { > - /* If only one of the offsets is non-constant, the difference cannot > - be a constant. */ > - return false; > - } > - diff += bytepos1 - bytepos2; > - /* The offset between the 2 need to be 0. */ > - if (!known_eq (diff, 0)) > - return false; > - src = fold_build1_loc (gimple_location (use_stmt), > - VIEW_CONVERT_EXPR, > - TREE_TYPE (src2), src); > - } > + src = new_src_based_on_copy (src2, dest, src); > + if (!src) > + 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. > @@ -1634,8 +1643,10 @@ optimize_agr_copyprop_arg (gimple *defstmt, gcall > *call, > || is_gimple_min_invariant (*argptr) > || TYPE_VOLATILE (TREE_TYPE (*argptr))) > continue; > - if (!operand_equal_p (*argptr, dest, 0)) > + tree newsrc = new_src_based_on_copy (*argptr, dest, src); > + if (!newsrc) > continue; > + > if (dump_file && (dump_flags & TDF_DETAILS)) > { > fprintf (dump_file, "Simplified\n "); > @@ -1643,7 +1654,7 @@ optimize_agr_copyprop_arg (gimple *defstmt, gcall *call, > fprintf (dump_file, "after previous\n "); > print_gimple_stmt (dump_file, defstmt, 0, dump_flags); > } > - *argptr = unshare_expr (src); > + *argptr = unshare_expr (newsrc); > changed = true; > if (dump_file && (dump_flags & TDF_DETAILS)) > { > -- > 2.43.0 >
