diff mbox series

[V5,C,3/4] c23: aliasing of compatible tagged types

Message ID 4e466b016a63f97abf38094d1ec601c8c99f205a.camel@tugraz.at
State New
Headers show
Series None | expand

Commit Message

Martin Uecker Dec. 17, 2023, 5:42 p.m. UTC
Tell the backend which types are equivalent by setting
TYPE_CANONICAL to one struct in the set of equivalent
structs.  Structs are considered equivalent by ignoring
all sizes of arrays nested in types below field level.

The following two structs are incompatible and lvalues
with these types can be assumed not to alias:

 struct foo { int a[3]; };
 struct foo { int a[4]; };

The following two structs are also incompatible, but
will get the same TYPE_CANONICAL and it is then not
exploited that lvalues with those types can not alias:

 struct bar { int (*p)[3]; };
 struct bar { int (*p)[4]; };

The reason is that both are compatible to

 struct bar { int (*p)[]; };

and therefore are in the same equivalence class.  For
the same reason all enums with the same underyling type
are in the same equivalence class.  Tests are added
for the expected aliasing behavior with optimization.

gcc/c:
	* c-decl.cc (c_struct_hasher): Hash stable for struct
	types.
	(c_struct_hasher::hash, c_struct_hasher::equal): New
	functions.
	(finish_struct): Set TYPE_CANONICAL to first struct in
	equivalence class.
	* c-objc-common.cc (c_get_alias_set): Let structs or
	unions with variable size alias anything.
	* c-tree.h (comptypes_equiv): New prototype.
	* c-typeck.cc (comptypes_equiv): New function.
	(comptypes_internal): Implement equivalence mode.
	(tagged_types_tu_compatible): Implement equivalence mode.

gcc/testsuite:
	* gcc.dg/c23-tag-2.c: Activate.
	* gcc.dg/c23-tag-5.c: Activate.
	* gcc.dg/c23-tag-alias-1.c: New test.
	* gcc.dg/c23-tag-alias-2.c: New test.
	* gcc.dg/c23-tag-alias-3.c: New test.
	* gcc.dg/c23-tag-alias-4.c: New test.
	* gcc.dg/c23-tag-alias-5.c: New test.
	* gcc.dg/gnu23-tag-alias-1.c: New test.
	* gcc.dg/gnu23-tag-alias-2.c: New test.
	* gcc.dg/gnu23-tag-alias-3.c: New test.
	* gcc.dg/gnu23-tag-alias-4.c: New test.
	* gcc.dg/gnu23-tag-alias-5.c: New test.
	* gcc.dg/gnu23-tag-alias-6.c: New test.
	* gcc.dg/gnu23-tag-alias-7.c: New test.
---
 gcc/c/c-decl.cc                          |  51 ++++++++++-
 gcc/c/c-objc-common.cc                   |   5 ++
 gcc/c/c-tree.h                           |   1 +
 gcc/c/c-typeck.cc                        |  31 +++++++
 gcc/testsuite/gcc.dg/c23-tag-2.c         |   2 +-
 gcc/testsuite/gcc.dg/c23-tag-5.c         |   2 +-
 gcc/testsuite/gcc.dg/c23-tag-alias-1.c   |  49 +++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-2.c   |  50 +++++++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-3.c   |  32 +++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-4.c   |  32 +++++++
 gcc/testsuite/gcc.dg/c23-tag-alias-5.c   |  36 ++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c |  33 +++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c |  85 ++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c |  83 ++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c |  36 ++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c | 107 +++++++++++++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c |  60 +++++++++++++
 gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c |  93 ++++++++++++++++++++
 18 files changed, 785 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-1.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-2.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-3.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-4.c
 create mode 100644 gcc/testsuite/gcc.dg/c23-tag-alias-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
 create mode 100644 gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c

Comments

Joseph Myers Dec. 19, 2023, 10:02 p.m. UTC | #1
On Sun, 17 Dec 2023, Martin Uecker wrote:

> +/* While tese tests check that incompatible definitions
> + * of enums can alias.  */

s/tese/these/

