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. 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/c2x-tag-2.c: Remove xfail. * gcc.dg/c2x-tag-6.c: Remove xfail. * gcc.dg/c2x-tag-alias-1.c: New test. * gcc.dg/c2x-tag-alias-2.c: New test. * gcc.dg/c2x-tag-alias-3.c: New test. * gcc.dg/c2x-tag-alias-4.c: New test. * gcc.dg/c2x-tag-alias-5.c: New test. * gcc.dg/c2x-tag-alias-6.c: New test. * gcc.dg/c2x-tag-alias-7.c: New test. * gcc.dg/c2x-tag-alias-8.c: New test. --- gcc/c/c-decl.cc | 48 +++++++++++++ gcc/c/c-objc-common.cc | 5 ++ gcc/c/c-tree.h | 1 + gcc/c/c-typeck.cc | 31 ++++++++ gcc/testsuite/gcc.dg/c2x-tag-2.c | 2 +- gcc/testsuite/gcc.dg/c2x-tag-6.c | 2 +- gcc/testsuite/gcc.dg/c2x-tag-alias-1.c | 48 +++++++++++++ gcc/testsuite/gcc.dg/c2x-tag-alias-2.c | 73 +++++++++++++++++++ gcc/testsuite/gcc.dg/c2x-tag-alias-3.c | 48 +++++++++++++ gcc/testsuite/gcc.dg/c2x-tag-alias-4.c | 73 +++++++++++++++++++ gcc/testsuite/gcc.dg/c2x-tag-alias-5.c | 30 ++++++++ gcc/testsuite/gcc.dg/c2x-tag-alias-6.c | 77 ++++++++++++++++++++ gcc/testsuite/gcc.dg/c2x-tag-alias-7.c | 98 ++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/c2x-tag-alias-8.c | 90 +++++++++++++++++++++++ 14 files changed, 624 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-1.c create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-2.c create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-3.c create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-4.c create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-5.c create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-6.c create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-7.c create mode 100644 gcc/testsuite/gcc.dg/c2x-tag-alias-8.c diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index b514e8a35ee..2137ba8b845 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -603,6 +603,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; @@ -9503,6 +9533,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_isoc2x) + { + 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 e4aed61ed00..992225bbb29 100644 --- a/gcc/c/c-objc-common.cc +++ b/gcc/c/c-objc-common.cc @@ -389,6 +389,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 511fd9ee0e5..1a8e8f072bd 100644 --- a/gcc/c/c-tree.h +++ b/gcc/c/c-tree.h @@ -748,6 +748,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 int 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 2b79cbba950..2489fa1e3d1 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -1041,6 +1041,7 @@ struct comptypes_data { bool different_types_p; bool warning_needed; bool anon_field; + bool equiv; const struct tagged_tu_seen_cache* cache; }; @@ -1101,6 +1102,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. */ + +int +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 @@ -1227,6 +1243,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 1; /* Sizes must match unless one is missing or variable. */ if (d1 == NULL_TREE || d2 == NULL_TREE || d1 == d2) return true; @@ -1443,6 +1462,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 0; + for (s1 = TYPE_FIELDS (t1), s2 = TYPE_FIELDS (t2); s1 && s2; s1 = DECL_CHAIN (s1), s2 = DECL_CHAIN (s2)) @@ -1462,6 +1484,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 0; } return true; diff --git a/gcc/testsuite/gcc.dg/c2x-tag-2.c b/gcc/testsuite/gcc.dg/c2x-tag-2.c index a68392e1fab..e28c2b5eea2 100644 --- a/gcc/testsuite/gcc.dg/c2x-tag-2.c +++ b/gcc/testsuite/gcc.dg/c2x-tag-2.c @@ -1,4 +1,4 @@ -/* { dg-do { compile xfail { *-*-* } } } +/* { dg-do compile } * { dg-options "-std=c2x" } */ diff --git a/gcc/testsuite/gcc.dg/c2x-tag-6.c b/gcc/testsuite/gcc.dg/c2x-tag-6.c index bf7cfb342d4..95a04bf9b0e 100644 --- a/gcc/testsuite/gcc.dg/c2x-tag-6.c +++ b/gcc/testsuite/gcc.dg/c2x-tag-6.c @@ -1,5 +1,5 @@ /* - * { dg-do { run xfail { "*-*-*" } } } + * { dg-do run } * { dg-options "-std=c2x" } */ diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-1.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-1.c new file mode 100644 index 00000000000..dfa1f084743 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-1.c @@ -0,0 +1,48 @@ +/* + * { dg-do run } + * { dg-options "-std=c2x -O2" } + */ + + +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 (A == test_bar(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-2.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-2.c new file mode 100644 index 00000000000..1354adb3483 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-2.c @@ -0,0 +1,73 @@ +/* + * { dg-do run } + * { dg-options "-std=c2x -O2" } + */ + + +struct foo { int x; }; + +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; +} + +int test_foo2(struct foo* a, void* b) +{ + a->x = 1; + + struct fox { int x; }* p = b; + p->x = 2; + + return a->x; +} + +enum bar { A = 1, B = 3, C = 5, D = 9 }; + +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; +} + +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 (A == test_bar1(&z, &z)) + __builtin_abort(); + + if (A == test_bar2(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-3.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-3.c new file mode 100644 index 00000000000..ec086dd52ab --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-3.c @@ -0,0 +1,48 @@ +/* + * { dg-do run } + * { dg-options "-std=c2x -flto -O2" } + */ + + +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 (A == test_bar(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-4.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-4.c new file mode 100644 index 00000000000..537a1874454 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-4.c @@ -0,0 +1,73 @@ +/* + * { dg-do run } + * { dg-options "-std=c2x -flto -O2" } + */ + + +struct foo { int x; }; + +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; +} + +int test_foo2(struct foo* a, void* b) +{ + a->x = 1; + + struct fox { int x; }* p = b; + p->x = 2; + + return a->x; +} + +enum bar { A = 1, B = 3, C = 5, D = 9 }; + +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; +} + +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 (A == test_bar1(&z, &z)) + __builtin_abort(); + + if (A == test_bar2(&z, &z)) + __builtin_abort(); + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-5.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-5.c new file mode 100644 index 00000000000..061676c2672 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-5.c @@ -0,0 +1,30 @@ +/* { dg-do run } + * { dg-options "-std=c2x -O2" } + */ + +// not sure this is wise, but this was already like thi sbefore + +typedef struct { int x; } foo_t; + +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/c2x-tag-alias-6.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-6.c new file mode 100644 index 00000000000..3617a159d7c --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-6.c @@ -0,0 +1,77 @@ +/* + * { dg-do run } + * { dg-options "-std=c2x -O2" } + */ + + +/* We check that we tolerate differences for + * optimization. + */ + + +struct bar0 { int x; int f[3]; int y; }; + +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; +} + + + +struct bar1 { int x; int (*f)[3]; }; + +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]; }; + +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; +} + + + +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(); + + + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/c2x-tag-alias-7.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-7.c new file mode 100644 index 00000000000..0c1114e2b70 --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-7.c @@ -0,0 +1,98 @@ +/* + * { dg-do run } + * { dg-options "-std=c2x -O2" } + */ + + + +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 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 test_bar3(struct bar* a, void* b) +{ + a->x = 1; + + struct bar { int x; int f[1]; }* p = b; + struct bar* q = a; /* { dg-warning "incompatible" } */ + p->x = 2; + + return a->x; +} + + +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]; }; + + +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_bar1(&z, &z)) + __builtin_abort(); + + if (2 != test_bar2(&z, &z)) + __builtin_abort(); +#if 0 + if (1 != test_bar3(&z, &z)) + __builtin_abort(); +#endif + 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/c2x-tag-alias-8.c b/gcc/testsuite/gcc.dg/c2x-tag-alias-8.c new file mode 100644 index 00000000000..e3f6173005f --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-tag-alias-8.c @@ -0,0 +1,90 @@ +/* + * { dg-do run } + * { dg-options "-std=c2x -O2" } + */ + + +/* We check that we tolerate differences for + * optimization. + */ + +struct bar1 { int x; enum A1 { X1 = 1 } f; }; + +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; }; + +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; }; + +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; }; + +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; +} + + -- 2.30.2
[C PATCH 5/6] c23: aliasing of compatible tagged types
Martin Uecker via Gcc-patches Sat, 26 Aug 2023 09:25:37 -0700
- c23 type compatibility rules, v2 Martin Uecker via Gcc-patches
- [C PATCH 1/6] c: reorganize recursive ... Martin Uecker via Gcc-patches
- Re: [C PATCH 1/6] c: reorganize re... Joseph Myers
- [C PATCH 1/6 v2] c: reorganize... Martin Uecker via Gcc-patches
- Re: [C PATCH 1/6 v2] c: re... Joseph Myers
- [C PATCH 2/6] c23: recursive type check... Martin Uecker via Gcc-patches
- [C PATCH 3/6] c23: tag compatibility ru... Martin Uecker via Gcc-patches
- [C PATCH 4/6] c23: tag compatibility ru... Martin Uecker via Gcc-patches
- [C PATCH 5/6] c23: aliasing of compatib... Martin Uecker via Gcc-patches
- [C PATCH 6/6] c23: construct composite ... Martin Uecker via Gcc-patches
- [C PATCH] c: flag for tag compatibility... Martin Uecker via Gcc-patches