On Mon, 31 Mar 2025, Martin Jambor wrote: > Hi, > > the testcase in PR 118924, when compiled on Aarch64, contains an > gimple aggregate assignment statement in between different types which > are types_compatible_p but behave differently for the purposes of > alias analysis. > > SRA replaces the statement with a series of scalar assignments which > however have LHSs access chains modeled on the RHS type and so do not > alias with a subsequent reads and so are DSEd. > > SRA clearly gets its "same_access_path" logic subtly wrong. One issue > is that the same_access_path_p function probably should be implemented > more along the lines of (parts of ao_compare::compare_ao_refs) instead > of internally relying on operand_equal_p. That is however not the > problem in the PR and so I will deal with it only later. > > The issue here is that even when the access path is the same, it must > not be bolted on an aggregate type that does not match. This patch > does that, taking just one simple function from the > ao_compare::compare_ao_refs machinery and using it to detect the > situation. The rest is just merging the information in between > accesses of the same access group. > > I looked at how many times we come across such assignment during > "make stage2-bubble" of GCC (configured with only c and C++ and > without multilib and libsanitizers) and on an x86_64 there were 87924 > such assignments (though now I realize not all of them had to be > aggregate), so they do happen. The patch leads to about 5% increase > of cases where we don't use an "access path" but resort to a > MEM_REF (from 90209 to 95204). On an Aarch64, there were 92268 such > assignments and the increase of falling back to MEM_REFs was by > 4% (but from a bigger base 132983 to 107991). > > Bootstrapped on x86_64-linux and Aarch64-linux. OK for master and then > for all active release branches?
I'm unsure about the lto_streaming_safe arg, in fact the implementation is if (lto_streaming_safe) return type1 == type2; else return TYPE_CANONICAL (type1) == TYPE_CANONICAL (type2); but I think we guarantee (we _need_ to guarantee!) that when TYPE_CANONICAL (type1) == TYPE_CANONICAL (type2), after LTO streaming it's still TYPE_CANONICAL (type1) == TYPE_CANONICAL (type2). Otherwise assignments previously valid GIMPLE might no longer be valid and things that aliased previously might no longer alias. But that's an implementation bug in types_equal_for_same_type_for_tbaa_p, but I think you should be able to pass in false as if the implementation were fixed. IMO if necessary the implementation itself should use sth like if (flag_lto && !in_lto_p) rather than leaving it up to the caller ... the only existing caller uses lto_streaming_expected_p () as arg, which is similar to my proposal. I'd say you want to export at a forwarder bool types_equal_for_same_type_for_tbaa_p (tree type1, tree type2) { return types_equal_for_same_type_for_tbaa_p (type1, type2, lto_streaming_expected_p ()); } instead as it should be an internal detail. OK with that change. Can you fixup the comment /* Return ture if TYPE1 and TYPE2 will always give the same answer when compared wit hother types using same_type_for_tbaa_p. */ when you are there? The function is called same_type_for_tbaa and 'wit hother' should be 'with other' Richard. > Thanks, > > Martin > > > gcc/ChangeLog: > > 2025-03-24 Martin Jambor <mjam...@suse.cz> > > PR tree-optimization/118924 > * tree-ssa-alias-compare.h (types_equal_for_same_type_for_tbaa_p): > Declare. > * tree-ssa-alias.cc (types_equal_for_same_type_for_tbaa_p): Make > public. > * tree-sra.cc: Include tree-ssa-alias-compare.h. > (create_access): Initialzie grp_same_access_path to true. > (build_accesses_from_assign): Detect tbaa hazards and clear > grp_same_access_path fields of involved accesses when they occur. > (sort_and_splice_var_accesses): Take previous values of > grp_same_access_path into account. > > gcc/testsuite/ChangeLog: > > 2025-03-25 Martin Jambor <mjam...@suse.cz> > > PR tree-optimization/118924 > * g++.dg/tree-ssa/pr118924.C: New test. > --- > gcc/testsuite/g++.dg/tree-ssa/pr118924.C | 29 ++++++++++++++++++++++++ > gcc/tree-sra.cc | 18 ++++++++++++--- > gcc/tree-ssa-alias-compare.h | 3 +++ > gcc/tree-ssa-alias.cc | 2 +- > 4 files changed, 48 insertions(+), 4 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/tree-ssa/pr118924.C > > diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr118924.C > b/gcc/testsuite/g++.dg/tree-ssa/pr118924.C > new file mode 100644 > index 00000000000..c95eacafc9c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/tree-ssa/pr118924.C > @@ -0,0 +1,29 @@ > +/* { dg-do run } */ > +/* { dg-options "-std=c++17 -O2" } */ > + > +template <int Size> struct Vector { > + int m_data[Size]; > + Vector(int, int, int) {} > +}; > +enum class E { POINTS, LINES, TRIANGLES }; > + > +__attribute__((noipa)) > +void getName(E type) { > + static E check = E::POINTS; > + if (type == check) > + check = (E)((int)check + 1); > + else > + __builtin_abort (); > +} > + > +int main() { > + int arr[]{0, 1, 2}; > + for (auto dim : arr) { > + Vector<3> localInvs(1, 1, 1); > + localInvs.m_data[dim] = 8; > + } > + E types[] = {E::POINTS, E::LINES, E::TRIANGLES}; > + for (auto primType : types) > + getName(primType); > + return 0; > +} > diff --git a/gcc/tree-sra.cc b/gcc/tree-sra.cc > index c26559edc66..22d5d1ce999 100644 > --- a/gcc/tree-sra.cc > +++ b/gcc/tree-sra.cc > @@ -100,6 +100,7 @@ along with GCC; see the file COPYING3. If not see > #include "builtins.h" > #include "tree-sra.h" > #include "opts.h" > +#include "tree-ssa-alias-compare.h" > > /* Enumeration of all aggregate reductions we can do. */ > enum sra_mode { SRA_MODE_EARLY_IPA, /* early call regularization */ > @@ -979,6 +980,7 @@ create_access (tree expr, gimple *stmt, bool write) > access->type = TREE_TYPE (expr); > access->write = write; > access->grp_unscalarizable_region = unscalarizable_region; > + access->grp_same_access_path = true; > access->stmt = stmt; > access->reverse = reverse; > > @@ -1522,6 +1524,10 @@ build_accesses_from_assign (gimple *stmt) > racc = build_access_from_expr_1 (rhs, stmt, false); > lacc = build_access_from_expr_1 (lhs, stmt, true); > > + bool tbaa_hazard > + = !types_equal_for_same_type_for_tbaa_p (TREE_TYPE (lhs), TREE_TYPE > (rhs), > + sra_mode == SRA_MODE_EARLY_INTRA); > + > if (lacc) > { > lacc->grp_assignment_write = 1; > @@ -1536,6 +1542,8 @@ build_accesses_from_assign (gimple *stmt) > bitmap_set_bit (cannot_scalarize_away_bitmap, > DECL_UID (lacc->base)); > } > + if (tbaa_hazard) > + lacc->grp_same_access_path = false; > } > > if (racc) > @@ -1555,6 +1563,8 @@ build_accesses_from_assign (gimple *stmt) > } > if (storage_order_barrier_p (lhs)) > racc->grp_unscalarizable_region = 1; > + if (tbaa_hazard) > + racc->grp_same_access_path = false; > } > > if (lacc && racc > @@ -2396,7 +2406,7 @@ sort_and_splice_var_accesses (tree var) > bool grp_partial_lhs = access->grp_partial_lhs; > bool first_scalar = is_gimple_reg_type (access->type); > bool unscalarizable_region = access->grp_unscalarizable_region; > - bool grp_same_access_path = true; > + bool grp_same_access_path = access->grp_same_access_path; > bool bf_non_full_precision > = (INTEGRAL_TYPE_P (access->type) > && TYPE_PRECISION (access->type) != access->size > @@ -2432,7 +2442,8 @@ sort_and_splice_var_accesses (tree var) > return NULL; > } > > - grp_same_access_path = path_comparable_for_same_access (access->expr); > + if (grp_same_access_path) > + grp_same_access_path = path_comparable_for_same_access (access->expr); > > j = i + 1; > while (j < access_count) > @@ -2496,7 +2507,8 @@ sort_and_splice_var_accesses (tree var) > } > > if (grp_same_access_path > - && !same_access_path_p (access->expr, ac2->expr)) > + && (!ac2->grp_same_access_path > + || !same_access_path_p (access->expr, ac2->expr))) > grp_same_access_path = false; > > ac2->group_representative = access; > diff --git a/gcc/tree-ssa-alias-compare.h b/gcc/tree-ssa-alias-compare.h > index 4c4856c9124..0d48cb160f9 100644 > --- a/gcc/tree-ssa-alias-compare.h > +++ b/gcc/tree-ssa-alias-compare.h > @@ -40,4 +40,7 @@ class ao_compare : public operand_compare > inchash::hash &hstate); > }; > > +bool types_equal_for_same_type_for_tbaa_p (tree type1, tree type2, > + bool lto_streaming_safe); > + > #endif > diff --git a/gcc/tree-ssa-alias.cc b/gcc/tree-ssa-alias.cc > index e93d5187d50..914daea10fc 100644 > --- a/gcc/tree-ssa-alias.cc > +++ b/gcc/tree-ssa-alias.cc > @@ -4167,7 +4167,7 @@ attr_fnspec::verify () > /* Return ture if TYPE1 and TYPE2 will always give the same answer > when compared wit hother types using same_type_for_tbaa_p. */ > > -static bool > +bool > types_equal_for_same_type_for_tbaa_p (tree type1, tree type2, > bool lto_streaming_safe) > { > -- 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)