Patch 3 is OK with that fix.
diff mbox series

Patch

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 26188aa225e..6639ec35e5f 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -634,6 +634,36 @@  public:
   auto_vec<tree> typedefs_seen;
 };
 
+
+/* Hash table for structs and unions.  */
+struct c_struct_hasher : ggc_ptr_hash<tree_node>
+{
+  static hashval_t hash (tree t);
+  static bool equal (tree, tree);
+};
+
+/* Hash an RECORD OR UNION.  */
+hashval_t
+c_struct_hasher::hash (tree type)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (TREE_CODE (type));
+  hstate.add_object (TYPE_NAME (type));
+
+  return hstate.end ();
+}
+
+/* Compare two RECORD or UNION types.  */
+bool
+c_struct_hasher::equal (tree t1,  tree t2)
+{
+  return comptypes_equiv_p (t1, t2);
+}
+
+/* All tagged typed so that TYPE_CANONICAL can be set correctly.  */
+static GTY (()) hash_table<c_struct_hasher> *c_struct_htab;
+
 /* Information for the struct or union currently being parsed, or
    NULL if not parsing a struct or union.  */
 static class c_struct_parse_info *struct_parse_info;
@@ -8713,7 +8743,8 @@  parser_xref_tag (location_t loc, enum tree_code code, tree name,
   ref = lookup_tag (code, name, has_enum_type_specifier, &refloc);
 
   /* If the visble type is still being defined, see if there is
-     an earlier definition (which may be complete).  */
+     an earlier definition (which may be complete).  We do not
+     have to loop because nested redefinitions are not allowed.  */
   if (flag_isoc23 && ref && C_TYPE_BEING_DEFINED (ref))
     {
       tree vis = previous_tag (ref);
@@ -9661,6 +9692,24 @@  finish_struct (location_t loc, tree t, tree fieldlist, tree attributes,
 
   C_TYPE_BEING_DEFINED (t) = 0;
 
+  /* Set type canonical based on equivalence class.  */
+  if (flag_isoc23)
+    {
+      if (NULL == c_struct_htab)
+	c_struct_htab = hash_table<c_struct_hasher>::create_ggc (61);
+
+      hashval_t hash = c_struct_hasher::hash (t);
+
+      tree *e = c_struct_htab->find_slot_with_hash (t, hash, INSERT);
+      if (*e)
+	TYPE_CANONICAL (t) = *e;
+      else
+	{
+	  TYPE_CANONICAL (t) = t;
+	  *e = t;
+	}
+    }
+
   tree incomplete_vars = C_TYPE_INCOMPLETE_VARS (TYPE_MAIN_VARIANT (t));
   for (x = TYPE_MAIN_VARIANT (t); x; x = TYPE_NEXT_VARIANT (x))
     {
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc
index 53eda7fa707..a3946275b23 100644
--- a/gcc/c/c-objc-common.cc
+++ b/gcc/c/c-objc-common.cc
@@ -422,6 +422,11 @@  c_get_alias_set (tree t)
   if (TREE_CODE (t) == ENUMERAL_TYPE)
     return get_alias_set (ENUM_UNDERLYING_TYPE (t));
 
+  /* Structs with variable size can alias different incompatible
+     structs.  Let them alias anything.   */
+  if (RECORD_OR_UNION_TYPE_P (t) && C_TYPE_VARIABLE_SIZE (t))
+    return 0;
+
   return c_common_get_alias_set (t);
 }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 54f1353ad34..02a09e53b05 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -759,6 +759,7 @@  extern tree require_complete_type (location_t, tree);
 extern bool same_translation_unit_p (const_tree, const_tree);
 extern int comptypes (tree, tree);
 extern bool comptypes_same_p (tree, tree);
+extern bool comptypes_equiv_p (tree, tree);
 extern int comptypes_check_different_types (tree, tree, bool *);
 extern int comptypes_check_enum_int (tree, tree, bool *);
 extern bool c_mark_addressable (tree, bool = false);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 0a9e8980c79..4d3079156ba 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -1063,6 +1063,7 @@  struct comptypes_data {
   bool different_types_p;
   bool warning_needed;
   bool anon_field;
+  bool equiv;
 
   const struct tagged_tu_seen_cache* cache;
 };
@@ -1123,6 +1124,21 @@  comptypes_check_different_types (tree type1, tree type2,
 
   return ret ? (data.warning_needed ? 2 : 1) : 0;
 }
+
+
+/* Like comptypes, but if it returns nonzero for struct and union
+   types considered equivalent for aliasing purposes.  */
+
+bool
+comptypes_equiv_p (tree type1, tree type2)
+{
+  struct comptypes_data data = { };
+  data.equiv = true;
+  bool ret = comptypes_internal (type1, type2, &data);
+
+  return ret;
+}
+
 
 /* Return true if TYPE1 and TYPE2 are compatible types for assignment
    or various other operations.  If they are compatible but a warning may
@@ -1250,6 +1266,9 @@  comptypes_internal (const_tree type1, const_tree type2,
 
 	if ((d1 == NULL_TREE) != (d2 == NULL_TREE))
 	  data->different_types_p = true;
+	/* Ignore size mismatches.  */
+	if (data->equiv)
+	  return true;
 	/* Sizes must match unless one is missing or variable.  */
 	if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2)
 	  return true;
@@ -1467,6 +1486,9 @@  tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 	if (list_length (TYPE_FIELDS (t1)) != list_length (TYPE_FIELDS (t2)))
 	  return false;
 
+	if (data->equiv && (C_TYPE_VARIABLE_SIZE (t1) || C_TYPE_VARIABLE_SIZE (t2)))
+	  return false;
+
 	for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2);
 	     s1 && s2;
 	     s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2))
