From: Jason Merrill Date: Wed, 3 Oct 2018 15:56:29 +0000 (-0400) Subject: Implement P0840, language support for empty objects. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=bedf03a298f87b43f02eaee57542fbd10228ec08;p=gcc.git Implement P0840, language support for empty objects. The [[no_unique_address]] attribute on a non-static data member enables the equivalent of the empty base optimization. gcc/cp/ * tree.c (handle_no_unique_addr_attribute): New. (cxx_attribute_table): Add [[no_unique_address]]. * class.c (field_poverlapping_p): New. (layout_class_type): Check it. Adjust DECL_SIZE of potentially overlapping fields. (layout_empty_base_or_field): Rename from layout_empty_base, handle FIELD_DECL as well. (build_base_field, record_subobject_offsets): Adjust. c-family/ * c-lex.c (c_common_has_attribute): Add no_unique_address. From-SVN: r264813 --- diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 015ca8a2fb9..f2c1f0988f9 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,7 @@ +2018-10-01 Jason Merrill + + * c-lex.c (c_common_has_attribute): Add no_unique_address. + 2018-10-01 Eric Botcazou * c-ada-spec.c (get_underlying_decl): Get to the main type variant. diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c index ac58335cf3a..78a132484fc 100644 --- a/gcc/c-family/c-lex.c +++ b/gcc/c-family/c-lex.c @@ -356,6 +356,8 @@ c_common_has_attribute (cpp_reader *pfile) || is_attribute_p ("nodiscard", attr_name) || is_attribute_p ("fallthrough", attr_name)) result = 201603; + else if (is_attribute_p ("no_unique_address", attr_name)) + result = 20180312; if (result) attr_name = NULL_TREE; } diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 53ff0eb0a27..40fa94fee4e 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,15 @@ +2018-10-03 Jason Merrill + + Implement P0840, language support for empty objects. + * tree.c (handle_no_unique_addr_attribute): New. + (cxx_attribute_table): Add [[no_unique_address]]. + * class.c (field_poverlapping_p): New. + (layout_class_type): Check it. Adjust DECL_SIZE of potentially + overlapping fields. + (layout_empty_base_or_field): Rename from layout_empty_base, handle + FIELD_DECL as well. + (build_base_field, record_subobject_offsets): Adjust. + 2018-10-03 Martin Liska PR gcov-profile/86109 diff --git a/gcc/cp/class.c b/gcc/cp/class.c index 9ca46441871..1789d1ecb70 100644 --- a/gcc/cp/class.c +++ b/gcc/cp/class.c @@ -180,8 +180,6 @@ static tree build_vtable (tree, tree, tree); static void initialize_vtable (tree, vec *); static void layout_nonempty_base_or_field (record_layout_info, tree, tree, splay_tree); -static tree end_of_class (tree, int); -static bool layout_empty_base (record_layout_info, tree, tree, splay_tree); static void accumulate_vtbl_inits (tree, tree, tree, tree, tree, vec **); static void dfs_accumulate_vtbl_inits (tree, tree, tree, tree, tree, @@ -202,7 +200,6 @@ static int record_subobject_offset (tree, tree, splay_tree); static int check_subobject_offset (tree, tree, splay_tree); static int walk_subobject_offsets (tree, subobject_offset_fn, tree, splay_tree, tree, int); -static void record_subobject_offsets (tree, tree, splay_tree, bool); static int layout_conflict_p (tree, tree, splay_tree, int); static int splay_tree_compare_integer_csts (splay_tree_key k1, splay_tree_key k2); @@ -3960,20 +3957,52 @@ walk_subobject_offsets (tree type, return 0; } -/* Record all of the empty subobjects of TYPE (either a type or a - binfo). If IS_DATA_MEMBER is true, then a non-static data member - is being placed at OFFSET; otherwise, it is a base class that is - being placed at OFFSET. */ +/* Return true iff FIELD_DECL DECL is potentially overlapping. */ + +static bool +field_poverlapping_p (tree decl) +{ + /* Base fields are actually potentially overlapping, but C++ bases go through + a different code path based on binfos, and ObjC++ base fields are laid out + in objc-act, so we don't want layout_class_type to mess with them. */ + if (DECL_FIELD_IS_BASE (decl)) + { + gcc_checking_assert (c_dialect_objc ()); + return false; + } + + return lookup_attribute ("no_unique_address", + DECL_ATTRIBUTES (decl)); +} + +/* Record all of the empty subobjects of DECL_OR_BINFO. */ static void -record_subobject_offsets (tree type, - tree offset, - splay_tree offsets, - bool is_data_member) +record_subobject_offsets (tree decl_or_binfo, + splay_tree offsets) { + tree type, offset; + bool overlapping, vbases_p; + + if (DECL_P (decl_or_binfo)) + { + tree decl = decl_or_binfo; + type = TREE_TYPE (decl); + offset = byte_position (decl); + overlapping = field_poverlapping_p (decl); + vbases_p = true; + } + else + { + type = BINFO_TYPE (decl_or_binfo); + offset = BINFO_OFFSET (decl_or_binfo); + overlapping = true; + vbases_p = false; + } + tree max_offset; /* If recording subobjects for a non-static data member or a - non-empty base class , we do not need to record offsets beyond + non-empty base class, we do not need to record offsets beyond the size of the biggest empty class. Additional data members will go at the end of the class. Additional base classes will go either at offset zero (if empty, in which case they cannot @@ -3985,13 +4014,13 @@ record_subobject_offsets (tree type, other empty classes might later be placed) or at the end of the class (where other objects might then be placed, so other empty subobjects might later overlap). */ - if (is_data_member - || !is_empty_class (BINFO_TYPE (type))) + if (!overlapping + || !is_empty_class (type)) max_offset = sizeof_biggest_empty_class; else max_offset = NULL_TREE; walk_subobject_offsets (type, record_subobject_offset, offset, - offsets, max_offset, is_data_member); + offsets, max_offset, vbases_p); } /* Returns nonzero if any of the empty subobjects of TYPE (located at @@ -4151,55 +4180,80 @@ empty_base_at_nonzero_offset_p (tree type, type. Return nonzero iff we added it at the end. */ static bool -layout_empty_base (record_layout_info rli, tree binfo, - tree eoc, splay_tree offsets) +layout_empty_base_or_field (record_layout_info rli, tree binfo_or_decl, + splay_tree offsets) { tree alignment; - tree basetype = BINFO_TYPE (binfo); bool atend = false; + tree binfo = NULL_TREE; + tree decl = NULL_TREE; + tree type; + if (TREE_CODE (binfo_or_decl) == TREE_BINFO) + { + binfo = binfo_or_decl; + type = BINFO_TYPE (binfo); + } + else + { + decl = binfo_or_decl; + type = TREE_TYPE (decl); + } - /* This routine should only be used for empty classes. */ - gcc_assert (is_empty_class (basetype)); - alignment = ssize_int (CLASSTYPE_ALIGN_UNIT (basetype)); + /* On some platforms (ARM), even empty classes will not be + byte-aligned. */ + tree eoc = round_up_loc (input_location, + rli_size_unit_so_far (rli), + CLASSTYPE_ALIGN_UNIT (type)); - if (!integer_zerop (BINFO_OFFSET (binfo))) - propagate_binfo_offsets - (binfo, size_diffop_loc (input_location, - size_zero_node, BINFO_OFFSET (binfo))); + /* This routine should only be used for empty classes. */ + gcc_assert (is_empty_class (type)); + alignment = size_int (CLASSTYPE_ALIGN_UNIT (type)); /* This is an empty base class. We first try to put it at offset zero. */ - if (layout_conflict_p (binfo, - BINFO_OFFSET (binfo), + tree offset = size_zero_node; + if (layout_conflict_p (type, + offset, offsets, /*vbases_p=*/0)) { /* That didn't work. Now, we move forward from the next available spot in the class. */ atend = true; - propagate_binfo_offsets (binfo, fold_convert (ssizetype, eoc)); + offset = eoc; while (1) { - if (!layout_conflict_p (binfo, - BINFO_OFFSET (binfo), + if (!layout_conflict_p (type, + offset, offsets, /*vbases_p=*/0)) /* We finally found a spot where there's no overlap. */ break; /* There's overlap here, too. Bump along to the next spot. */ - propagate_binfo_offsets (binfo, alignment); + offset = size_binop (PLUS_EXPR, offset, alignment); } } - if (CLASSTYPE_USER_ALIGN (basetype)) + if (CLASSTYPE_USER_ALIGN (type)) { - rli->record_align = MAX (rli->record_align, CLASSTYPE_ALIGN (basetype)); + rli->record_align = MAX (rli->record_align, CLASSTYPE_ALIGN (type)); if (warn_packed) - rli->unpacked_align = MAX (rli->unpacked_align, CLASSTYPE_ALIGN (basetype)); + rli->unpacked_align = MAX (rli->unpacked_align, CLASSTYPE_ALIGN (type)); TYPE_USER_ALIGN (rli->t) = 1; } + if (binfo) + /* Adjust BINFO_OFFSET (binfo) to be exactly OFFSET. */ + propagate_binfo_offsets (binfo, + size_diffop (offset, BINFO_OFFSET (binfo))); + else + { + DECL_FIELD_OFFSET (decl) = offset; + DECL_FIELD_BIT_OFFSET (decl) = bitsize_zero_node; + SET_DECL_OFFSET_ALIGN (decl, BITS_PER_UNIT); + } + return atend; } @@ -4277,15 +4331,7 @@ build_base_field (record_layout_info rli, tree binfo, } else { - tree eoc; - bool atend; - - /* On some platforms (ARM), even empty classes will not be - byte-aligned. */ - eoc = round_up_loc (input_location, - rli_size_unit_so_far (rli), - CLASSTYPE_ALIGN_UNIT (basetype)); - atend = layout_empty_base (rli, binfo, eoc, offsets); + bool atend = layout_empty_base_or_field (rli, binfo, offsets); /* A nearly-empty class "has no proper base class that is empty, not morally virtual, and at an offset other than zero." */ if (!BINFO_VIRTUAL_P (binfo) && CLASSTYPE_NEARLY_EMPTY_P (t)) @@ -4323,10 +4369,7 @@ build_base_field (record_layout_info rli, tree binfo, } /* Record the offsets of BINFO and its base subobjects. */ - record_subobject_offsets (binfo, - BINFO_OFFSET (binfo), - offsets, - /*is_data_member=*/false); + record_subobject_offsets (binfo, offsets); return next_field; } @@ -5886,10 +5929,11 @@ end_of_base (tree binfo) /* Returns the offset of the byte just past the end of the base class with the highest offset in T. If INCLUDE_VIRTUALS_P is zero, then - only non-virtual bases are included. */ + only non-virtual bases are included. If INCLUDE_FIELDS_P is true, + then also consider non-static data members. */ static tree -end_of_class (tree t, int include_virtuals_p) +end_of_class (tree t, bool include_virtuals_p, bool include_fields_p = false) { tree result = size_zero_node; vec *vbases; @@ -5912,6 +5956,16 @@ end_of_class (tree t, int include_virtuals_p) result = offset; } + if (include_fields_p) + for (tree field = TYPE_FIELDS (t); field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL) + { + offset = size_binop (PLUS_EXPR, DECL_FIELD_OFFSET (field), + DECL_SIZE_UNIT (field)); + if (tree_int_cst_lt (result, offset)) + result = offset; + } + if (include_virtuals_p) for (vbases = CLASSTYPE_VBASECLASSES (t), i = 0; vec_safe_iterate (vbases, i, &base_binfo); i++) @@ -6098,6 +6152,27 @@ layout_class_type (tree t, tree *virtuals_p) padding = NULL_TREE; + bool might_overlap = field_poverlapping_p (field); + + if (might_overlap && CLASS_TYPE_P (type) + && CLASSTYPE_NON_LAYOUT_POD_P (type)) + { + /* if D is a potentially-overlapping data member, update sizeof(C) to + max (sizeof(C), offset(D)+max (nvsize(D), dsize(D))). */ + tree nvsize = CLASSTYPE_SIZE_UNIT (type); + tree dsize = end_of_class (type, /*vbases*/true, /*fields*/true); + if (tree_int_cst_le (dsize, nvsize)) + { + DECL_SIZE_UNIT (field) = nvsize; + DECL_SIZE (field) = CLASSTYPE_SIZE (type); + } + else + { + DECL_SIZE_UNIT (field) = dsize; + DECL_SIZE (field) = bit_from_pos (dsize, bitsize_zero_node); + } + } + /* If this field is a bit-field whose width is greater than its type, then there are some special rules for allocating it. */ @@ -6164,15 +6239,14 @@ layout_class_type (tree t, tree *virtuals_p) /* We must also reset the DECL_MODE of the field. */ SET_DECL_MODE (field, TYPE_MODE (type)); } + else if (might_overlap && is_empty_class (type)) + layout_empty_base_or_field (rli, field, empty_base_offsets); else layout_nonempty_base_or_field (rli, field, NULL_TREE, empty_base_offsets); /* Remember the location of any empty classes in FIELD. */ - record_subobject_offsets (TREE_TYPE (field), - byte_position(field), - empty_base_offsets, - /*is_data_member=*/true); + record_subobject_offsets (field, empty_base_offsets); /* If a bit-field does not immediately follow another bit-field, and yet it starts in the middle of a byte, we have failed to diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index c6f216dab4b..e74b79c44e0 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -4428,6 +4428,31 @@ handle_nodiscard_attribute (tree *node, tree name, tree /*args*/, return NULL_TREE; } +/* Handle a C++2a "no_unique_address" attribute; arguments as in + struct attribute_spec.handler. */ +static tree +handle_no_unique_addr_attribute (tree* node, + tree name, + tree /*args*/, + int /*flags*/, + bool* no_add_attrs) +{ + if (TREE_CODE (*node) != FIELD_DECL) + { + warning (OPT_Wattributes, "%qE attribute can only be applied to " + "non-static data members", name); + *no_add_attrs = true; + } + else if (DECL_C_BIT_FIELD (*node)) + { + warning (OPT_Wattributes, "%qE attribute cannot be applied to " + "a bit-field", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + /* Table of valid C++ attributes. */ const struct attribute_spec cxx_attribute_table[] = { @@ -4449,6 +4474,8 @@ const struct attribute_spec std_attribute_table[] = handle_unused_attribute, NULL }, { "nodiscard", 0, 0, false, false, false, false, handle_nodiscard_attribute, NULL }, + { "no_unique_address", 0, 0, true, false, false, false, + handle_no_unique_addr_attribute, NULL }, { NULL, 0, 0, false, false, false, false, NULL, NULL } }; diff --git a/gcc/testsuite/g++.dg/abi/empty4.C b/gcc/testsuite/g++.dg/abi/empty4.C index d20a55cf7fc..8cc10a9fb0f 100644 --- a/gcc/testsuite/g++.dg/abi/empty4.C +++ b/gcc/testsuite/g++.dg/abi/empty4.C @@ -37,6 +37,16 @@ struct B B (long c) {m = c;} }; +#if __cpp_attributes +struct B2 +{ + [[no_unique_address]] Inter empty; + NonPod m; + + B2 (long c) {m = c;} +}; +#endif + struct C : NonPod, Inter { C (long c) : NonPod (c), Inter () {} @@ -65,6 +75,7 @@ int main () if (b2.m.m != 0x32333435) return 2; // we copied padding, which clobbered b2.m.m + { B c (0x12131415); was = c.m.m; c = 0x22232425; @@ -76,6 +87,22 @@ int main () if (c.m.m != 0x22232425) return 4; + } +#if __cpp_attributes + { + B2 c (0x12131415); + was = c.m.m; + c = 0x22232425; + if (was != now) + return 3; + + B2 d (0x32333435); + c.empty = d.empty; + + if (c.m.m != 0x22232425) + return 4; + } +#endif C e (0x32333435); diff --git a/gcc/testsuite/g++.dg/abi/empty5.C b/gcc/testsuite/g++.dg/abi/empty5.C index c3717727e21..9ad66d91f61 100644 --- a/gcc/testsuite/g++.dg/abi/empty5.C +++ b/gcc/testsuite/g++.dg/abi/empty5.C @@ -9,9 +9,20 @@ struct B { struct C : public B, public A {}; +#if __cpp_attributes +struct C2 : public B +{ + [[no_unique_address]] A a; +} c2; +#endif + C c; int main () { if ((void*) (A*) &c != &c) return 1; +#if __cpp_attributes + if ((void*)&c2.a != &c2) + return 2; +#endif } diff --git a/gcc/testsuite/g++.dg/abi/empty8.C b/gcc/testsuite/g++.dg/abi/empty8.C index a5287b15fa0..6d7954ec291 100644 --- a/gcc/testsuite/g++.dg/abi/empty8.C +++ b/gcc/testsuite/g++.dg/abi/empty8.C @@ -5,10 +5,20 @@ struct E1 {}; struct E2 : public E1 {}; struct S1 { int i; }; struct S2 : public S1, E2 {}; +#if __cpp_attributes +struct S22 : public S1 +{ + [[no_unique_address]] E2 e2; +} s22; +#endif S2 s2; int main () { if ((char *)(E2*) &s2 != (char *)&s2) return 1; +#if __cpp_attributes + if ((char *)&s22.e2 != (char *)&s22) + return 2; +#endif } diff --git a/gcc/testsuite/g++.dg/abi/no_unique_address1.C b/gcc/testsuite/g++.dg/abi/no_unique_address1.C new file mode 100644 index 00000000000..853726dd4b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/no_unique_address1.C @@ -0,0 +1,38 @@ +// { dg-do run { target c++2a } } + +struct B { }; + +struct A +{ + [[no_unique_address]] B b; + int i; +}; + +struct C +{ + B b; + int i; +}; + +struct D: B { }; + +struct E +{ + B b [[no_unique_address]]; + D d [[no_unique_address]]; +}; + +constexpr bool same (void *x, void *y) { return x == y; } + +int main() +{ + A a; + if (!same(&a.b, &a.i)) + __builtin_abort(); + C c; + if (same(&c.b, &c.i)) + __builtin_abort(); + E e; + if (same (&e.b, &e.d)) + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/abi/no_unique_address2.C b/gcc/testsuite/g++.dg/abi/no_unique_address2.C new file mode 100644 index 00000000000..bef6d5b43be --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/no_unique_address2.C @@ -0,0 +1,43 @@ +// { dg-do compile { target c++11 } } + +struct A +{ + virtual void f(); + char c; +}; + +struct B1 : A +{ + char c2; +}; + +struct B2 +{ + A a [[no_unique_address]]; + char c2; +}; + +struct C +{ + char c; +}; + +struct D: virtual C +{ + virtual void f(); +}; + +struct B3: D +{ + char c2; +}; + +struct B4 +{ + D d [[no_unique_address]]; + char c2; +}; + +#define SA(X) static_assert ((X), #X) +SA (sizeof (B2) == sizeof (B1)); +SA (sizeof (B3) == sizeof (B4)); diff --git a/gcc/testsuite/g++.dg/abi/no_unique_address3.C b/gcc/testsuite/g++.dg/abi/no_unique_address3.C new file mode 100644 index 00000000000..0bda77777e5 --- /dev/null +++ b/gcc/testsuite/g++.dg/abi/no_unique_address3.C @@ -0,0 +1,19 @@ +// { dg-do compile { target c++11 } } + +struct A { + unsigned char i : 1; +}; + +struct B: A +{ + unsigned char j : 7; +}; + +struct B2 +{ + [[no_unique_address]] A a; + unsigned char j : 7; +}; + +#define SA(X) static_assert ((X), #X) +SA (sizeof (B) == sizeof (B2)); diff --git a/gcc/testsuite/g++.dg/cpp2a/no_unique_address1.C b/gcc/testsuite/g++.dg/cpp2a/no_unique_address1.C new file mode 100644 index 00000000000..dd019393f28 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/no_unique_address1.C @@ -0,0 +1,13 @@ +// { dg-do compile { target c++2a } } + +[[no_unique_address]] struct B { }; // { dg-warning "attribute" } +[[no_unique_address]] int i; // { dg-warning "attribute" } +[[no_unique_address]] void f(); // { dg-warning "attribute" } + +struct A +{ + [[no_unique_address]] B b; + [[no_unique_address]] void f(); // { dg-warning "attribute" } + [[no_unique_address]] static B c; // { dg-warning "attribute" } + int i; +};