From patchwork Sun Dec 17 17:42:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Uecker X-Patchwork-Id: 1877122 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=tugraz.at header.i=@tugraz.at header.a=rsa-sha256 header.s=mailrelay header.b=B4Br7ikc; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4StVfR4kDCz20LT for ; Mon, 18 Dec 2023 04:42:55 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A60A6385800F for ; Sun, 17 Dec 2023 17:42:53 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mailrelay.tugraz.at (mailrelay.tugraz.at [129.27.2.202]) by sourceware.org (Postfix) with ESMTPS id E03573858288 for ; Sun, 17 Dec 2023 17:42:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E03573858288 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=tugraz.at Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=tugraz.at ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E03573858288 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=129.27.2.202 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1702834963; cv=none; b=m8zyO6FlrGcV8LsGqzoxzRkFdRZxNUEJh4WedBBlacnAfKDppryI8bTiG75mgYqbuKUH98wjHMw59ill5TSGXuWv99SBA8kno+EMa7f7vpViO8U/j6rRbgj/xT2st5p8qfntn/Nj78obMTyGpmjweZ05qXWUBOiqcJNMbOZFGFA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1702834963; c=relaxed/simple; bh=VD2ppu7N2xO5abXMYyRr7hq18v4FA5Kq3xG9S5SlGJk=; h=DKIM-Signature:Message-ID:Subject:From:To:Date:MIME-Version; b=x6MaVMtYHTG9O6dy71SXOdkXffH4oo6LpDvdKaKeMvrbxoorDYI+6hlbzFv7vERK2Aga75Sa6xYe85T/f3FFf1ayV/U6g56hPtxlsuklzl8wF6KMcY5RIbEAXMmBRGBNW9X0AoJ1LcyF8tPW9gE06SpchDOEPeplii07MXYLGZE= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from vra-173-234.tugraz.at (vra-173-234.tugraz.at [129.27.173.234]) by mailrelay.tugraz.at (Postfix) with ESMTPSA id 4StVdy1Pr4z1LLyr; Sun, 17 Dec 2023 18:42:29 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 mailrelay.tugraz.at 4StVdy1Pr4z1LLyr DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tugraz.at; s=mailrelay; t=1702834950; bh=nIH1rbr+GOLQ5Gvuc8WyZPm/hlfidUJEisqp82cbBok=; h=Subject:From:To:Cc:Date:In-Reply-To:References:From; b=B4Br7ikcyHKoWigQQ0XqvaCidGE4538xB+GWgyPwf/igPyEm/OypnEyhyzjhT0DQB Zj4N6bBG5UjqXOmgKwuEQX9h+HS6fbrS/BvG4+c4v3tDSLGKfM/bnKSljiOi3PWC43 0UZH8cL9afgqb/eM5+4g9qxZrzkyTLDuL+jblQP4= Message-ID: <4e466b016a63f97abf38094d1ec601c8c99f205a.camel@tugraz.at> Subject: [V5] [C PATCH 3/4] c23: aliasing of compatible tagged types From: Martin Uecker To: gcc-patches@gcc.gnu.org Cc: Joseph Myers Date: Sun, 17 Dec 2023 18:42:29 +0100 In-Reply-To: <02a9b94e4d653b6f1b9f89a1b62187f46e871738.camel@tugraz.at> References: <02a9b94e4d653b6f1b9f89a1b62187f46e871738.camel@tugraz.at> User-Agent: Evolution 3.46.4-2 MIME-Version: 1.0 X-TUG-Backscatter-control: G/VXY7/6zeyuAY/PU2/0qw X-Spam-Scanner: SpamAssassin 3.003001 X-Spam-Score-relay: -1.9 X-Scanned-By: MIMEDefang 2.74 on 129.27.10.117 X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org 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 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 typedefs_seen; }; + +/* Hash table for structs and unions. */ +struct c_struct_hasher : ggc_ptr_hash +{ + 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_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::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; +} + +