Hi Richard,
do you see an issue with the following approach? Calling
record_component_aliases for all versions? (should be rare
that there is more than one version.)
Bootstrapped and regression tested on aarch64.
(and running on x86_64).
Martin
Given the following two types, the C FE assigns the same
TYPE_CANONICAL to both struct bar, because it treats pointer to
tagged types with the same type as compatible (in this context).
struct foo { int y; };
struct bar { struct foo *c; }
struct foo { long y; };
struct bar { struct foo *c; }
get_alias_set records the components of aggregate types, but only
considers the components of the canonical version. We need to record
the components of all such variations which we now do incrementally
as we discover these types.
PR c/122572
gcc/ChangeLog:
* gcc/alias.cc (get_alias_set): Record components of all
versions with the same TYPE_CANONICAL.
gcc/testsuite/ChangeLog:
* gcc.dg/struct-alias-2.c: New test.
---
gcc/alias.cc | 32 +++++++-
gcc/testsuite/gcc.dg/struct-alias-2.c | 108 ++++++++++++++++++++++++++
2 files changed, 138 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/struct-alias-2.c
diff --git a/gcc/alias.cc b/gcc/alias.cc
index bce026200a1..95f1f017989 100644
--- a/gcc/alias.cc
+++ b/gcc/alias.cc
@@ -923,6 +923,9 @@ get_alias_set (tree t)
&& TYPE_TYPELESS_STORAGE (t))
return 0;
+ /* Remember the original type before replace with its canonical version. */
+ tree torig = t;
+
/* Always use the canonical type as well. If this is a type that
requires structural comparisons to identify compatible types
use alias set zero. */
@@ -954,7 +957,21 @@ get_alias_set (tree t)
/* If this is a type with a known alias set, return it. */
gcc_checking_assert (t == TYPE_MAIN_VARIANT (t));
if (TYPE_ALIAS_SET_KNOWN_P (t))
- return TYPE_ALIAS_SET (t);
+ {
+ /* The C FE for C23 sometimes creates TYPE_CANONICAL for aggregate types
+ that contain pointers to incompatible tagged types. In this case the
+ types which we had seen before may not have had exactly the same
+ components. We do need to record the components of the new type
+ we haven't seen before. */
+ if (!in_lto_p && AGGREGATE_TYPE_P(t) && !TYPE_ALIAS_SET_KNOWN_P (torig))
+ {
+ gcc_assert (torig != t);
+ gcc_assert (t == TYPE_CANONICAL (torig));
+ TYPE_ALIAS_SET (torig) = TYPE_ALIAS_SET (t);
+ record_component_aliases (torig);
+ }
+ return TYPE_ALIAS_SET (t);
+ }
/* We don't want to set TYPE_ALIAS_SET for incomplete types. */
if (!COMPLETE_TYPE_P (t))
@@ -1110,7 +1127,7 @@ get_alias_set (tree t)
/* Assign the alias set to both p and t.
We cannot call get_alias_set (p) here as that would trigger
infinite recursion when p == t. In other cases it would just
- trigger unnecesary legwork of rebuilding the pointer again. */
+ trigger unnecessary legwork of rebuilding the pointer again. */
gcc_checking_assert (p == TYPE_MAIN_VARIANT (p));
if (TYPE_ALIAS_SET_KNOWN_P (p))
set = TYPE_ALIAS_SET (p);
@@ -1147,6 +1164,17 @@ get_alias_set (tree t)
if (AGGREGATE_TYPE_P (t) || TREE_CODE (t) == COMPLEX_TYPE)
record_component_aliases (t);
+ /* The C FE for C23 sometimes creates TYPE_CANONICAL for aggregate types
+ that contain pointers to incompatible tagged types. In this case
+ TYPE_CANONICAL may not have exactly the same components as the original
+ type. We do need to record the components of both versions. */
+ if (!in_lto_p && torig != t && AGGREGATE_TYPE_P(t))
+ {
+ TYPE_ALIAS_SET (torig) = set;
+ record_component_aliases (torig);
+ }
+
+
/* We treat pointer types specially in alias_set_subset_of. */
if (POINTER_TYPE_P (t) && set)
{
diff --git a/gcc/testsuite/gcc.dg/struct-alias-2.c
b/gcc/testsuite/gcc.dg/struct-alias-2.c
new file mode 100644
index 00000000000..114a1275594
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/struct-alias-2.c
@@ -0,0 +1,108 @@
+/* { dg-do run } */
+/* { dg-options "-O2 -std=c23" } */
+
+
+/* Based on the submitted test case for PR123356 but using types with
+ * tags and moving the second version into another scope. */
+
+struct foo { long x; };
+
+void f()
+{
+ struct foo { };
+ struct bar { struct foo *c; };
+ union baz { struct foo *c; };
+ struct arr { struct foo *c[1]; };
+}
+
+
+void f1()
+{
+ struct foo { };
+ struct bar { struct foo *c; };
+ union baz { struct foo *c; };
+ struct arr { struct foo *c[1]; };
+}
+
+struct bar { struct foo *c; };
+union baz { struct foo *c; };
+struct arr { struct foo *c[1]; };
+
+void f2()
+{
+ struct foo { int y; };
+ struct bar { struct foo *c; };
+ union baz { struct foo *c; };
+ struct arr { struct foo *c[1]; };
+}
+
+__attribute__((noinline))
+struct foo * g1(struct bar *B, struct bar *Q)
+{
+ struct bar t = *B;
+ *B = *Q;
+ *Q = t;
+ return B->c;
+}
+
+__attribute__((noinline))
+struct foo * g2(union baz *B, union baz *Q)
+{
+ union baz t = *B;
+ *B = *Q;
+ *Q = t;
+ return B->c;
+}
+
+__attribute__((noinline))
+struct foo { long x; } *
+ g3(struct bar { struct foo { long x; } *c; } *B,
+ struct bar { struct foo { long x; } *c; } *Q)
+{
+ struct bar t = *B;
+ *B = *Q;
+ *Q = t;
+ return B->c;
+}
+
+struct foo * g4(struct arr *B,
+ struct arr *Q)
+{
+ struct arr t = *B;
+ *B = *Q;
+ *Q = t;
+ return B->c[0];
+}
+
+int main()
+{
+ struct foo Bc = { };
+ struct foo Qc = { };
+
+ struct bar B = { &Bc };
+ struct bar Q = { &Qc };
+
+ if (g1(&B, &Q) != &Qc)
+ __builtin_abort();
+
+ union baz Bu = { &Bc };
+ union baz Qu = { &Qc };
+
+ if (g2(&Bu, &Qu) != &Qc)
+ __builtin_abort();
+
+ struct bar B2 = { &Bc };
+ struct bar Q2 = { &Qc };
+
+ if (g3(&B2, &Q2) != &Qc)
+ __builtin_abort();
+
+ struct arr Ba = { &Bc };
+ struct arr Qa = { &Qc };
+
+ if (g4(&Ba, &Qa) != &Qc)
+ __builtin_abort();
+
+ return 0;
+}
+
--
2.43.7