cxx-ada-inherited-tail-padding

C++ Ada inherited tail padding

The C++ Ada mapper loses reusable base-class tail padding, including non-polymorphic and multiple-base layouts, so an Ada view can have a larger size and different field offsets than C++.

StagedApplies in patchset orderStaged in 1.2.0

Why this is staged.

The nested primary-base component this bundle selects is emitted by the staged virtual-base layout path, so a compiler carrying this patch without the staged ABI-layout bundles does not produce the corrected record. Held out of the published patchset with the rest of the layout family.

Depends on

Where it applies.

How each patchset treats this bundle on each GCC major
PatchsetGCC 13GCC 14GCC 15GCC 16
1.2.0 (latest)Staged13.2.0Staged14.2.0Staged15.3.0Staged16.2.0
1.1.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.2.0
1.0.1Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0
1.0.0Not applicable13.2.0Not applicable14.2.0Not applicable15.3.0Not applicable16.1.0

Explanation.

C++ may reuse the tail padding of a non-POD base subobject for a derived member. The Ada mapper currently loses the distinction between the base's data size and complete-object size, so GNAT places the derived member after the whole base object.

For example, on the supported 64-bit targets this C++ code gives both classes a complete-object size of 16 bytes; extra_ occupies bytes 12 through 15:

class Tail_Base
{
public:
  Tail_Base (int);
  virtual ~Tail_Base ();
protected:
  int value_;
};

class Tail_Derived : public Tail_Base
{
private:
  int extra_;
};

The unpatched mapper produces an Ada parent whose value and object sizes are both 16 bytes, then appends extra_u after it. GNAT therefore makes the derived object 24 bytes:

type Tail_Base is tagged limited record
   value_u : aliased int;
end record
with Import => True,
     Convention => CPP;

type Tail_Derived is limited new Tail_Base with record
   extra_u : aliased int;
end record
with Import => True,
     Convention => CPP;

The corrected output records the C++ base data size separately from its complete-object size. Because an Ada extension cannot place a child component inside its parent's Object_Size, a derived class that actually reuses the tail maps its primary base to a nested as-base storage view:

for Tail_Base'Size use 96;
for Tail_Base'Object_Size use 128;
for Tail_Base use record
   value_u at 8 range 0 .. 31;
end record;

type Tail_Base_As_Base is record
   value_u : int;
end record
with Convention => C_Pass_By_Copy;
pragma Component_Alignment (Storage_Unit, Tail_Base_As_Base);
for Tail_Base_As_Base'Size use 96;
for Tail_Base_As_Base'Object_Size use 96;
for Tail_Base_As_Base'Alignment use 4;
for Tail_Base_As_Base use record
   value_u at 8 range 0 .. 31;
end record;

type Tail_Derived is limited record
   parent : aliased Tail_Base_As_Base;
   extra_u : aliased int;
end record
with Import => True,
     Convention => CPP;
for Tail_Derived'Size use 128;
for Tail_Derived'Object_Size use 128;
for Tail_Derived use record
   parent at 0 range 0 .. 95;
   extra_u at 12 range 0 .. 31;
end record;

Tail reuse is not limited to polymorphic classes. For example:

struct Plain_Base { short s; char c; Plain_Base (); };
struct Plain_Derived : Plain_Base { char d; };

GCC gives Plain_Base four complete-object bytes but only three bytes as a base, places d at byte 3, and keeps sizeof (Plain_Derived) == 4. The corrected predicate therefore uses the C++ as-base size for every class and examines every direct non-virtual base, rather than requiring a tagged class with exactly one base.

For a three-byte plain base the storage view also uses pragma Component_Alignment (Storage_Unit, Plain_Base_As_Base), Size and Object_Size of 24 bits, and alignment 1. That keeps the base component addressable without forcing Ada to round its object size back to four bytes.

The nested view deliberately models static C++ storage, not native Ada inheritance. Operations on the complete derived object still use its imported C++ profiles; code that needs a typed base view may take the nested component's address and perform the same explicit access-view conversion described by the concrete multiple-inheritance bundle.

The executable regression runs at -O0 and -O2. It covers polymorphic, ordinary single-base, and ordinary multiple-base reuse, compares Ada object sizes with C++ sizeof, writes fields through the Ada view, and reads them back through C++ methods. The compiler tests also check the exact nested as-base clauses and reduced component alignment.

Patch.

Variant gcc-13-14

Applies to 13.2.0, 14.2.0. Source flavors: linux, darwin_arm64.

+276 −1 4 files

Download the patch

