Commit bedf03a2 by Jason Merrill Committed by Jason Merrill

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
parent 749c0e1d
2018-10-01 Jason Merrill <jason@redhat.com>
* c-lex.c (c_common_has_attribute): Add no_unique_address.
2018-10-01 Eric Botcazou <ebotcazou@adacore.com> 2018-10-01 Eric Botcazou <ebotcazou@adacore.com>
* c-ada-spec.c (get_underlying_decl): Get to the main type variant. * c-ada-spec.c (get_underlying_decl): Get to the main type variant.
......
...@@ -356,6 +356,8 @@ c_common_has_attribute (cpp_reader *pfile) ...@@ -356,6 +356,8 @@ c_common_has_attribute (cpp_reader *pfile)
|| is_attribute_p ("nodiscard", attr_name) || is_attribute_p ("nodiscard", attr_name)
|| is_attribute_p ("fallthrough", attr_name)) || is_attribute_p ("fallthrough", attr_name))
result = 201603; result = 201603;
else if (is_attribute_p ("no_unique_address", attr_name))
result = 20180312;
if (result) if (result)
attr_name = NULL_TREE; attr_name = NULL_TREE;
} }
......
2018-10-03 Jason Merrill <jason@redhat.com>
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 <mliska@suse.cz> 2018-10-03 Martin Liska <mliska@suse.cz>
PR gcov-profile/86109 PR gcov-profile/86109
......
...@@ -180,8 +180,6 @@ static tree build_vtable (tree, tree, tree); ...@@ -180,8 +180,6 @@ static tree build_vtable (tree, tree, tree);
static void initialize_vtable (tree, vec<constructor_elt, va_gc> *); static void initialize_vtable (tree, vec<constructor_elt, va_gc> *);
static void layout_nonempty_base_or_field (record_layout_info, static void layout_nonempty_base_or_field (record_layout_info,
tree, tree, splay_tree); 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, static void accumulate_vtbl_inits (tree, tree, tree, tree, tree,
vec<constructor_elt, va_gc> **); vec<constructor_elt, va_gc> **);
static void dfs_accumulate_vtbl_inits (tree, tree, tree, tree, tree, static void dfs_accumulate_vtbl_inits (tree, tree, tree, tree, tree,
...@@ -202,7 +200,6 @@ static int record_subobject_offset (tree, tree, splay_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 check_subobject_offset (tree, tree, splay_tree);
static int walk_subobject_offsets (tree, subobject_offset_fn, static int walk_subobject_offsets (tree, subobject_offset_fn,
tree, splay_tree, tree, int); 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 layout_conflict_p (tree, tree, splay_tree, int);
static int splay_tree_compare_integer_csts (splay_tree_key k1, static int splay_tree_compare_integer_csts (splay_tree_key k1,
splay_tree_key k2); splay_tree_key k2);
...@@ -3960,20 +3957,52 @@ walk_subobject_offsets (tree type, ...@@ -3960,20 +3957,52 @@ walk_subobject_offsets (tree type,
return 0; return 0;
} }
/* Record all of the empty subobjects of TYPE (either a type or a /* Return true iff FIELD_DECL DECL is potentially overlapping. */
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 static bool
being placed at OFFSET. */ 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 static void
record_subobject_offsets (tree type, record_subobject_offsets (tree decl_or_binfo,
tree offset, splay_tree offsets)
splay_tree offsets,
bool is_data_member)
{ {
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; tree max_offset;
/* If recording subobjects for a non-static data member or a /* 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 the size of the biggest empty class. Additional data members
will go at the end of the class. Additional base classes will go will go at the end of the class. Additional base classes will go
either at offset zero (if empty, in which case they cannot either at offset zero (if empty, in which case they cannot
...@@ -3985,13 +4014,13 @@ record_subobject_offsets (tree type, ...@@ -3985,13 +4014,13 @@ record_subobject_offsets (tree type,
other empty classes might later be placed) or at the end of the other empty classes might later be placed) or at the end of the
class (where other objects might then be placed, so other empty class (where other objects might then be placed, so other empty
subobjects might later overlap). */ subobjects might later overlap). */
if (is_data_member if (!overlapping
|| !is_empty_class (BINFO_TYPE (type))) || !is_empty_class (type))
max_offset = sizeof_biggest_empty_class; max_offset = sizeof_biggest_empty_class;
else else
max_offset = NULL_TREE; max_offset = NULL_TREE;
walk_subobject_offsets (type, record_subobject_offset, offset, 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 /* Returns nonzero if any of the empty subobjects of TYPE (located at
...@@ -4151,55 +4180,80 @@ empty_base_at_nonzero_offset_p (tree type, ...@@ -4151,55 +4180,80 @@ empty_base_at_nonzero_offset_p (tree type,
type. Return nonzero iff we added it at the end. */ type. Return nonzero iff we added it at the end. */
static bool static bool
layout_empty_base (record_layout_info rli, tree binfo, layout_empty_base_or_field (record_layout_info rli, tree binfo_or_decl,
tree eoc, splay_tree offsets) splay_tree offsets)
{ {
tree alignment; tree alignment;
tree basetype = BINFO_TYPE (binfo);
bool atend = false; 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. */ /* On some platforms (ARM), even empty classes will not be
gcc_assert (is_empty_class (basetype)); byte-aligned. */
alignment = ssize_int (CLASSTYPE_ALIGN_UNIT (basetype)); tree eoc = round_up_loc (input_location,
rli_size_unit_so_far (rli),
CLASSTYPE_ALIGN_UNIT (type));
if (!integer_zerop (BINFO_OFFSET (binfo))) /* This routine should only be used for empty classes. */
propagate_binfo_offsets gcc_assert (is_empty_class (type));
(binfo, size_diffop_loc (input_location, alignment = size_int (CLASSTYPE_ALIGN_UNIT (type));
size_zero_node, BINFO_OFFSET (binfo)));
/* This is an empty base class. We first try to put it at offset /* This is an empty base class. We first try to put it at offset
zero. */ zero. */
if (layout_conflict_p (binfo, tree offset = size_zero_node;
BINFO_OFFSET (binfo), if (layout_conflict_p (type,
offset,
offsets, offsets,
/*vbases_p=*/0)) /*vbases_p=*/0))
{ {
/* That didn't work. Now, we move forward from the next /* That didn't work. Now, we move forward from the next
available spot in the class. */ available spot in the class. */
atend = true; atend = true;
propagate_binfo_offsets (binfo, fold_convert (ssizetype, eoc)); offset = eoc;
while (1) while (1)
{ {
if (!layout_conflict_p (binfo, if (!layout_conflict_p (type,
BINFO_OFFSET (binfo), offset,
offsets, offsets,
/*vbases_p=*/0)) /*vbases_p=*/0))
/* We finally found a spot where there's no overlap. */ /* We finally found a spot where there's no overlap. */
break; break;
/* There's overlap here, too. Bump along to the next spot. */ /* 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) 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; 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; return atend;
} }
...@@ -4277,15 +4331,7 @@ build_base_field (record_layout_info rli, tree binfo, ...@@ -4277,15 +4331,7 @@ build_base_field (record_layout_info rli, tree binfo,
} }
else else
{ {
tree eoc; bool atend = layout_empty_base_or_field (rli, binfo, offsets);
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);
/* A nearly-empty class "has no proper base class that is empty, /* A nearly-empty class "has no proper base class that is empty,
not morally virtual, and at an offset other than zero." */ not morally virtual, and at an offset other than zero." */
if (!BINFO_VIRTUAL_P (binfo) && CLASSTYPE_NEARLY_EMPTY_P (t)) if (!BINFO_VIRTUAL_P (binfo) && CLASSTYPE_NEARLY_EMPTY_P (t))
...@@ -4323,10 +4369,7 @@ build_base_field (record_layout_info rli, tree binfo, ...@@ -4323,10 +4369,7 @@ build_base_field (record_layout_info rli, tree binfo,
} }
/* Record the offsets of BINFO and its base subobjects. */ /* Record the offsets of BINFO and its base subobjects. */
record_subobject_offsets (binfo, record_subobject_offsets (binfo, offsets);
BINFO_OFFSET (binfo),
offsets,
/*is_data_member=*/false);
return next_field; return next_field;
} }
...@@ -5886,10 +5929,11 @@ end_of_base (tree binfo) ...@@ -5886,10 +5929,11 @@ end_of_base (tree binfo)
/* Returns the offset of the byte just past the end of the base class /* 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 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 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; tree result = size_zero_node;
vec<tree, va_gc> *vbases; vec<tree, va_gc> *vbases;
...@@ -5912,6 +5956,16 @@ end_of_class (tree t, int include_virtuals_p) ...@@ -5912,6 +5956,16 @@ end_of_class (tree t, int include_virtuals_p)
result = offset; 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) if (include_virtuals_p)
for (vbases = CLASSTYPE_VBASECLASSES (t), i = 0; for (vbases = CLASSTYPE_VBASECLASSES (t), i = 0;
vec_safe_iterate (vbases, i, &base_binfo); i++) vec_safe_iterate (vbases, i, &base_binfo); i++)
...@@ -6098,6 +6152,27 @@ layout_class_type (tree t, tree *virtuals_p) ...@@ -6098,6 +6152,27 @@ layout_class_type (tree t, tree *virtuals_p)
padding = NULL_TREE; 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 /* If this field is a bit-field whose width is greater than its
type, then there are some special rules for allocating type, then there are some special rules for allocating
it. */ it. */
...@@ -6164,15 +6239,14 @@ layout_class_type (tree t, tree *virtuals_p) ...@@ -6164,15 +6239,14 @@ layout_class_type (tree t, tree *virtuals_p)
/* We must also reset the DECL_MODE of the field. */ /* We must also reset the DECL_MODE of the field. */
SET_DECL_MODE (field, TYPE_MODE (type)); 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 else
layout_nonempty_base_or_field (rli, field, NULL_TREE, layout_nonempty_base_or_field (rli, field, NULL_TREE,
empty_base_offsets); empty_base_offsets);
/* Remember the location of any empty classes in FIELD. */ /* Remember the location of any empty classes in FIELD. */
record_subobject_offsets (TREE_TYPE (field), record_subobject_offsets (field, empty_base_offsets);
byte_position(field),
empty_base_offsets,
/*is_data_member=*/true);
/* If a bit-field does not immediately follow another bit-field, /* 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 and yet it starts in the middle of a byte, we have failed to
......
...@@ -4428,6 +4428,31 @@ handle_nodiscard_attribute (tree *node, tree name, tree /*args*/, ...@@ -4428,6 +4428,31 @@ handle_nodiscard_attribute (tree *node, tree name, tree /*args*/,
return NULL_TREE; 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. */ /* Table of valid C++ attributes. */
const struct attribute_spec cxx_attribute_table[] = const struct attribute_spec cxx_attribute_table[] =
{ {
...@@ -4449,6 +4474,8 @@ const struct attribute_spec std_attribute_table[] = ...@@ -4449,6 +4474,8 @@ const struct attribute_spec std_attribute_table[] =
handle_unused_attribute, NULL }, handle_unused_attribute, NULL },
{ "nodiscard", 0, 0, false, false, false, false, { "nodiscard", 0, 0, false, false, false, false,
handle_nodiscard_attribute, NULL }, 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 } { NULL, 0, 0, false, false, false, false, NULL, NULL }
}; };
......
...@@ -37,6 +37,16 @@ struct B ...@@ -37,6 +37,16 @@ struct B
B (long c) {m = c;} 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 struct C : NonPod, Inter
{ {
C (long c) : NonPod (c), Inter () {} C (long c) : NonPod (c), Inter () {}
...@@ -65,6 +75,7 @@ int main () ...@@ -65,6 +75,7 @@ int main ()
if (b2.m.m != 0x32333435) if (b2.m.m != 0x32333435)
return 2; // we copied padding, which clobbered b2.m.m return 2; // we copied padding, which clobbered b2.m.m
{
B c (0x12131415); B c (0x12131415);
was = c.m.m; was = c.m.m;
c = 0x22232425; c = 0x22232425;
...@@ -76,6 +87,22 @@ int main () ...@@ -76,6 +87,22 @@ int main ()
if (c.m.m != 0x22232425) if (c.m.m != 0x22232425)
return 4; 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); C e (0x32333435);
......
...@@ -9,9 +9,20 @@ struct B { ...@@ -9,9 +9,20 @@ struct B {
struct C : public B, public A {}; struct C : public B, public A {};
#if __cpp_attributes
struct C2 : public B
{
[[no_unique_address]] A a;
} c2;
#endif
C c; C c;
int main () { int main () {
if ((void*) (A*) &c != &c) if ((void*) (A*) &c != &c)
return 1; return 1;
#if __cpp_attributes
if ((void*)&c2.a != &c2)
return 2;
#endif
} }
...@@ -5,10 +5,20 @@ struct E1 {}; ...@@ -5,10 +5,20 @@ struct E1 {};
struct E2 : public E1 {}; struct E2 : public E1 {};
struct S1 { int i; }; struct S1 { int i; };
struct S2 : public S1, E2 {}; struct S2 : public S1, E2 {};
#if __cpp_attributes
struct S22 : public S1
{
[[no_unique_address]] E2 e2;
} s22;
#endif
S2 s2; S2 s2;
int main () { int main () {
if ((char *)(E2*) &s2 != (char *)&s2) if ((char *)(E2*) &s2 != (char *)&s2)
return 1; return 1;
#if __cpp_attributes
if ((char *)&s22.e2 != (char *)&s22)
return 2;
#endif
} }
// { 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();
}
// { 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));
// { 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));
// { 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;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment