This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH] Tweak class layout
- To: gcc-patches at gcc dot gnu dot org
- Subject: [C++ PATCH] Tweak class layout
- From: Nathan Sidwell <nathan at codesourcery dot com>
- Date: Fri, 20 Apr 2001 16:33:31 +0100
- Organization: Codesourcery LLC
Hi,
I've installed the attached patch on both mainline and 3.0 branch which
fixes several things wrong with empty and nearly empty class layout.
Firstly, it is possible for
a class with only empty bases to be not nearly empty. If an empty
base had to be placed at the end, the size becomes larger. Fixed by
propagating a flag up from layout_empty_base.
Secondly, end_of_class used the dsize of a class, which is zero for empty
bases, and then layout_class checks if it is greater than the current size.
This is an off-by-one error which fails for something like,
stuct A {};
struct B1 : A {};
struct B2 : A {};
struct C : B1, B2 {virtual void foo ();};
Here B2 is placed at offset 4, but that's not bigger than the existing
size of C. C ends up with a sizeof of 4, and a base at offset 4.
Fixed by having end_of_class use TYPE_SIZE_UNIT for empty bases (which
will usually be 1, but sometimes is more, like on the ARM).
Finally, we can now already get a non-zero size on an empty class because
of empty bases. So only add a padding member if the class is both empty and
has no empty bases.
built & tested on i686-pc-linux-gnu, approved by Mark.
nathan
--
Dr Nathan Sidwell :: http://www.codesourcery.com :: CodeSourcery LLC
'But that's a lie.' - 'Yes it is. What's your point?'
nathan@codesourcery.com : http://www.cs.bris.ac.uk/~nathan/ : nathan@acm.org
2001-04-20 Nathan Sidwell <nathan@codesourcery.com>
* class.c (dfs_unshared_virtual_bases): Add ATTRIBUTE_UNUSED.
(layout_empty_base): Return at end flag.
(build_base_field): Likewise.
(build_base_fields): Likewise.
(layout_virtual_bases): Don't add 1 to eoc value.
(end_of_class): Use full size for empty bases.
(layout_class_type): Clear CLASSNEARLY_EMPTY_P if we appended
empty bases. Don't add 1 to eoc value. Only add trailing padding
if we're an empty class with no empty bases.
(dump_class_hierarchy): Dump size and alignment.
Index: cp/class.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/cp/class.c,v
retrieving revision 1.358.2.14
diff -c -3 -p -r1.358.2.14 class.c
*** class.c 2001/04/12 07:39:18 1.358.2.14
--- class.c 2001/04/12 11:15:03
*************** static void check_bitfield_decl PARAMS (
*** 144,153 ****
static void check_field_decl PARAMS ((tree, tree, int *, int *, int *, int *));
static void check_field_decls PARAMS ((tree, tree *, int *, int *, int *,
int *));
! static void build_base_field PARAMS ((record_layout_info, tree, int *,
splay_tree));
- static void build_base_fields PARAMS ((record_layout_info, int *,
- splay_tree));
static tree build_vbase_pointer_fields PARAMS ((record_layout_info, int *));
static tree build_vtbl_or_vbase_field PARAMS ((tree, tree, tree, tree, tree,
int *));
--- 144,153 ----
static void check_field_decl PARAMS ((tree, tree, int *, int *, int *, int *));
static void check_field_decls PARAMS ((tree, tree *, int *, int *, int *,
int *));
! static bool build_base_field PARAMS ((record_layout_info, tree, int *,
! splay_tree));
! static bool build_base_fields PARAMS ((record_layout_info, int *,
splay_tree));
static tree build_vbase_pointer_fields PARAMS ((record_layout_info, int *));
static tree build_vtbl_or_vbase_field PARAMS ((tree, tree, tree, tree, tree,
int *));
*************** static void layout_nonempty_base_or_fiel
*** 180,186 ****
tree, tree,
splay_tree));
static unsigned HOST_WIDE_INT end_of_class PARAMS ((tree, int));
! static void layout_empty_base PARAMS ((tree, tree, splay_tree));
static void accumulate_vtbl_inits PARAMS ((tree, tree, tree, tree, tree));
static tree dfs_accumulate_vtbl_inits PARAMS ((tree, tree, tree, tree,
tree));
--- 180,186 ----
tree, tree,
splay_tree));
static unsigned HOST_WIDE_INT end_of_class PARAMS ((tree, int));
! static bool layout_empty_base PARAMS ((tree, tree, splay_tree));
static void accumulate_vtbl_inits PARAMS ((tree, tree, tree, tree, tree));
static tree dfs_accumulate_vtbl_inits PARAMS ((tree, tree, tree, tree,
tree));
*************** mark_primary_virtual_base (binfo, base_b
*** 1826,1832 ****
static tree dfs_unshared_virtual_bases (binfo, data)
tree binfo;
! void *data;
{
if (TREE_VIA_VIRTUAL (binfo) && !BINFO_MARKED (binfo)
&& CLASSTYPE_HAS_PRIMARY_BASE_P (BINFO_TYPE (binfo)))
--- 1826,1832 ----
static tree dfs_unshared_virtual_bases (binfo, data)
tree binfo;
! void *data ATTRIBUTE_UNUSED;
{
if (TREE_VIA_VIRTUAL (binfo) && !BINFO_MARKED (binfo)
&& CLASSTYPE_HAS_PRIMARY_BASE_P (BINFO_TYPE (binfo)))
*************** layout_nonempty_base_or_field (rli, decl
*** 4016,4024 ****
/* Layout the empty base BINFO. EOC indicates the byte currently just
past the end of the class, and should be correctly aligned for a
class of the type indicated by BINFO; OFFSETS gives the offsets of
! the empty bases allocated so far. */
! static void
layout_empty_base (binfo, eoc, offsets)
tree binfo;
tree eoc;
--- 4016,4025 ----
/* Layout the empty base BINFO. EOC indicates the byte currently just
past the end of the class, and should be correctly aligned for a
class of the type indicated by BINFO; OFFSETS gives the offsets of
! the empty bases allocated so far. Return non-zero iff we added it
! at the end. */
! static bool
layout_empty_base (binfo, eoc, offsets)
tree binfo;
tree eoc;
*************** layout_empty_base (binfo, eoc, offsets)
*** 4026,4031 ****
--- 4027,4033 ----
{
tree alignment;
tree basetype = BINFO_TYPE (binfo);
+ bool atend = false;
/* This routine should only be used for empty classes. */
my_friendly_assert (is_empty_class (basetype), 20000321);
*************** layout_empty_base (binfo, eoc, offsets)
*** 4040,4045 ****
--- 4042,4048 ----
{
/* That didn't work. Now, we move forward from the next
available spot in the class. */
+ atend = true;
propagate_binfo_offsets (binfo, convert (ssizetype, eoc));
while (1)
{
*************** layout_empty_base (binfo, eoc, offsets)
*** 4054,4067 ****
propagate_binfo_offsets (binfo, alignment);
}
}
}
/* Build a FIELD_DECL for the base given by BINFO in the class
indicated by RLI. If the new object is non-empty, clear *EMPTY_P.
*BASE_ALIGN is a running maximum of the alignments of any base
! class. OFFSETS gives the location of empty base subobjects. */
! static void
build_base_field (rli, binfo, empty_p, offsets)
record_layout_info rli;
tree binfo;
--- 4057,4072 ----
propagate_binfo_offsets (binfo, alignment);
}
}
+ return atend;
}
/* Build a FIELD_DECL for the base given by BINFO in the class
indicated by RLI. If the new object is non-empty, clear *EMPTY_P.
*BASE_ALIGN is a running maximum of the alignments of any base
! class. OFFSETS gives the location of empty base subobjects. Return
! non-zero if the new object cannot be nearly-empty. */
! static bool
build_base_field (rli, binfo, empty_p, offsets)
record_layout_info rli;
tree binfo;
*************** build_base_field (rli, binfo, empty_p, o
*** 4070,4080 ****
{
tree basetype = BINFO_TYPE (binfo);
tree decl;
if (!COMPLETE_TYPE_P (basetype))
/* This error is now reported in xref_tag, thus giving better
location information. */
! return;
decl = build_decl (FIELD_DECL, NULL_TREE, basetype);
DECL_ARTIFICIAL (decl) = 1;
--- 4075,4086 ----
{
tree basetype = BINFO_TYPE (binfo);
tree decl;
+ bool atend = false;
if (!COMPLETE_TYPE_P (basetype))
/* This error is now reported in xref_tag, thus giving better
location information. */
! return atend;
decl = build_decl (FIELD_DECL, NULL_TREE, basetype);
DECL_ARTIFICIAL (decl) = 1;
*************** build_base_field (rli, binfo, empty_p, o
*** 4103,4109 ****
byte-aligned. */
eoc = tree_low_cst (rli_size_unit_so_far (rli), 0);
eoc = CEIL (eoc, DECL_ALIGN_UNIT (decl)) * DECL_ALIGN_UNIT (decl);
! layout_empty_base (binfo, size_int (eoc), offsets);
}
/* Record the offsets of BINFO and its base subobjects. */
--- 4109,4115 ----
byte-aligned. */
eoc = tree_low_cst (rli_size_unit_so_far (rli), 0);
eoc = CEIL (eoc, DECL_ALIGN_UNIT (decl)) * DECL_ALIGN_UNIT (decl);
! atend |= layout_empty_base (binfo, size_int (eoc), offsets);
}
/* Record the offsets of BINFO and its base subobjects. */
*************** build_base_field (rli, binfo, empty_p, o
*** 4111,4122 ****
BINFO_OFFSET (binfo),
offsets,
/*vbases_p=*/0);
}
/* Layout all of the non-virtual base classes. Record empty
! subobjects in OFFSETS. */
! static void
build_base_fields (rli, empty_p, offsets)
record_layout_info rli;
int *empty_p;
--- 4117,4130 ----
BINFO_OFFSET (binfo),
offsets,
/*vbases_p=*/0);
+ return atend;
}
/* Layout all of the non-virtual base classes. Record empty
! subobjects in OFFSETS. Return non-zero if the type cannot be nearly
! empty. */
! static bool
build_base_fields (rli, empty_p, offsets)
record_layout_info rli;
int *empty_p;
*************** build_base_fields (rli, empty_p, offsets
*** 4127,4132 ****
--- 4135,4141 ----
tree rec = rli->t;
int n_baseclasses = CLASSTYPE_N_BASECLASSES (rec);
int i;
+ bool atend = 0;
/* Under the new ABI, the primary base class is always allocated
first. */
*************** build_base_fields (rli, empty_p, offsets
*** 4153,4160 ****
&& !BINFO_PRIMARY_P (base_binfo))
continue;
! build_base_field (rli, base_binfo, empty_p, offsets);
}
}
/* Go through the TYPE_METHODS of T issuing any appropriate
--- 4162,4170 ----
&& !BINFO_PRIMARY_P (base_binfo))
continue;
! atend |= build_base_field (rli, base_binfo, empty_p, offsets);
}
+ return atend;
}
/* Go through the TYPE_METHODS of T issuing any appropriate
*************** layout_virtual_bases (t, offsets)
*** 4829,4835 ****
multiple such bases at the same location. */
eoc = end_of_class (t, /*include_virtuals_p=*/1);
if (eoc * BITS_PER_UNIT > dsize)
! dsize = (eoc + 1) * BITS_PER_UNIT;
/* Now, make sure that the total size of the type is a multiple of
its alignment. */
--- 4839,4845 ----
multiple such bases at the same location. */
eoc = end_of_class (t, /*include_virtuals_p=*/1);
if (eoc * BITS_PER_UNIT > dsize)
! dsize = eoc * BITS_PER_UNIT;
/* Now, make sure that the total size of the type is a multiple of
its alignment. */
*************** end_of_class (t, include_virtuals_p)
*** 4868,4873 ****
--- 4878,4884 ----
{
tree base_binfo;
tree offset;
+ tree size;
unsigned HOST_WIDE_INT end_of_base;
base_binfo = BINFO_BASETYPE (TYPE_BINFO (t), i);
*************** end_of_class (t, include_virtuals_p)
*** 4877,4885 ****
&& !BINFO_PRIMARY_P (base_binfo))
continue;
offset = size_binop (PLUS_EXPR,
BINFO_OFFSET (base_binfo),
! CLASSTYPE_SIZE_UNIT (BINFO_TYPE (base_binfo)));
end_of_base = tree_low_cst (offset, /*pos=*/1);
if (end_of_base > result)
result = end_of_base;
--- 4888,4903 ----
&& !BINFO_PRIMARY_P (base_binfo))
continue;
+ if (is_empty_class (BINFO_TYPE (base_binfo)))
+ /* An empty class has zero CLASSTYPE_SIZE_UNIT, but we need to
+ allocate some space for it. It cannot have virtual bases,
+ so TYPE_SIZE_UNIT is fine. */
+ size = TYPE_SIZE_UNIT (BINFO_TYPE (base_binfo));
+ else
+ size = CLASSTYPE_SIZE_UNIT (BINFO_TYPE (base_binfo));
offset = size_binop (PLUS_EXPR,
BINFO_OFFSET (base_binfo),
! size);
end_of_base = tree_low_cst (offset, /*pos=*/1);
if (end_of_base > result)
result = end_of_base;
*************** layout_class_type (t, empty_p, vfuns_p,
*** 4971,4977 ****
/* Build FIELD_DECLs for all of the non-virtual base-types. */
empty_base_offsets = splay_tree_new (splay_tree_compare_integer_csts,
NULL, NULL);
! build_base_fields (rli, empty_p, empty_base_offsets);
/* Add pointers to all of our virtual base-classes. */
TYPE_FIELDS (t) = chainon (build_vbase_pointer_fields (rli, empty_p),
TYPE_FIELDS (t));
--- 4989,4997 ----
/* Build FIELD_DECLs for all of the non-virtual base-types. */
empty_base_offsets = splay_tree_new (splay_tree_compare_integer_csts,
NULL, NULL);
! if (build_base_fields (rli, empty_p, empty_base_offsets))
! CLASSTYPE_NEARLY_EMPTY_P (t) = 0;
!
/* Add pointers to all of our virtual base-classes. */
TYPE_FIELDS (t) = chainon (build_vbase_pointer_fields (rli, empty_p),
TYPE_FIELDS (t));
*************** layout_class_type (t, empty_p, vfuns_p,
*** 5061,5067 ****
if (TREE_CODE (rli_size_unit_so_far (rli)) == INTEGER_CST
&& compare_tree_int (rli_size_unit_so_far (rli), eoc) < 0)
{
! rli->offset = size_binop (MAX_EXPR, rli->offset, size_int (eoc + 1));
rli->bitpos = bitsize_zero_node;
}
--- 5081,5087 ----
if (TREE_CODE (rli_size_unit_so_far (rli)) == INTEGER_CST
&& compare_tree_int (rli_size_unit_so_far (rli), eoc) < 0)
{
! rli->offset = size_binop (MAX_EXPR, rli->offset, size_int (eoc));
rli->bitpos = bitsize_zero_node;
}
*************** layout_class_type (t, empty_p, vfuns_p,
*** 5070,5076 ****
if it has basetypes. Therefore, we add the fake field after all
the other fields; if there are already FIELD_DECLs on the list,
their offsets will not be disturbed. */
! if (*empty_p)
{
tree padding;
--- 5090,5096 ----
if it has basetypes. Therefore, we add the fake field after all
the other fields; if there are already FIELD_DECLs on the list,
their offsets will not be disturbed. */
! if (!eoc && *empty_p)
{
tree padding;
*************** dump_class_hierarchy (name, t)
*** 6761,6768 ****
error ("could not open dump file `%s'", name);
return;
}
! fprintf (stream, "%s\n",
! type_as_string (t, TFF_PLAIN_IDENTIFIER));
dump_class_hierarchy_r (stream, t, TYPE_BINFO (t), 0);
fprintf (stream, "\n");
if (name)
--- 6781,6791 ----
error ("could not open dump file `%s'", name);
return;
}
! fprintf (stream, "%s size=", type_as_string (t, TFF_PLAIN_IDENTIFIER));
! fprintf (stream, HOST_WIDE_INT_PRINT_DEC,
! tree_low_cst (TYPE_SIZE (t), 0) / BITS_PER_UNIT);
! fprintf (stream, " align=%lu\n",
! (unsigned long)(TYPE_ALIGN (t) / BITS_PER_UNIT));
dump_class_hierarchy_r (stream, t, TYPE_BINFO (t), 0);
fprintf (stream, "\n");
if (name)
// Build don't link:
// Copyright (C) 2001 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 12 Apr 2001 <nathan@codesourcery.com>
// Check we deal with trailing empty base classes properly
struct A {};
struct B1 : A {};
struct B2 : A {};
struct B3 : A {};
struct B4 : A {};
struct B5 : A {};
struct B6 : A {};
struct B7 : A {};
struct B8 : A {};
struct C1 : B1
{
virtual void Foo () {};
};
struct C2 : B1, B2
{
virtual void Foo () {};
};
struct C3 : B1, B2, B3
{
virtual void Foo () {};
};
struct C4 : B1, B2, B3, B4
{
virtual void Foo () {};
};
struct C5 : B1, B2, B3, B4, B5
{
virtual void Foo () {};
};
struct C6 : B1, B2, B3, B4, B5, B6
{
virtual void Foo () {};
};
struct C7 : B1, B2, B3, B4, B5, B6, B7
{
virtual void Foo () {};
};
struct C8 : B1, B2, B3, B4, B5, B6, B7, B8
{
virtual void Foo () {};
};
struct D1 : virtual C1 {};
struct D2 : virtual C2 {};
struct D3 : virtual C3 {};
struct D4 : virtual C4 {};
struct D5 : virtual C5 {};
struct D6 : virtual C6 {};
struct D7 : virtual C7 {};
struct D8 : virtual C8 {};
unsigned const nearly_empty_size = sizeof (D1);
template <typename Cn, typename Dn> int Check (Dn const &ref)
{
if ((sizeof (Cn) <= nearly_empty_size)
!= (static_cast <void const *> (&ref)
== static_cast <Cn const *> (&ref)))
return 1;
return 0;
}
template <typename Bn, typename Cn> int Check ()
{
Cn c[2];
if (static_cast <A *> (static_cast <B1 *> (&c[1]))
== static_cast <A *> (static_cast <Bn *> (&c[0])))
return 1;
return 0;
}
int main ()
{
#if defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100
if (Check<B1, C1> ())
return 1;
if (Check<B2, C2> ())
return 2;
if (Check<B3, C3> ())
return 3;
if (Check<B4, C4> ())
return 4;
if (Check<B5, C5> ())
return 5;
if (Check<B6, C6> ())
return 6;
if (Check<B7, C7> ())
return 7;
if (Check<B8, C8> ())
return 8;
if (Check<C1> (D1 ()))
return 11;
if (Check<C2> (D2 ()))
return 12;
if (Check<C3> (D3 ()))
return 13;
if (Check<C4> (D4 ()))
return 14;
if (Check<C5> (D5 ()))
return 15;
if (Check<C6> (D6 ()))
return 16;
if (Check<C7> (D7 ()))
return 17;
if (Check<C8> (D8 ()))
return 18;
if (sizeof (C2) == nearly_empty_size)
return 22;
if (sizeof (C3) == nearly_empty_size)
return 23;
if (sizeof (C4) == nearly_empty_size)
return 24;
if (sizeof (C5) == nearly_empty_size)
return 25;
if (sizeof (C6) == nearly_empty_size)
return 26;
if (sizeof (C7) == nearly_empty_size)
return 27;
if (sizeof (C8) == nearly_empty_size)
return 28;
#endif
return 0;
}
// Build don't link:
// Copyright (C) 2001 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 12 Apr 2001 <nathan@codesourcery.com>
// Check we deal with aligning virtual bases after a trailing empty
// base class properly
struct A {};
struct B1 : A {};
struct B2 : A {};
struct B3 : A {};
struct C : B1, B2, virtual B3 {};
int main ()
{
#if defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100
C c;
if (((char *)static_cast <B3 *> (&c) - (char *)&c) % __alignof__ (C))
return 1;
#endif
return 0;
}