cxx-ada-virtual-diamond-layout

C++ Ada virtual-diamond base storage

The C++ Ada mapper uses complete virtual-base class types for shortened direct base subobjects in a virtual diamond.

StagedApplies in patchset orderStaged in 1.2.0

Why this is staged.

Shortened as-base views for diamond legs depend on the virtual-base layout representation and share its C++ ABI coupling. Held out of the published patchset for separate upstream review.

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.

A complete C++ object with a virtual base contains that shared base, while the same class used as a nonvirtual base subobject does not. In a diamond, the mapper currently declares each direct base component using its larger complete Ada type, even though C++ allocated only its shortened as-base storage.

For example, on the supported 64-bit Itanium ABI targets each direct base is 16 bytes as a complete object but only 12 bytes inside Diamond:

struct Root { int root_value; };
struct Left : virtual Root { int left_value; };
struct Right : virtual Root { int right_value; };

struct Diamond : Left, Right
{
  int diamond_value;
};

The unpatched mapper uses the complete Left and Right types as components. Their duplicated virtual Root members make Ada's view larger than the C++ object and give the visible fields the wrong addresses:

type Diamond is limited record
   parent : aliased Left;
   field_2 : aliased Right;
   diamond_value : aliased int;
   field_4 : aliased Root;
end record
with Import => True,
     Convention => CPP;

The corrected output emits explicit storage-only views for the shortened base subobjects. They retain each direct field, omit the shared virtual base, and use an alignment that their 12-byte object size can satisfy:

type Left_As_Base is limited record
   left_value : aliased int;
end record
with Convention => C_Pass_By_Copy;
for Left_As_Base'Object_Size use 96;
for Left_As_Base'Alignment use 4;
for Left_As_Base use record
   left_value at 8 range 0 .. 31;
end record;

type Diamond is limited record
   parent : aliased Left_As_Base;
   field_2 : aliased Right_As_Base;
   diamond_value : aliased int;
   field_4 : aliased Root;
end record
with Import => True,
     Convention => CPP;

The executable regression runs at -O0 and -O2. C++ constructs a real diamond and reports its complete size, alignment, and four visible field offsets. Ada verifies those values, writes through both shortened base views, the direct member, and the single shared virtual base, and C++ reads all four values back. Polymorphic concrete secondary bases remain a separate facade boundary because Ada cannot express their inheritance graph.

Patch.

Variant gcc-13-14

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