gcc/c-family/c-ada-spec.cc +109โˆ’0modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2783 +2783 @@dump_ada_methods (pretty_printer *buffer, tree node, int spc)
27832783 return 1;
27842784}
27852785
2786Added line. /* Return true if NODE needs an explicit Ada layout to describe C++ tail
2787Added line. padding, either because its object size exceeds its size as a base class or
2788Added line. because it places a field in a direct base class's tail padding. */
2789Added line.
2790Added line. static bool
2791Added line. needs_tail_padding_layout (tree node)
2792Added line. {
2793Added line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (node))
2794Added line. return false;
2795Added line.
2796Added line. const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2797Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2798Added line. return (data_size >= 0 && object_size > data_size)
2799Added line. || cpp_check (node, REUSES_BASE_TAIL_PADDING);
2800Added line. }
2801Added line.
2802Added line. /* Return true if all of NODE's fields that are visible in the generated Ada
2803Added line. record have constant positions and sizes suitable for component clauses. */
2804Added line.
2805Added line. static bool
2806Added line. has_constant_field_layout (tree node)
2807Added line. {
2808Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2809Added line. if (TREE_CODE (field) == FIELD_DECL
2810Added line. && DECL_NAME (field)
2811Added line. && !DECL_VIRTUAL_P (field)
2812Added line. && (!DECL_FIELD_OFFSET (field)
2813Added line. || !DECL_FIELD_BIT_OFFSET (field)
2814Added line. || !DECL_SIZE (field)
2815Added line. || !tree_fits_uhwi_p (DECL_FIELD_OFFSET (field))
2816Added line. || !tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (field))
2817Added line. || !tree_fits_uhwi_p (DECL_SIZE (field))
2818Added line. || tree_to_uhwi (DECL_SIZE (field)) == 0))
2819Added line. return false;
2820Added line.
2821Added line. return true;
2822Added line. }
2823Added line.
2824Added line. /* Dump the size and component clauses needed to preserve NODE's C++ tail
2825Added line. padding layout. TYPE is the declaration used for Ada name qualification. */
2826Added line.
2827Added line. static void
2828Added line. dump_ada_tail_padding_layout (pretty_printer *buffer, tree node, tree type,
2829Added line. int spc)
2830Added line. {
2831Added line. const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2832Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2833Added line.
2834Added line. gcc_assert (data_size >= 0 && object_size >= 0);
2835Added line.
2836Added line. newline_and_indent (buffer, spc);
2837Added line. pp_string (buffer, "for ");
2838Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
2839Added line. pp_string (buffer, "'Size use ");
2840Added line. pp_wide_integer (buffer, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
2841Added line. pp_semicolon (buffer);
2842Added line.
2843Added line. newline_and_indent (buffer, spc);
2844Added line. pp_string (buffer, "for ");
2845Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
2846Added line. pp_string (buffer, "'Object_Size use ");
2847Added line. pp_wide_integer (buffer, object_size * BITS_PER_UNIT);
2848Added line. pp_semicolon (buffer);
2849Added line.
2850Added line. newline_and_indent (buffer, spc);
2851Added line. pp_string (buffer, "for ");
2852Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
2853Added line. pp_string (buffer, " use record");
2854Added line.
2855Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2856Added line. if (TREE_CODE (field) == FIELD_DECL
2857Added line. && DECL_NAME (field)
2858Added line. && !DECL_VIRTUAL_P (field))
2859Added line. {
2860Added line. const unsigned HOST_WIDE_INT bitpos
2861Added line. = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
2862Added line. + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
2863Added line. const unsigned HOST_WIDE_INT position = bitpos / BITS_PER_UNIT;
2864Added line. const unsigned HOST_WIDE_INT first_bit = bitpos % BITS_PER_UNIT;
2865Added line. const unsigned HOST_WIDE_INT last_bit
2866Added line. = first_bit + tree_to_uhwi (DECL_SIZE (field)) - 1;
2867Added line.
2868Added line. newline_and_indent (buffer, spc + INDENT_INCR);
2869Added line. dump_ada_decl_name (buffer, field, false);
2870Added line. pp_string (buffer, " at ");
2871Added line. pp_unsigned_wide_integer (buffer, position);
2872Added line. pp_string (buffer, " range ");
2873Added line. pp_unsigned_wide_integer (buffer, first_bit);
2874Added line. pp_string (buffer, " .. ");
2875Added line. pp_unsigned_wide_integer (buffer, last_bit);
2876Added line. pp_semicolon (buffer);
2877Added line. }
2878Added line.
2879Added line. newline_and_indent (buffer, spc);
2880Added line. pp_string (buffer, "end record;");
2881Added line. }
2882Added line.
27862883/* Dump in BUFFER a forward declaration for TYPE present inside T.
27872884 SPC is the indentation level. */
27882885
@@ -3816 +3913 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
38163913
38173914 need_semicolon = !dump_ada_methods (buffer, node, spc);
38183915
3916Added line. if (needs_tail_padding_layout (node) && has_constant_field_layout (node))
3917Added line. {
3918Added line. if (need_semicolon)
3919Added line. {
3920Added line. need_semicolon = false;
3921Added line. pp_semicolon (buffer);
3922Added line. }
3923Added line.
3924Added line. pp_newline (buffer);
3925Added line. dump_ada_tail_padding_layout (buffer, node, type, spc);
3926Added line. }
3927Added line.
38193928 /* Print the static fields of the structure, if any. */
38203929 for (tree tmp = TYPE_FIELDS (node); tmp; tmp = TREE_CHAIN (tmp))
38213930 {
gcc/c-family/c-ada-spec.h +3โˆ’1modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -25 +25 @@along with GCC; see the file COPYING3. If not see
2525/* In c-ada-spec.cc */
2626
2727enum cpp_operation {
28Added line. GET_AS_BASE_SIZE,
2829 GET_FUNCTION_QUALIFIERS,
2930 HAS_DEPENDENT_TEMPLATE_ARGS,
3031 IS_ABSTRACT,
@@ -34 +35 @@enum cpp_operation {
3435 IS_COPY_CONSTRUCTOR,
3536 IS_MOVE_CONSTRUCTOR,
3637 IS_TEMPLATE,
37Removed line. IS_TRIVIAL
38Added line. IS_TRIVIAL,
39Added line. REUSES_BASE_TAIL_PADDING
3840};
3941
4042enum cpp_function_qualifier {
gcc/cp/decl2.cc +50โˆ’0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -5257 +5257 @@cpp_check (tree t, cpp_operation op)
52575257{
52585258 switch (op)
52595259 {
5260Added line. case GET_AS_BASE_SIZE:
5261Added line. {
5262Added line. if (!CLASS_TYPE_P (t) || !COMPLETE_TYPE_P (t))
5263Added line. return -1;
5264Added line.
5265Added line. const HOST_WIDE_INT size
5266Added line. = int_size_in_bytes (CLASSTYPE_AS_BASE (t));
5267Added line. return size >= 0 && size <= INT_MAX ? size : -1;
5268Added line. }
52605269 case GET_FUNCTION_QUALIFIERS:
52615270 {
52625271 int qualifiers = 0;
@@ -5305 +5314 @@cpp_check (tree t, cpp_operation op)
53055314 return TREE_CODE (t) == TEMPLATE_DECL;
53065315 case IS_TRIVIAL:
53075316 return trivial_type_p (t);
5317Added line. case REUSES_BASE_TAIL_PADDING:
5318Added line. {
5319Added line. if (!CLASS_TYPE_P (t) || !COMPLETE_TYPE_P (t) || !TYPE_BINFO (t))
5320Added line. return 0;
5321Added line.
5322Added line. for (unsigned int i = 0;
5323Added line. i < BINFO_N_BASE_BINFOS (TYPE_BINFO (t));
5324Added line. ++i)
5325Added line. {
5326Added line. tree base_binfo = BINFO_BASE_BINFO (TYPE_BINFO (t), i);
5327Added line. if (BINFO_VIRTUAL_P (base_binfo)
5328Added line. || !tree_fits_uhwi_p (BINFO_OFFSET (base_binfo)))
5329Added line. continue;
5330Added line.
5331Added line. tree base_type = BINFO_TYPE (base_binfo);
5332Added line. const HOST_WIDE_INT data_size
5333Added line. = int_size_in_bytes (CLASSTYPE_AS_BASE (base_type));
5334Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (base_type);
5335Added line. if (data_size < 0 || object_size <= data_size)
5336Added line. continue;
5337Added line.
5338Added line. const unsigned HOST_WIDE_INT base_offset
5339Added line. = tree_to_uhwi (BINFO_OFFSET (base_binfo));
5340Added line. for (tree field = TYPE_FIELDS (t); field;
5341Added line. field = DECL_CHAIN (field))
5342Added line. if (TREE_CODE (field) == FIELD_DECL
5343Added line. && !DECL_ARTIFICIAL (field)
5344Added line. && tree_fits_uhwi_p (bit_position (field)))
5345Added line. {
5346Added line. const unsigned HOST_WIDE_INT field_bitpos
5347Added line. = tree_to_uhwi (bit_position (field));
5348Added line. if (field_bitpos
5349Added line. >= (base_offset + data_size) * BITS_PER_UNIT
5350Added line. && field_bitpos < ((base_offset + object_size)
5351Added line. * BITS_PER_UNIT))
5352Added line. return 1;
5353Added line. }
5354Added line. }
5355Added line.
5356Added line. return 0;
5357Added line. }
53085358 default:
53095359 return 0;
53105360 }
gcc/testsuite/g++.dg/ada-spec/inherited-tail-padding.C +114โˆ’0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/inherited-tail-padding.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-require-effective-target lp64 } */
3Added line. /* { dg-options "-fdump-ada-spec-slim" } */
4Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "for Tail_Base'Size use 96;" } } */
5Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "for Tail_Base'Object_Size use 128;" } } */
6Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "value_u at 8 range 0 .. 31;" } } */
7Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "extra_u at 12 range 0 .. 31;" } } */
8Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "for Plain_Base'Size use 24;" } } */
9Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "derived_char at 3 range 0 .. 7;" } } */
10Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "extra at 7 range 0 .. 7;" } } */
11Added line.
12Added line. class Tail_Base
13Added line. {
14Added line. public:
15Added line. Tail_Base (int value);
16Added line. virtual ~Tail_Base ();
17Added line. int value () const { return value_; }
18Added line.
19Added line. protected:
20Added line. int value_;
21Added line. };
22Added line.
23Added line. class Tail_Derived : public Tail_Base
24Added line. {
25Added line. public:
26Added line. Tail_Derived (int value, int extra);
27Added line. ~Tail_Derived () override;
28Added line. int extra () const { return extra_; }
29Added line.
30Added line. private:
31Added line. int extra_;
32Added line. };
33Added line.
34Added line. Tail_Base::Tail_Base (int value) : value_ (value) {}
35Added line. Tail_Base::~Tail_Base () = default;
36Added line. Tail_Derived::Tail_Derived (int value, int extra)
37Added line. : Tail_Base (value), extra_ (extra) {}
38Added line. Tail_Derived::~Tail_Derived () = default;
39Added line.
40Added line. extern "C" unsigned long
41Added line. cpp_base_size ()
42Added line. {
43Added line. return sizeof (Tail_Base);
44Added line. }
45Added line.
46Added line. extern "C" unsigned long
47Added line. cpp_derived_size ()
48Added line. {
49Added line. return sizeof (Tail_Derived);
50Added line. }
51Added line.
52Added line. extern "C" int
53Added line. cpp_value (const Tail_Derived *object)
54Added line. {
55Added line. return object->value ();
56Added line. }
57Added line.
58Added line. extern "C" int
59Added line. cpp_extra (const Tail_Derived *object)
60Added line. {
61Added line. return object->extra ();
62Added line. }
63Added line.
64Added line. struct Plain_Base
65Added line. {
66Added line. short base_short;
67Added line. char base_char;
68Added line. Plain_Base () : base_short (1), base_char (2) {}
69Added line. };
70Added line.
71Added line. struct Plain_Derived : Plain_Base
72Added line. {
73Added line. char derived_char;
74Added line. Plain_Derived () : derived_char (3) {}
75Added line. };
76Added line.
77Added line. struct Plain_Left
78Added line. {
79Added line. short left_short;
80Added line. char left_char;
81Added line. Plain_Left () : left_short (4), left_char (5) {}
82Added line. };
83Added line.
84Added line. struct Plain_Right
85Added line. {
86Added line. short right_short;
87Added line. char right_char;
88Added line. Plain_Right () : right_short (6), right_char (7) {}
89Added line. };
90Added line.
91Added line. struct Plain_Both : Plain_Left, Plain_Right
92Added line. {
93Added line. char extra;
94Added line. Plain_Both () : extra (8) {}
95Added line. };
96Added line.
97Added line. extern "C" Plain_Derived *cpp_plain_create () { return new Plain_Derived; }
98Added line. extern "C" void cpp_plain_delete (Plain_Derived *p) { delete p; }
99Added line. extern "C" unsigned long cpp_plain_size () { return sizeof (Plain_Derived); }
100Added line. extern "C" int cpp_plain_values (const Plain_Derived *p)
101Added line. {
102Added line. return p->base_short + p->base_char + p->derived_char;
103Added line. }
104Added line.
105Added line. extern "C" Plain_Both *cpp_both_create () { return new Plain_Both; }
106Added line. extern "C" void cpp_both_delete (Plain_Both *p) { delete p; }
107Added line. extern "C" unsigned long cpp_both_plain_size () { return sizeof (Plain_Both); }
108Added line. extern "C" int cpp_both_values (const Plain_Both *p)
109Added line. {
110Added line. return p->left_short + p->left_char + p->right_short
111Added line. + p->right_char + p->extra;
112Added line. }
113Added line.
114Added line. /* { dg-final { cleanup-ada-spec } } */
115

