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 <[email protected]>
>
> 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 <[email protected]>
>
> 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 <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Ivo Totev, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)