+328 −2 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +214−2modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2856 +2856 @@is_ada_record_field (tree field)
28562856 return DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field));
28572857}
28582858
2859Added line. /* Return true if NODE needs a distinct storage type when used as a base
2860Added line. subobject. A non-polymorphic class with virtual bases contains those bases
2861Added line. as a complete object, but excludes them from its as-base size. */
2862Added line.
2863Added line. static bool
2864Added line. needs_virtual_as_base_type (tree node)
2865Added line. {
2866Added line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (node) || is_tagged_type (node)
2867Added line. || !cpp_check (node, HAS_VIRTUAL_BASE))
2868Added line. return false;
2869Added line.
2870Added line. const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2871Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2872Added line. return data_size > 0 && object_size > data_size;
2873Added line. }
2874Added line.
2875Added line. /* Return true if FIELD stores only the as-base portion of a virtual-base
2876Added line. class. The complete Ada type has a larger Object_Size and cannot be used
2877Added line. for this component. */
2878Added line.
2879Added line. static bool
2880Added line. is_shortened_virtual_base_field (tree field)
2881Added line. {
2882Added line. if (TREE_CODE (field) != FIELD_DECL || DECL_NAME (field)
2883Added line. || !needs_virtual_as_base_type (TREE_TYPE (field))
2884Added line. || !DECL_SIZE (field) || !tree_fits_uhwi_p (DECL_SIZE (field)))
2885Added line. return false;
2886Added line.
2887Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (TREE_TYPE (field));
2888Added line. return object_size > 0
2889Added line. && tree_to_uhwi (DECL_SIZE (field))
2890Added line. < (unsigned HOST_WIDE_INT) object_size * BITS_PER_UNIT;
2891Added line. }
2892Added line.
2893Added line. /* Dump FIELD's named type, selecting the shortened storage type when FIELD
2894Added line. is a base subobject that excludes its type's virtual bases. */
2895Added line.
2896Added line. static void
2897Added line. dump_ada_record_field_type (pretty_printer *buffer, tree field)
2898Added line. {
2899Added line. dump_ada_decl_name (buffer, TYPE_NAME (TREE_TYPE (field)), false);
2900Added line. if (is_shortened_virtual_base_field (field))
2901Added line. pp_string (buffer, "_As_Base");
2902Added line. }
2903Added line.
2904Added line. /* Return true if FIELD lies wholly inside NODE's C++ as-base size. */
2905Added line.
2906Added line. static bool
2907Added line. field_fits_as_base (tree node, tree field)
2908Added line. {
2909Added line. const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2910Added line. if (data_size < 0 || !is_ada_record_field (node, field)
2911Added line. || !DECL_FIELD_OFFSET (field) || !DECL_FIELD_BIT_OFFSET (field)
2912Added line. || !DECL_SIZE (field)
2913Added line. || !tree_fits_uhwi_p (DECL_FIELD_OFFSET (field))
2914Added line. || !tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (field))
2915Added line. || !tree_fits_uhwi_p (DECL_SIZE (field)))
2916Added line. return false;
2917Added line.
2918Added line. const unsigned HOST_WIDE_INT bitpos
2919Added line. = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
2920Added line. + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
2921Added line. const unsigned HOST_WIDE_INT size = tree_to_uhwi (DECL_SIZE (field));
2922Added line. return size > 0 && bitpos + size
2923Added line. <= (unsigned HOST_WIDE_INT) data_size * BITS_PER_UNIT;
2924Added line. }
2925Added line.
2926Added line. /* Return the strongest Ada alignment that divides NODE's as-base size. The
2927Added line. complete C++ alignment can exceed that storage size because reusable tail
2928Added line. padding is not part of a base subobject. */
2929Added line.
2930Added line. static unsigned int
2931Added line. virtual_as_base_alignment (tree node, unsigned int data_size)
2932Added line. {
2933Added line. unsigned int alignment = TYPE_ALIGN (node) / BITS_PER_UNIT;
2934Added line. while (alignment > 1 && data_size % alignment != 0)
2935Added line. alignment >>= 1;
2936Added line. return alignment;
2937Added line. }
2938Added line.
2939Added line. /* Dump a storage-only Ada type for NODE's C++ as-base representation. This
2940Added line. keeps each direct field addressable while excluding shared virtual bases. */
2941Added line.
2942Added line. static void
2943Added line. dump_ada_virtual_as_base_type (pretty_printer *buffer, tree node, tree type,
2944Added line. int spc)
2945Added line. {
2946Added line. const unsigned int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2947Added line. int field_num = 0;
2948Added line.
2949Added line. pp_newline (buffer);
2950Added line. newline_and_indent (buffer, spc);
2951Added line. pp_string (buffer, "type ");
2952Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
2953Added line. pp_string (buffer, "_As_Base is record");
2954Added line. pp_newline (buffer);
2955Added line.
2956Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
2957Added line. if (field_fits_as_base (node, field))
2958Added line. {
2959Added line. if (!DECL_NAME (field))
2960Added line. {
2961Added line. if (is_tagged_type (TREE_TYPE (field)))
2962Added line. continue;
2963Added line.
2964Added line. INDENT (spc + INDENT_INCR);
2965Added line. if (field_num == 0)
2966Added line. pp_string (buffer, "parent : ");
2967Added line. else
2968Added line. {
2969Added line. char buf[32];
2970Added line. sprintf (buf, "field_%d : ", field_num + 1);
2971Added line. pp_string (buffer, buf);
2972Added line. }
2973Added line. dump_ada_record_field_type (buffer, field);
2974Added line. pp_semicolon (buffer);
2975Added line. pp_newline (buffer);
2976Added line. field_num++;
2977Added line. }
2978Added line. else
2979Added line. {
2980Added line. INDENT (spc + INDENT_INCR);
2981Added line. dump_ada_node (buffer, field, type, spc, false, true);
2982Added line. pp_string (buffer, " : ");
2983Added line. dump_ada_record_field_type (buffer, field);
2984Added line. pp_string (buffer, "; -- ");
2985Added line. dump_sloc (buffer, field);
2986Added line. pp_newline (buffer);
2987Added line. field_num++;
2988Added line. }
2989Added line. }
2990Added line.
2991Added line. if (field_num == 0)
2992Added line. {
2993Added line. INDENT (spc + INDENT_INCR);
2994Added line. pp_string (buffer, "null;");
2995Added line. pp_newline (buffer);
2996Added line. }
2997Added line.
2998Added line. INDENT (spc);
2999Added line. pp_string (buffer, "end record");
3000Added line. newline_and_indent (buffer, spc);
3001Added line. pp_string (buffer, "with Convention => C_Pass_By_Copy;");
3002Added line.
3003Added line. newline_and_indent (buffer, spc);
3004Added line. pp_string (buffer, "for ");
3005Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
3006Added line. pp_string (buffer, "_As_Base'Size use ");
3007Added line. pp_wide_integer (buffer, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
3008Added line. pp_semicolon (buffer);
3009Added line.
3010Added line. newline_and_indent (buffer, spc);
3011Added line. pp_string (buffer, "for ");
3012Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
3013Added line. pp_string (buffer, "_As_Base'Object_Size use ");
3014Added line. pp_wide_integer (buffer, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
3015Added line. pp_semicolon (buffer);
3016Added line.
3017Added line. newline_and_indent (buffer, spc);
3018Added line. pp_string (buffer, "for ");
3019Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
3020Added line. pp_string (buffer, "_As_Base'Alignment use ");
3021Added line. pp_decimal_int (buffer, virtual_as_base_alignment (node, data_size));
3022Added line. pp_semicolon (buffer);
3023Added line.
3024Added line. newline_and_indent (buffer, spc);
3025Added line. pp_string (buffer, "for ");
3026Added line. dump_ada_node (buffer, TYPE_NAME (node), type, spc, false, true);
3027Added line. pp_string (buffer, "_As_Base use record");
3028Added line.
3029Added line. int repr_field_num = 0;
3030Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3031Added line. if (field_fits_as_base (node, field))
3032Added line. {
3033Added line. if (!DECL_NAME (field) && is_tagged_type (TREE_TYPE (field)))
3034Added line. continue;
3035Added line.
3036Added line. repr_field_num++;
3037Added line. const unsigned HOST_WIDE_INT bitpos
3038Added line. = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
3039Added line. + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
3040Added line. const unsigned HOST_WIDE_INT position = bitpos / BITS_PER_UNIT;
3041Added line. const unsigned HOST_WIDE_INT first_bit = bitpos % BITS_PER_UNIT;
3042Added line. const unsigned HOST_WIDE_INT last_bit
3043Added line. = first_bit + tree_to_uhwi (DECL_SIZE (field)) - 1;
3044Added line.
3045Added line. newline_and_indent (buffer, spc + INDENT_INCR);
3046Added line. if (DECL_NAME (field))
3047Added line. dump_ada_decl_name (buffer, field, false);
3048Added line. else if (repr_field_num == 1)
3049Added line. pp_string (buffer, "parent");
3050Added line. else
3051Added line. {
3052Added line. char buf[32];
3053Added line. sprintf (buf, "field_%d", repr_field_num);
3054Added line. pp_string (buffer, buf);
3055Added line. }
3056Added line. pp_string (buffer, " at ");
3057Added line. pp_unsigned_wide_integer (buffer, position);
3058Added line. pp_string (buffer, " range ");
3059Added line. pp_unsigned_wide_integer (buffer, first_bit);
3060Added line. pp_string (buffer, " .. ");
3061Added line. pp_unsigned_wide_integer (buffer, last_bit);
3062Added line. pp_semicolon (buffer);
3063Added line. }
3064Added line.
3065Added line. newline_and_indent (buffer, spc);
3066Added line. pp_string (buffer, "end record;");
3067Added line. }
3068Added line.
28593069/* Dump the value and object sizes of an empty C++ class. Ada's value size is
28603070 zero so an empty base consumes no storage, while Object_Size preserves the
28613071 byte occupied by a complete C++ object. */
@@ -3929 +4133 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
39294133 sprintf (buf, "field_%d : aliased ", field_num + 1);
39304134 pp_string (buffer, buf);
39314135 }
3932Removed line. dump_ada_decl_name (buffer, TYPE_NAME (TREE_TYPE (tmp)),
3933Removed line. false);
4136Added line. dump_ada_record_field_type (buffer, tmp);
39344137 pp_semicolon (buffer);
39354138 }
39364139
@@ -4056 +4259 @@dump_ada_structure (pretty_printer *buffer, tree node, tree type, bool nested,
40564259 pp_newline (buffer);
40574260 dump_ada_cpp_layout (buffer, node, type, spc,
40584261 cpp_check (node, HAS_VIRTUAL_BASE));
4262Added line.
4263Added line. if (needs_virtual_as_base_type (node))
4264Added line. dump_ada_virtual_as_base_type (buffer, node, type, spc);
40594265 }
40604266
40614267 /* Print the static fields of the structure, if any. */
gcc/testsuite/g++.dg/ada-spec/virtual-diamond-layout.C +114−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/virtual-diamond-layout.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 virtual_diamond_layout_c.ads "type Diamond_Left_As_Base" } } */
5Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "for Diamond_Left_As_Base'Object_Size use 96;" } } */
6Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "type Diamond_Right_As_Base" } } */
7Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "for Diamond_Right_As_Base'Object_Size use 96;" } } */
8Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "parent : aliased Diamond_Left_As_Base;" } } */
9Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "field_2 : aliased Diamond_Right_As_Base;" } } */
10Added line.
11Added line. struct Diamond_Root
12Added line. {
13Added line. int root_value;
14Added line. };
15Added line.
16Added line. struct Diamond_Left : virtual Diamond_Root
17Added line. {
18Added line. int left_value;
19Added line. };
20Added line.
21Added line. struct Diamond_Right : virtual Diamond_Root
22Added line. {
23Added line. int right_value;
24Added line. };
25Added line.
26Added line. struct Diamond : Diamond_Left, Diamond_Right
27Added line. {
28Added line. int diamond_value;
29Added line. Diamond (int root, int left, int right, int diamond_arg);
30Added line. };
31Added line.
32Added line. Diamond::Diamond (int root, int left, int right, int diamond_arg)
33Added line. : Diamond_Root {root}, Diamond_Left {}, Diamond_Right {},
34Added line. diamond_value {diamond_arg}
35Added line. {
36Added line. left_value = left;
37Added line. right_value = right;
38Added line. }
39Added line.
40Added line. extern "C" unsigned long cpp_diamond_root_size ()
41Added line. {
42Added line. return sizeof (Diamond_Root);
43Added line. }
44Added line.
45Added line. extern "C" unsigned long cpp_diamond_size ()
46Added line. {
47Added line. return sizeof (Diamond);
48Added line. }
49Added line.
50Added line. extern "C" unsigned long cpp_diamond_alignment ()
51Added line. {
52Added line. return alignof (Diamond);
53Added line. }
54Added line.
55Added line. extern "C" unsigned long cpp_diamond_left_offset ()
56Added line. {
57Added line. Diamond object (0, 0, 0, 0);
58Added line. return reinterpret_cast<char *> (&object.left_value)
59Added line. - reinterpret_cast<char *> (&object);
60Added line. }
61Added line.
62Added line. extern "C" unsigned long cpp_diamond_right_offset ()
63Added line. {
64Added line. Diamond object (0, 0, 0, 0);
65Added line. return reinterpret_cast<char *> (&object.right_value)
66Added line. - reinterpret_cast<char *> (&object);
67Added line. }
68Added line.
69Added line. extern "C" unsigned long cpp_diamond_value_offset ()
70Added line. {
71Added line. Diamond object (0, 0, 0, 0);
72Added line. return reinterpret_cast<char *> (&object.diamond_value)
73Added line. - reinterpret_cast<char *> (&object);
74Added line. }
75Added line.
76Added line. extern "C" unsigned long cpp_diamond_root_offset ()
77Added line. {
78Added line. Diamond object (0, 0, 0, 0);
79Added line. return reinterpret_cast<char *> (&object.root_value)
80Added line. - reinterpret_cast<char *> (&object);
81Added line. }
82Added line.
83Added line. extern "C" Diamond *cpp_diamond_create
84Added line. (int root, int left, int right, int diamond_arg)
85Added line. {
86Added line. return new Diamond (root, left, right, diamond_arg);
87Added line. }
88Added line.
89Added line. extern "C" void cpp_diamond_delete (Diamond *object)
90Added line. {
91Added line. delete object;
92Added line. }
93Added line.
94Added line. extern "C" int cpp_diamond_root_value (const Diamond *object)
95Added line. {
96Added line. return object->root_value;
97Added line. }
98Added line.
99Added line. extern "C" int cpp_diamond_left_value (const Diamond *object)
100Added line. {
101Added line. return object->left_value;
102Added line. }
103Added line.
104Added line. extern "C" int cpp_diamond_right_value (const Diamond *object)
105Added line. {
106Added line. return object->right_value;
107Added line. }
108Added line.
109Added line. extern "C" int cpp_diamond_value (const Diamond *object)
110Added line. {
111Added line. return object->diamond_value;
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.

+328 −2 2 files

Download the patch

gcc/c-family/c-ada-spec.cc +214−2modified
Unified diff for gcc/c-family/c-ada-spec.cc: original line, patched line, change, source
@@ -2924 +2924 @@is_ada_record_field (tree field)
29242924 return DECL_NAME (field) || !is_tagged_type (TREE_TYPE (field));
29252925}
29262926
2927Added line. /* Return true if NODE needs a distinct storage type when used as a base
2928Added line. subobject. A non-polymorphic class with virtual bases contains those bases
2929Added line. as a complete object, but excludes them from its as-base size. */
2930Added line.
2931Added line. static bool
2932Added line. needs_virtual_as_base_type (tree node)
2933Added line. {
2934Added line. if (!cpp_check || !RECORD_OR_UNION_TYPE_P (node) || is_tagged_type (node)
2935Added line. || !cpp_check (node, HAS_VIRTUAL_BASE))
2936Added line. return false;
2937Added line.
2938Added line. const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2939Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (node);
2940Added line. return data_size > 0 && object_size > data_size;
2941Added line. }
2942Added line.
2943Added line. /* Return true if FIELD stores only the as-base portion of a virtual-base
2944Added line. class. The complete Ada type has a larger Object_Size and cannot be used
2945Added line. for this component. */
2946Added line.
2947Added line. static bool
2948Added line. is_shortened_virtual_base_field (tree field)
2949Added line. {
2950Added line. if (TREE_CODE (field) != FIELD_DECL || DECL_NAME (field)
2951Added line. || !needs_virtual_as_base_type (TREE_TYPE (field))
2952Added line. || !DECL_SIZE (field) || !tree_fits_uhwi_p (DECL_SIZE (field)))
2953Added line. return false;
2954Added line.
2955Added line. const HOST_WIDE_INT object_size = int_size_in_bytes (TREE_TYPE (field));
2956Added line. return object_size > 0
2957Added line. && tree_to_uhwi (DECL_SIZE (field))
2958Added line. < (unsigned HOST_WIDE_INT) object_size * BITS_PER_UNIT;
2959Added line. }
2960Added line.
2961Added line. /* Dump FIELD's named type, selecting the shortened storage type when FIELD
2962Added line. is a base subobject that excludes its type's virtual bases. */
2963Added line.
2964Added line. static void
2965Added line. dump_ada_record_field_type (pretty_printer *pp, tree field)
2966Added line. {
2967Added line. dump_ada_decl_name (pp, TYPE_NAME (TREE_TYPE (field)), false);
2968Added line. if (is_shortened_virtual_base_field (field))
2969Added line. pp_string (pp, "_As_Base");
2970Added line. }
2971Added line.
2972Added line. /* Return true if FIELD lies wholly inside NODE's C++ as-base size. */
2973Added line.
2974Added line. static bool
2975Added line. field_fits_as_base (tree node, tree field)
2976Added line. {
2977Added line. const int data_size = cpp_check (node, GET_AS_BASE_SIZE);
2978Added line. if (data_size < 0 || !is_ada_record_field (node, field)
2979Added line. || !DECL_FIELD_OFFSET (field) || !DECL_FIELD_BIT_OFFSET (field)
2980Added line. || !DECL_SIZE (field)
2981Added line. || !tree_fits_uhwi_p (DECL_FIELD_OFFSET (field))
2982Added line. || !tree_fits_uhwi_p (DECL_FIELD_BIT_OFFSET (field))
2983Added line. || !tree_fits_uhwi_p (DECL_SIZE (field)))
2984Added line. return false;
2985Added line.
2986Added line. const unsigned HOST_WIDE_INT bitpos
2987Added line. = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
2988Added line. + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
2989Added line. const unsigned HOST_WIDE_INT size = tree_to_uhwi (DECL_SIZE (field));
2990Added line. return size > 0 && bitpos + size
2991Added line. <= (unsigned HOST_WIDE_INT) data_size * BITS_PER_UNIT;
2992Added line. }
2993Added line.
2994Added line. /* Return the strongest Ada alignment that divides NODE's as-base size. The
2995Added line. complete C++ alignment can exceed that storage size because reusable tail
2996Added line. padding is not part of a base subobject. */
2997Added line.
2998Added line. static unsigned int
2999Added line. virtual_as_base_alignment (tree node, unsigned int data_size)
3000Added line. {
3001Added line. unsigned int alignment = TYPE_ALIGN (node) / BITS_PER_UNIT;
3002Added line. while (alignment > 1 && data_size % alignment != 0)
3003Added line. alignment >>= 1;
3004Added line. return alignment;
3005Added line. }
3006Added line.
3007Added line. /* Dump a storage-only Ada type for NODE's C++ as-base representation. This
3008Added line. keeps each direct field addressable while excluding shared virtual bases. */
3009Added line.
3010Added line. static void
3011Added line. dump_ada_virtual_as_base_type (pretty_printer *pp, tree node, tree type,
3012Added line. int spc)
3013Added line. {
3014Added line. const unsigned int data_size = cpp_check (node, GET_AS_BASE_SIZE);
3015Added line. int field_num = 0;
3016Added line.
3017Added line. pp_newline (pp);
3018Added line. newline_and_indent (pp, spc);
3019Added line. pp_string (pp, "type ");
3020Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
3021Added line. pp_string (pp, "_As_Base is record");
3022Added line. pp_newline (pp);
3023Added line.
3024Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3025Added line. if (field_fits_as_base (node, field))
3026Added line. {
3027Added line. if (!DECL_NAME (field))
3028Added line. {
3029Added line. if (is_tagged_type (TREE_TYPE (field)))
3030Added line. continue;
3031Added line.
3032Added line. INDENT (spc + INDENT_INCR);
3033Added line. if (field_num == 0)
3034Added line. pp_string (pp, "parent : ");
3035Added line. else
3036Added line. {
3037Added line. char buf[32];
3038Added line. sprintf (buf, "field_%d : ", field_num + 1);
3039Added line. pp_string (pp, buf);
3040Added line. }
3041Added line. dump_ada_record_field_type (pp, field);
3042Added line. pp_semicolon (pp);
3043Added line. pp_newline (pp);
3044Added line. field_num++;
3045Added line. }
3046Added line. else
3047Added line. {
3048Added line. INDENT (spc + INDENT_INCR);
3049Added line. dump_ada_node (pp, field, type, spc, false, true);
3050Added line. pp_string (pp, " : ");
3051Added line. dump_ada_record_field_type (pp, field);
3052Added line. pp_string (pp, "; -- ");
3053Added line. dump_sloc (pp, field);
3054Added line. pp_newline (pp);
3055Added line. field_num++;
3056Added line. }
3057Added line. }
3058Added line.
3059Added line. if (field_num == 0)
3060Added line. {
3061Added line. INDENT (spc + INDENT_INCR);
3062Added line. pp_string (pp, "null;");
3063Added line. pp_newline (pp);
3064Added line. }
3065Added line.
3066Added line. INDENT (spc);
3067Added line. pp_string (pp, "end record");
3068Added line. newline_and_indent (pp, spc);
3069Added line. pp_string (pp, "with Convention => C_Pass_By_Copy;");
3070Added line.
3071Added line. newline_and_indent (pp, spc);
3072Added line. pp_string (pp, "for ");
3073Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
3074Added line. pp_string (pp, "_As_Base'Size use ");
3075Added line. pp_wide_integer (pp, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
3076Added line. pp_semicolon (pp);
3077Added line.
3078Added line. newline_and_indent (pp, spc);
3079Added line. pp_string (pp, "for ");
3080Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
3081Added line. pp_string (pp, "_As_Base'Object_Size use ");
3082Added line. pp_wide_integer (pp, (HOST_WIDE_INT) data_size * BITS_PER_UNIT);
3083Added line. pp_semicolon (pp);
3084Added line.
3085Added line. newline_and_indent (pp, spc);
3086Added line. pp_string (pp, "for ");
3087Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
3088Added line. pp_string (pp, "_As_Base'Alignment use ");
3089Added line. pp_decimal_int (pp, virtual_as_base_alignment (node, data_size));
3090Added line. pp_semicolon (pp);
3091Added line.
3092Added line. newline_and_indent (pp, spc);
3093Added line. pp_string (pp, "for ");
3094Added line. dump_ada_node (pp, TYPE_NAME (node), type, spc, false, true);
3095Added line. pp_string (pp, "_As_Base use record");
3096Added line.
3097Added line. int repr_field_num = 0;
3098Added line. for (tree field = TYPE_FIELDS (node); field; field = TREE_CHAIN (field))
3099Added line. if (field_fits_as_base (node, field))
3100Added line. {
3101Added line. if (!DECL_NAME (field) && is_tagged_type (TREE_TYPE (field)))
3102Added line. continue;
3103Added line.
3104Added line. repr_field_num++;
3105Added line. const unsigned HOST_WIDE_INT bitpos
3106Added line. = tree_to_uhwi (DECL_FIELD_OFFSET (field)) * BITS_PER_UNIT
3107Added line. + tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
3108Added line. const unsigned HOST_WIDE_INT position = bitpos / BITS_PER_UNIT;
3109Added line. const unsigned HOST_WIDE_INT first_bit = bitpos % BITS_PER_UNIT;
3110Added line. const unsigned HOST_WIDE_INT last_bit
3111Added line. = first_bit + tree_to_uhwi (DECL_SIZE (field)) - 1;
3112Added line.
3113Added line. newline_and_indent (pp, spc + INDENT_INCR);
3114Added line. if (DECL_NAME (field))
3115Added line. dump_ada_decl_name (pp, field, false);
3116Added line. else if (repr_field_num == 1)
3117Added line. pp_string (pp, "parent");
3118Added line. else
3119Added line. {
3120Added line. char buf[32];
3121Added line. sprintf (buf, "field_%d", repr_field_num);
3122Added line. pp_string (pp, buf);
3123Added line. }
3124Added line. pp_string (pp, " at ");
3125Added line. pp_unsigned_wide_integer (pp, position);
3126Added line. pp_string (pp, " range ");
3127Added line. pp_unsigned_wide_integer (pp, first_bit);
3128Added line. pp_string (pp, " .. ");
3129Added line. pp_unsigned_wide_integer (pp, last_bit);
3130Added line. pp_semicolon (pp);
3131Added line. }
3132Added line.
3133Added line. newline_and_indent (pp, spc);
3134Added line. pp_string (pp, "end record;");
3135Added line. }
3136Added line.
29273137/* Dump the value and object sizes of an empty C++ class. Ada's value size is
29283138 zero so an empty base consumes no storage, while Object_Size preserves the
29293139 byte occupied by a complete C++ object. */
@@ -3992 +4196 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
39924196 sprintf (buf, "field_%d : aliased ", field_num + 1);
39934197 pp_string (pp, buf);
39944198 }
3995Removed line. dump_ada_decl_name (pp, TYPE_NAME (TREE_TYPE (tmp)),
3996Removed line. false);
4199Added line. dump_ada_record_field_type (pp, tmp);
39974200 pp_semicolon (pp);
39984201 }
39994202
@@ -4119 +4322 @@dump_ada_structure (pretty_printer *pp, tree node, tree type, bool nested,
41194322 pp_newline (pp);
41204323 dump_ada_cpp_layout (pp, node, type, spc,
41214324 cpp_check (node, HAS_VIRTUAL_BASE));
4325Added line.
4326Added line. if (needs_virtual_as_base_type (node))
4327Added line. dump_ada_virtual_as_base_type (pp, node, type, spc);
41224328 }
41234329
41244330 /* Print the static fields of the structure, if any. */
gcc/testsuite/g++.dg/ada-spec/virtual-diamond-layout.C +114−0new file
Unified diff for gcc/testsuite/g++.dg/ada-spec/virtual-diamond-layout.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 virtual_diamond_layout_c.ads "type Diamond_Left_As_Base" } } */
5Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "for Diamond_Left_As_Base'Object_Size use 96;" } } */
6Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "type Diamond_Right_As_Base" } } */
7Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "for Diamond_Right_As_Base'Object_Size use 96;" } } */
8Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "parent : aliased Diamond_Left_As_Base;" } } */
9Added line. /* { dg-final { scan-file virtual_diamond_layout_c.ads "field_2 : aliased Diamond_Right_As_Base;" } } */
10Added line.
11Added line. struct Diamond_Root
12Added line. {
13Added line. int root_value;
14Added line. };
15Added line.
16Added line. struct Diamond_Left : virtual Diamond_Root
17Added line. {
18Added line. int left_value;
19Added line. };
20Added line.
21Added line. struct Diamond_Right : virtual Diamond_Root
22Added line. {
23Added line. int right_value;
24Added line. };
25Added line.
26Added line. struct Diamond : Diamond_Left, Diamond_Right
27Added line. {
28Added line. int diamond_value;
29Added line. Diamond (int root, int left, int right, int diamond_arg);
30Added line. };
31Added line.
32Added line. Diamond::Diamond (int root, int left, int right, int diamond_arg)
33Added line. : Diamond_Root {root}, Diamond_Left {}, Diamond_Right {},
34Added line. diamond_value {diamond_arg}
35Added line. {
36Added line. left_value = left;
37Added line. right_value = right;
38Added line. }
39Added line.
40Added line. extern "C" unsigned long cpp_diamond_root_size ()
41Added line. {
42Added line. return sizeof (Diamond_Root);
43Added line. }
44Added line.
45Added line. extern "C" unsigned long cpp_diamond_size ()
46Added line. {
47Added line. return sizeof (Diamond);
48Added line. }
49Added line.
50Added line. extern "C" unsigned long cpp_diamond_alignment ()
51Added line. {
52Added line. return alignof (Diamond);
53Added line. }
54Added line.
55Added line. extern "C" unsigned long cpp_diamond_left_offset ()
56Added line. {
57Added line. Diamond object (0, 0, 0, 0);
58Added line. return reinterpret_cast<char *> (&object.left_value)
59Added line. - reinterpret_cast<char *> (&object);
60Added line. }
61Added line.
62Added line. extern "C" unsigned long cpp_diamond_right_offset ()
63Added line. {
64Added line. Diamond object (0, 0, 0, 0);
65Added line. return reinterpret_cast<char *> (&object.right_value)
66Added line. - reinterpret_cast<char *> (&object);
67Added line. }
68Added line.
69Added line. extern "C" unsigned long cpp_diamond_value_offset ()
70Added line. {
71Added line. Diamond object (0, 0, 0, 0);
72Added line. return reinterpret_cast<char *> (&object.diamond_value)
73Added line. - reinterpret_cast<char *> (&object);
74Added line. }
75Added line.
76Added line. extern "C" unsigned long cpp_diamond_root_offset ()
77Added line. {
78Added line. Diamond object (0, 0, 0, 0);
79Added line. return reinterpret_cast<char *> (&object.root_value)
80Added line. - reinterpret_cast<char *> (&object);
81Added line. }
82Added line.
83Added line. extern "C" Diamond *cpp_diamond_create
84Added line. (int root, int left, int right, int diamond_arg)
85Added line. {
86Added line. return new Diamond (root, left, right, diamond_arg);
87Added line. }
88Added line.
89Added line. extern "C" void cpp_diamond_delete (Diamond *object)
90Added line. {
91Added line. delete object;
92Added line. }
93Added line.
94Added line. extern "C" int cpp_diamond_root_value (const Diamond *object)
95Added line. {
96Added line. return object->root_value;
97Added line. }
98Added line.
99Added line. extern "C" int cpp_diamond_left_value (const Diamond *object)
100Added line. {
101Added line. return object->left_value;
102Added line. }
103Added line.
104Added line. extern "C" int cpp_diamond_right_value (const Diamond *object)
105Added line. {
106Added line. return object->right_value;
107Added line. }
108Added line.
109Added line. extern "C" int cpp_diamond_value (const Diamond *object)
110Added line. {
111Added line. return object->diamond_value;
112Added line. }
113Added line.
114Added line. /* { dg-final { cleanup-ada-spec } } */
115