Variant gcc-15-16

Applies to 15.3.0, 16.1.0, 16.2.0. Source flavors: linux, darwin_arm64.

+275 −1 4 files

Download the patch

gcc/c-family/c-ada-spec.cc +108โˆ’0modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2875 +2875 @@dump_ada_methods (pretty_printer *pp, tree node, int spc)
28752875 return 1;
28762876}
28772877
2878Added line. /* Return true if NODE needs an explicit Ada layout to describe C++ tail
2879Added line. padding, either because its object size exceeds its size as a base class or
2880Added line. because it places a field in a direct base class's tail padding. */
2881Added line.
2882Added line. static bool
2883Added line. needs_tail_padding_layout (tree node)
2884Added line. {
2885Added line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (node))
2886Added line. return false;
2887Added line.
2888Added line. const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2889Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2890Added line. return (data_size >= 0 && object_size > data_size)
2891Added line. || cpp_check (node, REUSES_BASE_TAIL_PADDING);
2892Added line. }
2893Added line.
2894Added line. /* Return true if all of NODE's fields that are visible in the generated Ada
2895Added line. record have constant positions and sizes suitable for component clauses. */
2896Added line.
2897Added line. static bool
2898Added line. has_constant_field_layout (tree node)
2899Added line. {
2900Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2901Added line. if (TREE_CODE (field) == FIELD_DECL
2902Added line. && DECL_NAME (field)
2903Added line. && !DECL_VIRTUAL_P (field)
2904Added line. && (!DECL_FIELD_OFFSET (field)
2905Added line. || !DECL_FIELD_BIT_OFFSET (field)
2906Added line. || !DECL_SIZE (field)
2907Added line. || !tree_fits_uhwi_p (DECL_FIELD_OFFSET (field))
2908Added line. || !tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (field))
2909Added line. || !tree_fits_uhwi_p (DECL_SIZE (field))
2910Added line. || tree_to_uhwi (DECL_SIZE (field)) == 0))
2911Added line. return false;
2912Added line.
2913Added line. return true;
2914Added line. }
2915Added line.
2916Added line. /* Dump the size and component clauses needed to preserve NODE's C++ tail
2917Added line. padding layout. TYPE is the declaration used for Ada name qualification. */
2918Added line.
2919Added line. static void
2920Added line. dump_ada_tail_padding_layout (pretty_printer *pp, tree node, tree type, int spc)
2921Added line. {
2922Added line. const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2923Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2924Added line.
2925Added line. gcc_assert (data_size >= 0 && object_size >= 0);
2926Added line.
2927Added line. newline_and_indent (pp, spc);
2928Added line. pp_string (pp, "for ");
2929Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
2930Added line. pp_string (pp, "'Size use ");
2931Added line. pp_wide_integer (pp, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
2932Added line. pp_semicolon (pp);
2933Added line.
2934Added line. newline_and_indent (pp, spc);
2935Added line. pp_string (pp, "for ");
2936Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
2937Added line. pp_string (pp, "'Object_Size use ");
2938Added line. pp_wide_integer (pp, object_size * BITS_PER_UNIT);
2939Added line. pp_semicolon (pp);
2940Added line.
2941Added line. newline_and_indent (pp, spc);
2942Added line. pp_string (pp, "for ");
2943Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
2944Added line. pp_string (pp, " use record");
2945Added line.
2946Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2947Added line. if (TREE_CODE (field) == FIELD_DECL
2948Added line. && DECL_NAME (field)
2949Added line. && !DECL_VIRTUAL_P (field))
2950Added line. {
2951Added line. const unsigned HOST_WIDE_INT bitpos
2952Added line. = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
2953Added line. + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
2954Added line. const unsigned HOST_WIDE_INT position = bitpos / BITS_PER_UNIT;
2955Added line. const unsigned HOST_WIDE_INT first_bit = bitpos % BITS_PER_UNIT;
2956Added line. const unsigned HOST_WIDE_INT last_bit
2957Added line. = first_bit + tree_to_uhwi (DECL_SIZE (field)) - 1;
2958Added line.
2959Added line. newline_and_indent (pp, spc + INDENT_INCR);
2960Added line. dump_ada_decl_name (pp, field, false);
2961Added line. pp_string (pp, " at ");
2962Added line. pp_unsigned_wide_integer (pp, position);
2963Added line. pp_string (pp, " range ");
2964Added line. pp_unsigned_wide_integer (pp, first_bit);
2965Added line. pp_string (pp, " .. ");
2966Added line. pp_unsigned_wide_integer (pp, last_bit);
2967Added line. pp_semicolon (pp);
2968Added line. }
2969Added line.
2970Added line. newline_and_indent (pp, spc);
2971Added line. pp_string (pp, "end record;");
2972Added line. }
2973Added line.
28782974/* Dump in PP a forward declaration for TYPE present inside T.
28792975 SPC is the indentation level. */
28802976
@@ -3897 +3993 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
38973993
38983994 need_semicolon = !dump_ada_methods (pp, node, spc);
38993995
3996Added line. if (needs_tail_padding_layout (node) && has_constant_field_layout (node))
3997Added line. {
3998Added line. if (need_semicolon)
3999Added line. {
4000Added line. need_semicolon = false;
4001Added line. pp_semicolon (pp);
4002Added line. }
4003Added line.
4004Added line. pp_newline (pp);
4005Added line. dump_ada_tail_padding_layout (pp, node, type, spc);
4006Added line. }
4007Added line.
39004008 /* Print the static fields of the structure, if any. */
39014009 for (tree tmp = TYPE_FIELDS (node); tmp; tmp = TREE_CHAIN (tmp))
39024010 {
gcc/c-family/c-ada-spec.h +3โˆ’1modified
Unified diff for gcc/c-family/c-ada-spec.h: original line, patched line, change, source
@@ -25 +25 @@along with GCC; see the file COPYING3. If not see
2525/* In c-ada-spec.cc */
2626
2727enum cpp_operation {
28Added line. GET_AS_BASE_SIZE,
2829 GET_FUNCTION_QUALIFIERS,
2930 HAS_DEPENDENT_TEMPLATE_ARGS,
3031 IS_ABSTRACT,
@@ -34 +35 @@enum cpp_operation {
3435 IS_COPY_CONSTRUCTOR,
3536 IS_MOVE_CONSTRUCTOR,
3637 IS_TEMPLATE,
37Removed line. IS_TRIVIAL
38Added line. IS_TRIVIAL,
39Added line. REUSES_BASE_TAIL_PADDING
3840};
3941
4042enum cpp_function_qualifier {
gcc/cp/decl2.cc +50โˆ’0modified
Unified diff for gcc/cp/decl2.cc: original line, patched line, change, source
@@ -5257 +5257 @@cpp_check (tree t, cpp_operation op)
52575257{
52585258 switch (op)
52595259 {
5260Added line. case GET_AS_BASE_SIZE:
5261Added line. {
5262Added line. if (!CLASS_TYPE_P (t) || !COMPLETE_TYPE_P (t))
5263Added line. return -1;
5264Added line.
5265Added line. const HOST_WIDE_INT size
5266Added line. = int_size_in_bytes (CLASSTYPE_AS_BASE (t));
5267Added line. return size >= 0 && size <= INT_MAX ? size : -1;
5268Added line. }
52605269 case GET_FUNCTION_QUALIFIERS:
52615270 {
52625271 int qualifiers = 0;
@@ -5305 +5314 @@cpp_check (tree t, cpp_operation op)
53055314 return TREE_CODE (t) == TEMPLATE_DECL;
53065315 case IS_TRIVIAL:
53075316 return trivial_type_p (t);
5317Added line. case REUSES_BASE_TAIL_PADDING:
5318Added line. {
5319Added line. if (!CLASS_TYPE_P (t) || !COMPLETE_TYPE_P (t) || !TYPE_BINFO (t))
5320Added line. return 0;
5321Added line.
5322Added line. for (unsigned int i = 0;
5323Added line. i < BINFO_N_BASE_BINFOS (TYPE_BINFO (t));
5324Added line. ++i)
5325Added line. {
5326Added line. tree base_binfo = BINFO_BASE_BINFO (TYPE_BINFO (t), i);
5327Added line. if (BINFO_VIRTUAL_P (base_binfo)
5328Added line. || !tree_fits_uhwi_p (BINFO_OFFSET (base_binfo)))
5329Added line. continue;
5330Added line.
5331Added line. tree base_type = BINFO_TYPE (base_binfo);
5332Added line. const HOST_WIDE_INT data_size
5333Added line. = int_size_in_bytes (CLASSTYPE_AS_BASE (base_type));
5334Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (base_type);
5335Added line. if (data_size < 0 || object_size <= data_size)
5336Added line. continue;
5337Added line.
5338Added line. const unsigned HOST_WIDE_INT base_offset
5339Added line. = tree_to_uhwi (BINFO_OFFSET (base_binfo));
5340Added line. for (tree field = TYPE_FIELDS (t); field;
5341Added line. field = DECL_CHAIN (field))
5342Added line. if (TREE_CODE (field) == FIELD_DECL
5343Added line. && !DECL_ARTIFICIAL (field)
5344Added line. && tree_fits_uhwi_p (bit_position (field)))
5345Added line. {
5346Added line. const unsigned HOST_WIDE_INT field_bitpos
5347Added line. = tree_to_uhwi (bit_position (field));
5348Added line. if (field_bitpos
5349Added line. >= (base_offset + data_size) * BITS_PER_UNIT
5350Added line. && field_bitpos < ((base_offset + object_size)
5351Added line. * BITS_PER_UNIT))
5352Added line. return 1;
5353Added line. }
5354Added line. }
5355Added line.
5356Added line. return 0;
5357Added line. }
53085358 default:
53095359 return 0;
53105360 }
gcc/testsuite/g++.dg/ada-spec/inherited-tail-padding.C +114โˆ’0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/inherited-tail-padding.C: original line, patched line, change, source
@@ -0 +1 @@
1Added line. /* { dg-do compile } */
2Added line. /* { dg-require-effective-target lp64 } */
3Added line. /* { dg-options "-fdump-ada-spec-slim" } */
4Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "for Tail_Base'Size use 96;" } } */
5Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "for Tail_Base'Object_Size use 128;" } } */
6Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "value_u at 8 range 0 .. 31;" } } */
7Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "extra_u at 12 range 0 .. 31;" } } */
8Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "for Plain_Base'Size use 24;" } } */
9Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "derived_char at 3 range 0 .. 7;" } } */
10Added line. /* { dg-final { scan-file inherited_tail_padding_c.ads "extra at 7 range 0 .. 7;" } } */
11Added line.
12Added line. class Tail_Base
13Added line. {
14Added line. public:
15Added line. Tail_Base (int value);
16Added line. virtual ~Tail_Base ();
17Added line. int value () const { return value_; }
18Added line.
19Added line. protected:
20Added line. int value_;
21Added line. };
22Added line.
23Added line. class Tail_Derived : public Tail_Base
24Added line. {
25Added line. public:
26Added line. Tail_Derived (int value, int extra);
27Added line. ~Tail_Derived () override;
28Added line. int extra () const { return extra_; }
29Added line.
30Added line. private:
31Added line. int extra_;
32Added line. };
33Added line.
34Added line. Tail_Base::Tail_Base (int value) : value_ (value) {}
35Added line. Tail_Base::~Tail_Base () = default;
36Added line. Tail_Derived::Tail_Derived (int value, int extra)
37Added line. : Tail_Base (value), extra_ (extra) {}
38Added line. Tail_Derived::~Tail_Derived () = default;
39Added line.
40Added line. extern "C" unsigned long
41Added line. cpp_base_size ()
42Added line. {
43Added line. return sizeof (Tail_Base);
44Added line. }
45Added line.
46Added line. extern "C" unsigned long
47Added line. cpp_derived_size ()
48Added line. {
49Added line. return sizeof (Tail_Derived);
50Added line. }
51Added line.
52Added line. extern "C" int
53Added line. cpp_value (const Tail_Derived *object)
54Added line. {
55Added line. return object->value ();
56Added line. }
57Added line.
58Added line. extern "C" int
59Added line. cpp_extra (const Tail_Derived *object)
60Added line. {
61Added line. return object->extra ();
62Added line. }
63Added line.
64Added line. struct Plain_Base
65Added line. {
66Added line. short base_short;
67Added line. char base_char;
68Added line. Plain_Base () : base_short (1), base_char (2) {}
69Added line. };
70Added line.
71Added line. struct Plain_Derived : Plain_Base
72Added line. {
73Added line. char derived_char;
74Added line. Plain_Derived () : derived_char (3) {}
75Added line. };
76Added line.
77Added line. struct Plain_Left
78Added line. {
79Added line. short left_short;
80Added line. char left_char;
81Added line. Plain_Left () : left_short (4), left_char (5) {}
82Added line. };
83Added line.
84Added line. struct Plain_Right
85Added line. {
86Added line. short right_short;
87Added line. char right_char;
88Added line. Plain_Right () : right_short (6), right_char (7) {}
89Added line. };
90Added line.
91Added line. struct Plain_Both : Plain_Left, Plain_Right
92Added line. {
93Added line. char extra;
94Added line. Plain_Both () : extra (8) {}
95Added line. };
96Added line.
97Added line. extern "C" Plain_Derived *cpp_plain_create () { return new Plain_Derived; }
98Added line. extern "C" void cpp_plain_delete (Plain_Derived *p) { delete p; }
99Added line. extern "C" unsigned long cpp_plain_size () { return sizeof (Plain_Derived); }
100Added line. extern "C" int cpp_plain_values (const Plain_Derived *p)
101Added line. {
102Added line. return p->base_short + p->base_char + p->derived_char;
103Added line. }
104Added line.
105Added line. extern "C" Plain_Both *cpp_both_create () { return new Plain_Both; }
106Added line. extern "C" void cpp_both_delete (Plain_Both *p) { delete p; }
107Added line. extern "C" unsigned long cpp_both_plain_size () { return sizeof (Plain_Both); }
108Added line. extern "C" int cpp_both_values (const Plain_Both *p)
109Added line. {
110Added line. return p->left_short + p->left_char + p->right_short
111Added line. + p->right_char + p->extra;
112Added line. }
113Added line.
114Added line. /* { dg-final { cleanup-ada-spec } } */
115