@@ -1486,6 +1508,15 @@  tagged_types_tu_compatible_p (const_tree t1, const_tree t2,
 		&& simple_cst_equal (DECL_FIELD_BIT_OFFSET (s1),
 				     DECL_FIELD_BIT_OFFSET (s2)) != 1)
 	      return false;
+
+	    tree st1 = TYPE_SIZE (TREE_TYPE (s1));
+	    tree st2 = TYPE_SIZE (TREE_TYPE (s2));
+
+	    if (data->equiv
+		&& st1 && TREE_CODE (st1) == INTEGER_CST
+		&& st2 && TREE_CODE (st2) == INTEGER_CST
+		&& !tree_int_cst_equal (st1, st2))
+	     return false;
 	  }
 	return true;
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-2.c b/gcc/testsuite/gcc.dg/c23-tag-2.c
index 5dd4a21e9df..444605a93c6 100644
--- a/gcc/testsuite/gcc.dg/c23-tag-2.c
+++ b/gcc/testsuite/gcc.dg/c23-tag-2.c
@@ -1,4 +1,4 @@ 
-/* { dg-do compile { target { ! "*-*-*" } } }
+/* { dg-do compile }
  * { dg-options "-std=c23" }
  */
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-5.c b/gcc/testsuite/gcc.dg/c23-tag-5.c
index ff7bbd662aa..90111266e06 100644
--- a/gcc/testsuite/gcc.dg/c23-tag-5.c
+++ b/gcc/testsuite/gcc.dg/c23-tag-5.c
@@ -1,4 +1,4 @@ 
-/* { dg-do run { target { ! "*-*-*" } } }
+/* { dg-do run }
  * { dg-options "-std=c23 -fpermissive" }
  */
 
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-1.c b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c
new file mode 100644
index 00000000000..c92f942af88
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-1.c
@@ -0,0 +1,49 @@ 
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* These tests check that redefinitions of tagged
+   types can alias the original definitions.  */
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-2.c b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
new file mode 100644
index 00000000000..64ff67d8552
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-2.c
@@ -0,0 +1,50 @@ 
+/* { dg-do run }
+ * { dg-options "-std=c23 -flto -O2" }
+ */
+
+/* These tests check that compatible definitions of
+   tagged types can alias the original definitions
+   with LTO.  */
+
+struct foo { int x; };
+
+int test_foo(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+enum bar { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (2 != test_foo(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-3.c b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c
new file mode 100644
index 00000000000..b9fe6f3b407
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-3.c
@@ -0,0 +1,32 @@ 
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+/* These tests check that definitions of enums with 
+ * the same underlying type can alias, even when
+ * they are not compatible.  */
+
+enum bar : long { A = 1, B = 3 };
+
+int test_bar(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum foo : long { C = 2, D = 4 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	enum bar z;
+
+	if (B != test_bar(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-4.c b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c
new file mode 100644
index 00000000000..1d43d0d754d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-4.c
@@ -0,0 +1,32 @@ 
+/* { dg-do run }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+
+/* Here we check that structs with flexible array
+ * members can alias a compatible redefinition.  */
+
+struct bar { int x; int f[]; };
+
+int test_bar1(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar1(&z, &z))
+        	__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/c23-tag-alias-5.c b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
new file mode 100644
index 00000000000..f5cfad161a5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/c23-tag-alias-5.c
@@ -0,0 +1,36 @@ 
+/* { dg-do compile }
+ * { dg-options "-std=c23 -O2" }
+ */
+
+/* The structs are incompatible so can be assumed not to
+ * alias, but this is not exploited.  So do not check for 
+ * this below but check the error about incompatibility.  */
+
+struct bar { int x; int f[]; };
+
+int test_bar3(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[1]; }* p = b;
+	struct bar* q = a;			/* { dg-error "incompatible" } */
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int main()
+{
+	struct bar z;
+
+	// allow both results here
+	int r = test_bar3(&z, &z);
+
+	// UB but could be expected to return 1 with optimization
+	// exploiting the UB (not done at time of writing) or 2
+
+	return r;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
new file mode 100644
index 00000000000..c51417f831a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-1.c
@@ -0,0 +1,33 @@ 
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* Check that structs with flexible array member can alias.  */
+
+struct bar { int x; int f[]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	struct bar { int x; int f[0]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
new file mode 100644
index 00000000000..c09c3ca40a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-2.c
@@ -0,0 +1,85 @@ 
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+/* These tests check that incompatible definitions of
+   tagged types can be assumed not to alias and that
+   this is exploited during optimization.  */
+
+struct foo { int x; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+/* While these tests check that incompatible enums can still
+ * alias, although this is not required.   */
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (B != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
new file mode 100644
index 00000000000..a07a1e6fa38
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-3.c
@@ -0,0 +1,83 @@ 
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -flto -O2" }
+ */
+
+/* These tests check that incompatible definitions of
+   tagged types can be assumed not to alias and that
+   this is exploited during optimization with LTO.  */
+
+struct foo { int x; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct foo { int x; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo2(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	struct fox { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+/* While tese tests check that incompatible definitions
+ * of enums can alias.  */
+
+enum bar { A = 1, B = 3, C = 5, D = 9 };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum bar { A = 1, B = 3, C = 6, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(enum bar* a, void* b)
+{
+	*a = A;
+
+	enum baX { A = 1, B = 3, C = 5, D = 9 }* p = b;
+	*p = B;
+
+	return *a;
+}
+
+
+int main()
+{
+	struct foo y;
+
+	if (1 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	if (1 != test_foo2(&y, &y))
+		__builtin_abort();
+
+	enum bar z;
+
+	if (B != test_bar1(&z, &z))
+		__builtin_abort();
+
+	if (B != test_bar2(&z, &z))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
new file mode 100644
index 00000000000..1ea3a883d0c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-4.c
@@ -0,0 +1,36 @@ 
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* This test checks that an incompatible definition of
+ * a tagged type without tag can be assumed not to alias.  
+ * and that this is exploited during optimization.  */
+
+
+// not sure this is wise, but this was already like this before
+
+typedef struct { int x; } foo_t;
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo(foo_t* a, void* b)
+{
+	a->x = 1;
+
+	struct { int x; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+int main()
+{
+	foo_t y;
+
+	if (1 != test_foo(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
new file mode 100644
index 00000000000..5a83397bd6d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-5.c
@@ -0,0 +1,107 @@ 
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+/* This test checks that different field offsets imply
+ * that the types can be assumed not to alias
+ * and that this is exploited during optimization.  */
+
+
+struct bar0 { int x; int f[3]; int y; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar0(struct bar0* a, void* b)
+{
+	a->x = 1;
+
+	struct bar0 { int x; int f[4]; int y; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+/* While these tests check that different structs with different
+ * sizes in arrays pointed to by field members can alias,
+ * even though the types are incompatible.  */
+
+
+struct bar1 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; int (*f)[3]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; int (*f)[4]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+/* This test checks that different structs with pointers to
+ * different compatible arrays types can alias.  */
+
+
+struct bar3 { int x; int (*f)[3]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar3(struct bar3* a, void* b)
+{
+	a->x = 1;
+
+	struct bar3 { int x; int (*f)[]; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+
+int main()
+{
+	// control
+
+	struct bar0 z0;
+
+	if (1 != test_bar0(&z0, &z0))
+		__builtin_abort();
+
+	// this could be different
+	struct bar1 z1;
+
+	if (2 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (2 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+	struct bar3 z3;
+
+	if (2 != test_bar3(&z3, &z3))
+		__builtin_abort();
+
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
new file mode 100644
index 00000000000..78d2abf2ed9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-6.c
@@ -0,0 +1,60 @@ 
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+
+/* Here we check that struct with a variable size (GNU extension)
+ * can alias a struct with a flexible array member or a struct with a
+ * fixed size array as last element.  */
+
+struct bar { int x; int f[]; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar4(struct bar* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct bar { int x; int f[n]; }* p = b;
+	struct bar* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct foo { int x; int f[3]; };
+
+
+[[gnu::noinline,gnu::noipa]]
+int test_foo1(struct foo* a, void* b)
+{
+	a->x = 1;
+
+	int n = 3;
+	struct foo { int x; int f[n]; }* p = b;
+	struct foo* q = a;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar z;
+
+	if (2 != test_bar4(&z, &z))
+		__builtin_abort();
+
+	struct foo y;
+
+	if (2 != test_foo1(&y, &y))
+		__builtin_abort();
+
+	return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c b/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c
new file mode 100644
index 00000000000..d3fc4bd57e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gnu23-tag-alias-7.c
@@ -0,0 +1,93 @@ 
+/* { dg-do run }
+ * { dg-options "-std=gnu23 -O2" }
+ */
+
+
+/* We check that the incompatible enums as fields lead to
+ * incompatible types that can be assumed not to alias
+ * and that this is exploited during optimization.  */
+
+struct bar1 { int x; enum A1 { X1 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar1(struct bar1* a, void* b)
+{
+	a->x = 1;
+
+	struct bar1 { int x; enum A1 { X1 = 2 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar2 { int x; enum A2 { X2 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar2(struct bar2* a, void* b)
+{
+	a->x = 1;
+
+	struct bar2 { int x; enum B2 { X2 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+struct bar3 { int x; enum A3 { X3 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar3(struct bar3* a, void* b)
+{
+	a->x = 1;
+
+	struct bar3 { int x; enum A3 { Y3 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+struct bar4 { int x; enum { Z4 = 1 } f; };
+
+[[gnu::noinline,gnu::noipa]]
+int test_bar4(struct bar4* a, void* b)
+{
+	a->x = 1;
+
+	struct bar4 { int x; enum { Z4 = 1 } f; }* p = b;
+	p->x = 2;
+
+	return a->x;
+}
+
+
+
+int main()
+{
+	struct bar1 z1;
+
+	if (1 != test_bar1(&z1, &z1))
+		__builtin_abort();
+
+	struct bar2 z2;
+
+	if (1 != test_bar2(&z2, &z2))
+		__builtin_abort();
+
+	struct bar3 z3;
+
+	if (1 != test_bar3(&z3, &z3))
+		__builtin_abort();
+
+	struct bar4 z4;
+
+	if (1 != test_bar4(&z4, &z4))
+		__builtin_abort();
+
+	return 0;
+}
+
+