Tests.

virtual-diamond-layout.C C++ · 114 lines
/* { dg-do compile } */
/* { dg-require-effective-target lp64 } */
/* { dg-options "-fdump-ada-spec-slim" } */
/* { dg-final { scan-file virtual_diamond_layout_c.ads "type Diamond_Left_As_Base" } } */
/* { dg-final { scan-file virtual_diamond_layout_c.ads "for Diamond_Left_As_Base'Object_Size use 96;" } } */
/* { dg-final { scan-file virtual_diamond_layout_c.ads "type Diamond_Right_As_Base" } } */
/* { dg-final { scan-file virtual_diamond_layout_c.ads "for Diamond_Right_As_Base'Object_Size use 96;" } } */
/* { dg-final { scan-file virtual_diamond_layout_c.ads "parent : aliased Diamond_Left_As_Base;" } } */
/* { dg-final { scan-file virtual_diamond_layout_c.ads "field_2 : aliased Diamond_Right_As_Base;" } } */

struct Diamond_Root
{
  int root_value;
};

struct Diamond_Left : virtual Diamond_Root
{
  int left_value;
};

struct Diamond_Right : virtual Diamond_Root
{
  int right_value;
};

struct Diamond : Diamond_Left, Diamond_Right
{
  int diamond_value;
  Diamond (int root, int left, int right, int diamond_arg);
};