Tests.

cpp_tail_padding.adb Ada · 32 lines
--  { dg-do compile }
--  { dg-require-effective-target lp64 }

procedure Cpp_Tail_Padding is
   pragma Warnings (Off);

   type Tail_Base is tagged limited record
      Value : Integer;
   end record
   with Import => True,
        Convention => CPP;

   for Tail_Base'Size use 96;
   for Tail_Base'Object_Size use 128;
   for Tail_Base use record
      Value at 8 range 0 .. 31;
   end record;

   type Tail_Derived is limited new Tail_Base with record
      Extra : Integer;
   end record
   with Import => True,
        Convention => CPP;

   for Tail_Derived'Size use 128;
   for Tail_Derived'Object_Size use 128;
   for Tail_Derived use record
      Extra at 12 range 0 .. 31;
   end record;
begin
   null;
end Cpp_Tail_Padding;

Download · View in repository

inherited-tail-padding.C C++ · 114 lines
/* { dg-do compile } */
/* { dg-require-effective-target lp64 } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file inherited_tail_padding_c.ads "for Tail_Base'Size use 96;" } } */
/* { dg-final { scan-file inherited_tail_padding_c.ads "for Tail_Base'Object_Size use 128;" } } */
/* { dg-final { scan-file inherited_tail_padding_c.ads "value_u at 8 range 0 .. 31;" } } */
/* { dg-final { scan-file inherited_tail_padding_c.ads "extra_u at 12 range 0 .. 31;" } } */
/* { dg-final { scan-file inherited_tail_padding_c.ads "for Plain_Base'Size use 24;" } } */
/* { dg-final { scan-file inherited_tail_padding_c.ads "derived_char at 3 range 0 .. 7;" } } */
/* { dg-final { scan-file inherited_tail_padding_c.ads "extra at 7 range 0 .. 7;" } } */

class Tail_Base
{
public:
  Tail_Base (int value);
  virtual ~Tail_Base ();
  int value () const { return value_; }

protected:
  int value_;
};

class Tail_Derived : public Tail_Base
{
public:
  Tail_Derived (int value, int extra);
  ~Tail_Derived () override;
  int extra () const { return extra_; }

private:
  int extra_;
};

Tail_Base::Tail_Base (int value) : value_ (value) {}
Tail_Base::~Tail_Base () = default;
Tail_Derived::Tail_Derived (int value, int extra)
  : Tail_Base (value), extra_ (extra) {}
Tail_Derived::~Tail_Derived () = default;

extern "C" unsigned long
cpp_base_size ()
{
  return sizeof (Tail_Base);
}

extern "C" unsigned long
cpp_derived_size ()
{
  return sizeof (Tail_Derived);
}

extern "C" int
cpp_value (const Tail_Derived *object)
{
  return object->value ();
}

extern "C" int
cpp_extra (const Tail_Derived *object)
{
  return object->extra ();
}

struct Plain_Base
{
  short base_short;
  char base_char;
  Plain_Base () : base_short (1), base_char (2) {}
};

struct Plain_Derived : Plain_Base
{
  char derived_char;
  Plain_Derived () : derived_char (3) {}
};