Diamond::Diamond (int root, int left, int right, int diamond_arg)
  : Diamond_Root {root}, Diamond_Left {}, Diamond_Right {},
    diamond_value {diamond_arg}
{
  left_value = left;
  right_value = right;
}

extern "C" unsigned long cpp_diamond_root_size ()
{
  return sizeof (Diamond_Root);
}

extern "C" unsigned long cpp_diamond_size ()
{
  return sizeof (Diamond);
}

extern "C" unsigned long cpp_diamond_alignment ()
{
  return alignof (Diamond);
}

extern "C" unsigned long cpp_diamond_left_offset ()
{
  Diamond object (0, 0, 0, 0);
  return reinterpret_cast<char *> (&object.left_value)
    - reinterpret_cast<char *> (&object);
}

extern "C" unsigned long cpp_diamond_right_offset ()
{
  Diamond object (0, 0, 0, 0);
  return reinterpret_cast<char *> (&object.right_value)
    - reinterpret_cast<char *> (&object);
}

extern "C" unsigned long cpp_diamond_value_offset ()
{
  Diamond object (0, 0, 0, 0);
  return reinterpret_cast<char *> (&object.diamond_value)
    - reinterpret_cast<char *> (&object);
}

extern "C" unsigned long cpp_diamond_root_offset ()
{
  Diamond object (0, 0, 0, 0);
  return reinterpret_cast<char *> (&object.root_value)
    - reinterpret_cast<char *> (&object);
}