struct Plain_Left
{
  short left_short;
  char left_char;
  Plain_Left () : left_short (4), left_char (5) {}
};

struct Plain_Right
{
  short right_short;
  char right_char;
  Plain_Right () : right_short (6), right_char (7) {}
};

struct Plain_Both : Plain_Left, Plain_Right
{
  char extra;
  Plain_Both () : extra (8) {}
};

extern "C" Plain_Derived *cpp_plain_create () { return new Plain_Derived; }
extern "C" void cpp_plain_delete (Plain_Derived *p) { delete p; }
extern "C" unsigned long cpp_plain_size () { return sizeof (Plain_Derived); }
extern "C" int cpp_plain_values (const Plain_Derived *p)
{
  return p->base_short + p->base_char + p->derived_char;
}

extern "C" Plain_Both *cpp_both_create () { return new Plain_Both; }
extern "C" void cpp_both_delete (Plain_Both *p) { delete p; }
extern "C" unsigned long cpp_both_plain_size () { return sizeof (Plain_Both); }
extern "C" int cpp_both_values (const Plain_Both *p)
{
  return p->left_short + p->left_char + p->right_short
    + p->right_char + p->extra;
}

/* { dg-final { cleanup-ada-spec } } */

Download · View in repository

inherited_tail_padding_consumer.adb Ada · 60 lines
with Ada.Text_IO;
with Inherited_Tail_Padding_C;
with Interfaces.C; use Interfaces.C;
with System;

procedure Inherited_Tail_Padding_Consumer is
   package Bindings renames Inherited_Tail_Padding_C;

   Object : aliased Bindings.Class_Tail_Derived.Tail_Derived :=
     Bindings.Class_Tail_Derived.New_Tail_Derived (31, 47);
   Ada_Base_Bytes : constant unsigned_long :=
     unsigned_long
       (Bindings.Class_Tail_Base.Tail_Base'Object_Size /
        System.Storage_Unit);
   Ada_Derived_Bytes : constant unsigned_long :=
     unsigned_long
       (Bindings.Class_Tail_Derived.Tail_Derived'Object_Size /
        System.Storage_Unit);
   Plain : access Bindings.Class_Plain_Derived.Plain_Derived :=
     Bindings.cpp_plain_create;
   Both : access Bindings.Class_Plain_Both.Plain_Both :=
     Bindings.cpp_both_create;
begin
   Object.parent.value_u := 211;
   Object.extra_u := 307;

   if Ada_Base_Bytes = Bindings.cpp_base_size
     and then Ada_Derived_Bytes = Bindings.cpp_derived_size
     and then Bindings.cpp_value (Object'Access) = 211
     and then Bindings.cpp_extra (Object'Access) = 307
     and then unsigned_long
       (Bindings.Class_Plain_Derived.Plain_Derived'Object_Size /
        System.Storage_Unit) = Bindings.cpp_plain_size
     and then unsigned_long
       (Bindings.Class_Plain_Both.Plain_Both'Object_Size /
        System.Storage_Unit) = Bindings.cpp_both_plain_size
   then
      Plain.parent.base_short := 11;
      Plain.parent.base_char := Interfaces.C.char'Val (13);
      Plain.derived_char := Interfaces.C.char'Val (17);
      Both.parent.left_short := 19;
      Both.parent.left_char := Interfaces.C.char'Val (23);
      Both.field_2.right_short := 29;
      Both.field_2.right_char := Interfaces.C.char'Val (31);
      Both.extra := Interfaces.C.char'Val (37);

      if Bindings.cpp_plain_values (Plain) /= 41
        or else Bindings.cpp_both_values (Both) /= 139
      then
         raise Program_Error with "non-polymorphic tail padding mismatch";
      end if;

      Ada.Text_IO.Put_Line ("MATCH C++ Ada inherited tail padding");
   else
      Ada.Text_IO.Put_Line ("MISMATCH C++ Ada inherited tail padding");
   end if;

   Bindings.cpp_plain_delete (Plain);
   Bindings.cpp_both_delete (Both);
end Inherited_Tail_Padding_Consumer;

Download · View in repository

inherited_tail_padding_unpatched_consumer.adb Ada · 32 lines
with Ada.Text_IO;
with Inherited_Tail_Padding_C;
with Interfaces.C; use Interfaces.C;
with System;

procedure Inherited_Tail_Padding_Consumer is
   package Bindings renames Inherited_Tail_Padding_C;

   Object : aliased Bindings.Class_Tail_Derived.Tail_Derived :=
     Bindings.Class_Tail_Derived.New_Tail_Derived (31, 47);
   Ada_Base_Bytes : constant unsigned_long :=
     unsigned_long
       (Bindings.Class_Tail_Base.Tail_Base'Object_Size /
        System.Storage_Unit);
   Ada_Derived_Bytes : constant unsigned_long :=
     unsigned_long
       (Bindings.Class_Tail_Derived.Tail_Derived'Object_Size /
        System.Storage_Unit);
begin
   Object.value_u := 211;
   Object.extra_u := 307;

   if Ada_Base_Bytes = Bindings.cpp_base_size
     and then Ada_Derived_Bytes = Bindings.cpp_derived_size
     and then Bindings.cpp_value (Object'Access) = 211
     and then Bindings.cpp_extra (Object'Access) = 307
   then
      Ada.Text_IO.Put_Line ("MATCH C++ Ada inherited tail padding");
   else
      Ada.Text_IO.Put_Line ("MISMATCH C++ Ada inherited tail padding");
   end if;
end Inherited_Tail_Padding_Consumer;

Download · View in repository

run-test.sh shell · 72 lines
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -ne 3 ]]; then
  echo "usage: $0 TOOLCHAIN_ROOT GCC_VERSION unpatched|patched" >&2
  exit 2
fi

root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
source "$root/scripts/regression-common.sh"
resolve_regression_toolchain "$1"
version=$2
state=$3
[[ "$state" == unpatched || "$state" == patched ]] || {
  echo "error: state must be unpatched or patched" >&2
  exit 2
}

gxx="$REGRESSION_TOOLCHAIN/bin/g++"
[[ -x "$gxx" ]] || {
  echo "error: no g++ in $REGRESSION_TOOLCHAIN" >&2
  exit 1
}

cxx_fixture="$root/bundles/cxx-ada-inherited-tail-padding/tests/inherited-tail-padding.C"
if [[ "$state" == unpatched ]]; then
  ada_fixture="$root/bundles/cxx-ada-inherited-tail-padding/tests/inherited_tail_padding_unpatched_consumer.adb"
else
  ada_fixture="$root/bundles/cxx-ada-inherited-tail-padding/tests/inherited_tail_padding_consumer.adb"
fi
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-tail-padding.XXXXXX")
trap 'rm -rf "$work"' EXIT

for optimization in 0 2; do
  case_dir="$work/O$optimization"
  mkdir -p "$case_dir"
  cp "$cxx_fixture" "$case_dir/"
  cp "$ada_fixture" "$case_dir/inherited_tail_padding_consumer.adb"
  (
    cd "$case_dir"
    "${REGRESSION_ENV[@]}" "$gxx" -c "-O$optimization" \
      -fdump-ada-spec-slim inherited-tail-padding.C
    "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f \
      "-O$optimization" inherited_tail_padding_consumer.adb \
      -largs inherited-tail-padding.o -lstdc++
    "${REGRESSION_ENV[@]}" ./inherited_tail_padding_consumer
  ) >"$case_dir/output.log" 2>&1 || {
    cat "$case_dir/output.log"
    exit 1
  }

  spec="$case_dir/inherited_tail_padding_c.ads"
  if [[ "$state" == unpatched ]]; then
    grep -F "MISMATCH C++ Ada inherited tail padding" "$case_dir/output.log"
    if grep -Fq "for Tail_Base'Size use" "$spec"; then
      echo "error: unpatched mapper unexpectedly emitted tail-padding layout" >&2
      exit 1
    fi
    echo "cxx-ada-inherited-tail-padding -O$optimization: expected mismatch (GCC $version)"
    continue
  fi

  grep -F "for Tail_Base'Size use" "$spec"
  grep -F "for Tail_Base'Object_Size use" "$spec"
  grep -F "value_u at" "$spec"
  grep -F "extra_u at" "$spec"
  grep -F "for Plain_Base'Size use 24;" "$spec"
  grep -F "derived_char at 3 range 0 .. 7;" "$spec"
  grep -F "extra at 7 range 0 .. 7;" "$spec"
  grep -F "MATCH C++ Ada inherited tail padding" "$case_dir/output.log"
  echo "cxx-ada-inherited-tail-padding -O$optimization: patched (GCC $version)"
done

Download · View in repository

Commands.

Apply the patch
patch --fuzz=0 -p1 -i bundles/cxx-ada-inherited-tail-padding/patches/VARIANT.patch
Build the compiler
PATH=/path/to/bootstrap/bin:$PATH ./scripts/build-gnat.sh SOURCE BUILD INSTALL
Run the regression
./scripts/run-regressions.sh INSTALL 1.2.0 GCC_MAJOR patched

Metadata.