extern "C" Diamond *cpp_diamond_create
  (int root, int left, int right, int diamond_arg)
{
  return new Diamond (root, left, right, diamond_arg);
}

extern "C" void cpp_diamond_delete (Diamond *object)
{
  delete object;
}

extern "C" int cpp_diamond_root_value (const Diamond *object)
{
  return object->root_value;
}

extern "C" int cpp_diamond_left_value (const Diamond *object)
{
  return object->left_value;
}

extern "C" int cpp_diamond_right_value (const Diamond *object)
{
  return object->right_value;
}

extern "C" int cpp_diamond_value (const Diamond *object)
{
  return object->diamond_value;
}

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

Download · View in repository

virtual_diamond_layout_consumer.adb Ada · 58 lines
with Ada.Text_IO;
with Interfaces.C; use Interfaces.C;
with System;
with Virtual_Diamond_Layout_C;

procedure Virtual_Diamond_Layout_Consumer is
   package Bindings renames Virtual_Diamond_Layout_C;
   subtype Diamond is Bindings.Class_Diamond.Diamond;
   Object : access Diamond := Bindings.cpp_diamond_create (11, 22, 33, 44);

   function Bytes (Bits : Natural) return unsigned_long is
     (unsigned_long (Bits / System.Storage_Unit));

   Layout_Matches : constant Boolean :=
     Bytes (Diamond'Object_Size) = Bindings.cpp_diamond_size
     and then unsigned_long (Diamond'Alignment) =
       Bindings.cpp_diamond_alignment
     and then unsigned_long
       (Object.parent'Position + Object.parent.left_value'Position) =
       Bindings.cpp_diamond_left_offset
     and then unsigned_long
       (Object.field_2'Position + Object.field_2.right_value'Position) =
       Bindings.cpp_diamond_right_offset
     and then unsigned_long (Object.diamond_value'Position) =
       Bindings.cpp_diamond_value_offset
     and then unsigned_long (Object.field_4'Position) =
       Bindings.cpp_diamond_root_offset;
begin
   if Object = null
     or else Bytes (Bindings.Diamond_Root'Object_Size) /=
       Bindings.cpp_diamond_root_size
     or else Bindings.cpp_diamond_root_value (Object) /= 11
     or else Bindings.cpp_diamond_left_value (Object) /= 22
     or else Bindings.cpp_diamond_right_value (Object) /= 33
     or else Bindings.cpp_diamond_value (Object) /= 44
   then
      raise Program_Error with "C++ virtual diamond setup differs";
   end if;

   if not Layout_Matches then
      raise Program_Error with "C++ and Ada virtual diamond layouts differ";
   end if;

   Object.parent.left_value := 55;
   Object.field_2.right_value := 66;
   Object.diamond_value := 77;
   Object.field_4.root_value := 88;
   if Bindings.cpp_diamond_left_value (Object) /= 55
     or else Bindings.cpp_diamond_right_value (Object) /= 66
     or else Bindings.cpp_diamond_value (Object) /= 77
     or else Bindings.cpp_diamond_root_value (Object) /= 88
   then
      raise Program_Error with "Ada writes use the wrong diamond layout";
   end if;

   Bindings.cpp_diamond_delete (Object);
   Ada.Text_IO.Put_Line ("MATCH C++ Ada virtual diamond layout");
end Virtual_Diamond_Layout_Consumer;

Download · View in repository

run-test.sh shell · 83 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++"
cxx="$root/bundles/cxx-ada-virtual-diamond-layout/tests/virtual-diamond-layout.C"
ada="$root/bundles/cxx-ada-virtual-diamond-layout/tests/virtual_diamond_layout_consumer.adb"
work=$(mktemp -d "${TMPDIR:-/tmp}/gnat-cxx-ada-virtual-diamond.XXXXXX")
trap 'rm -rf "$work"' EXIT

for optimization in 0 2; do
  case_dir="$work/O$optimization"
  mkdir -p "$case_dir"
  cp "$cxx" "$ada" "$case_dir/"
  (
    cd "$case_dir"
    "${REGRESSION_ENV[@]}" "$gxx" -c "-O$optimization" \
      -fdump-ada-spec-slim virtual-diamond-layout.C
  )

  spec="$case_dir/virtual_diamond_layout_c.ads"
  if [[ "$state" == unpatched ]]; then
    (
      cd "$case_dir"
      "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f \
        "-O$optimization" virtual_diamond_layout_consumer.adb \
        -largs virtual-diamond-layout.o -lstdc++
    ) >"$case_dir/build.log" 2>&1
    set +e
    (
      cd "$case_dir"
      "${REGRESSION_ENV[@]}" ./virtual_diamond_layout_consumer
    ) >"$case_dir/output.log" 2>&1
    status=$?
    set -e
    [[ $status -ne 0 ]] || {
      echo "error: unpatched virtual diamond layout matched" >&2
      exit 1
    }
    grep -Fq 'C++ and Ada virtual diamond layouts differ' \
      "$case_dir/output.log"
    if grep -Fq "type Diamond_Left_As_Base" "$spec"; then
      echo "error: unpatched mapper unexpectedly emitted as-base storage" >&2
      exit 1
    fi
    echo "cxx-ada-virtual-diamond-layout -O$optimization: expected mismatch (GCC $version)"
    continue
  fi

  (
    cd "$case_dir"
    "${REGRESSION_ENV[@]}" "$REGRESSION_GNATMAKE" -q -f \
      "-O$optimization" virtual_diamond_layout_consumer.adb \
      -largs virtual-diamond-layout.o -lstdc++
    "${REGRESSION_ENV[@]}" ./virtual_diamond_layout_consumer
  ) >"$case_dir/output.log" 2>&1 || {
    cat "$case_dir/output.log"
    exit 1
  }

  grep -F "type Diamond_Left_As_Base" "$spec"
  grep -F "for Diamond_Left_As_Base'Object_Size use" "$spec"
  grep -F "type Diamond_Right_As_Base" "$spec"
  grep -F "for Diamond_Right_As_Base'Object_Size use" "$spec"
  grep -F "parent : aliased Diamond_Left_As_Base" "$spec"
  grep -F "field_2 : aliased Diamond_Right_As_Base" "$spec"
  grep -Fx "MATCH C++ Ada virtual diamond layout" "$case_dir/output.log"
  echo "cxx-ada-virtual-diamond-layout -O$optimization: patched (GCC $version)"
done

Download · View in repository

Commands.

Apply the patch
patch --fuzz=0 -p1 -i bundles/cxx-ada-virtual-diamond-layout/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.