On Tue, Oct 15, 2024 at 7:59 PM Jakub Jelinek <ja...@redhat.com> wrote: > > Hi! > > Here is an updated version of the patch. > > My reading of C23 is that if some aggregate field is initialized with > {} (which is supposed to newly clear padding bits) and then its > subobjects are overridden with designated initializers, then the > padding bits are cleared property should be kept unless one overwrites > that field completely with a different initializer, which was something > the previous patch didn't implement, all it did was set > CONSTRUCTOR_ZERO_PADDING_BITS flag on {} CONSTRUCTORs for flag_isoc23. > > This adjusted patch propagates it through the initializer handling > and adds testcase coverage with my comments on what I think is well > defined and what isn't (please correct me where I'm wrong). > > Haven't touched the C++ FE, I'm feeling lost there where zero initialization > happens and where other forms of initialization happen, and where it should > be propagated from one CONSTRUCTOR to another and where it shouldn't. > > Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
The middle-end changes are OK. Richard. > 2024-10-15 Jakub Jelinek <ja...@redhat.com> > > PR c++/116416 > gcc/ > * flag-types.h (enum zero_init_padding_bits_kind): New type. > * tree.h (CONSTRUCTOR_ZERO_PADDING_BITS): Define. > * common.opt (fzero-init-padding-bits=): New option. > * expr.cc (categorize_ctor_elements_1): Handle > CONSTRUCTOR_ZERO_PADDING_BITS or > flag_zero_init_padding_bits == ZERO_INIT_PADDING_BITS_ALL. Fix up > *p_complete = -1; setting for unions. > (complete_ctor_at_level_p): Handle unions differently for > flag_zero_init_padding_bits == ZERO_INIT_PADDING_BITS_STANDARD. > * gimple-fold.cc (type_has_padding_at_level_p): Fix up UNION_TYPE > handling, return also true for UNION_TYPE with no FIELD_DECLs > and non-zero size, handle QUAL_UNION_TYPE like UNION_TYPE. > * doc/invoke.texi (-fzero-init-padding-bits=@var{value}): Document. > gcc/c/ > * c-parser.cc (c_parser_braced_init): Set > CONSTRUCTOR_ZERO_PADDING_BITS > for flag_isoc23 empty initializers. > * c-typeck.cc (constructor_zero_padding_bits): New variable. > (struct constructor_stack): Add zero_padding_bits member. > (really_start_incremental_init): Save and clear > constructor_zero_padding_bits. > (push_init_level): Save constructor_zero_padding_bits. Or into it > CONSTRUCTOR_ZERO_PADDING_BITS from previous value if implicit. > (pop_init_level): Set CONSTRUCTOR_ZERO_PADDING_BITS if > constructor_zero_padding_bits and restore > constructor_zero_padding_bits. > gcc/testsuite/ > * gcc.dg/plugin/infoleak-1.c (test_union_2b, test_union_4b): Expect > diagnostics. > * gcc.dg/c23-empty-init-4.c: New test. > * gcc.dg/gnu11-empty-init-1.c: New test. > * gcc.dg/gnu11-empty-init-2.c: New test. > * gcc.dg/gnu11-empty-init-3.c: New test. > * gcc.dg/gnu11-empty-init-4.c: New test. > > --- gcc/flag-types.h.jj 2024-10-07 11:40:04.518038504 +0200 > +++ gcc/flag-types.h 2024-10-15 13:50:34.800660119 +0200 > @@ -291,6 +291,13 @@ enum auto_init_type { > AUTO_INIT_ZERO = 2 > }; > > +/* Initialization of padding bits with zeros. */ > +enum zero_init_padding_bits_kind { > + ZERO_INIT_PADDING_BITS_STANDARD = 0, > + ZERO_INIT_PADDING_BITS_UNIONS = 1, > + ZERO_INIT_PADDING_BITS_ALL = 2 > +}; > + > /* Different instrumentation modes. */ > enum sanitize_code { > /* AddressSanitizer. */ > --- gcc/tree.h.jj 2024-10-07 11:40:04.521038462 +0200 > +++ gcc/tree.h 2024-10-15 13:50:34.801660105 +0200 > @@ -1225,6 +1225,9 @@ extern void omp_clause_range_check_faile > (vec_safe_length (CONSTRUCTOR_ELTS (NODE))) > #define CONSTRUCTOR_NO_CLEARING(NODE) \ > (CONSTRUCTOR_CHECK (NODE)->base.public_flag) > +/* True if even padding bits should be zeroed during initialization. */ > +#define CONSTRUCTOR_ZERO_PADDING_BITS(NODE) \ > + (CONSTRUCTOR_CHECK (NODE)->base.default_def_flag) > > /* Iterate through the vector V of CONSTRUCTOR_ELT elements, yielding the > value of each element (stored within VAL). IX must be a scratch variable > --- gcc/common.opt.jj 2024-10-07 11:40:04.510038616 +0200 > +++ gcc/common.opt 2024-10-15 13:50:35.227654223 +0200 > @@ -3505,6 +3505,22 @@ fzero-call-used-regs= > Common RejectNegative Joined > Clear call-used registers upon function return. > > +fzero-init-padding-bits= > +Common Joined RejectNegative Enum(zero_init_padding_bits_kind) > Var(flag_zero_init_padding_bits) Init(ZERO_INIT_PADDING_BITS_STANDARD) > +-fzero-init-padding-bits=[standard|unions|all] Zero padding bits in > initializers. > + > +Enum > +Name(zero_init_padding_bits_kind) Type(enum zero_init_padding_bits_kind) > UnknownError(unrecognized zero init padding bits kind %qs) > + > +EnumValue > +Enum(zero_init_padding_bits_kind) String(standard) > Value(ZERO_INIT_PADDING_BITS_STANDARD) > + > +EnumValue > +Enum(zero_init_padding_bits_kind) String(unions) > Value(ZERO_INIT_PADDING_BITS_UNIONS) > + > +EnumValue > +Enum(zero_init_padding_bits_kind) String(all) > Value(ZERO_INIT_PADDING_BITS_ALL) > + > g > Common Driver RejectNegative JoinedOrMissing > Generate debug information in default format. > --- gcc/expr.cc.jj 2024-10-15 13:49:05.530892759 +0200 > +++ gcc/expr.cc 2024-10-15 13:50:35.230654182 +0200 > @@ -7219,6 +7219,28 @@ categorize_ctor_elements_1 (const_tree c > if (*p_complete && !complete_ctor_at_level_p (TREE_TYPE (ctor), > num_fields, elt_type)) > *p_complete = 0; > + else if (TREE_CODE (TREE_TYPE (ctor)) == UNION_TYPE > + || TREE_CODE (TREE_TYPE (ctor)) == QUAL_UNION_TYPE) > + { > + if (*p_complete > + && CONSTRUCTOR_ZERO_PADDING_BITS (ctor) > + && (num_fields > + ? simple_cst_equal (TYPE_SIZE (TREE_TYPE (ctor)), > + TYPE_SIZE (elt_type)) != 1 > + : type_has_padding_at_level_p (TREE_TYPE (ctor)))) > + *p_complete = 0; > + else if (*p_complete > 0 > + && (num_fields > + ? simple_cst_equal (TYPE_SIZE (TREE_TYPE (ctor)), > + TYPE_SIZE (elt_type)) != 1 > + : type_has_padding_at_level_p (TREE_TYPE (ctor)))) > + *p_complete = -1; > + } > + else if (*p_complete > + && (CONSTRUCTOR_ZERO_PADDING_BITS (ctor) > + || flag_zero_init_padding_bits == ZERO_INIT_PADDING_BITS_ALL) > + && type_has_padding_at_level_p (TREE_TYPE (ctor))) > + *p_complete = 0; > else if (*p_complete > 0 > && type_has_padding_at_level_p (TREE_TYPE (ctor))) > *p_complete = -1; > @@ -7293,19 +7315,32 @@ bool > complete_ctor_at_level_p (const_tree type, HOST_WIDE_INT num_elts, > const_tree last_type) > { > - if (TREE_CODE (type) == UNION_TYPE > - || TREE_CODE (type) == QUAL_UNION_TYPE) > + if (TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == QUAL_UNION_TYPE) > { > if (num_elts == 0) > - return false; > + { > + if (flag_zero_init_padding_bits >= ZERO_INIT_PADDING_BITS_UNIONS) > + return false; > + > + /* If the CONSTRUCTOR doesn't have any elts, it is > + incomplete if the union has at least one field. */ > + for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f)) > + if (TREE_CODE (f) == FIELD_DECL) > + return false; > + > + return true; > + } > > gcc_assert (num_elts == 1 && last_type); > > - /* ??? We could look at each element of the union, and find the > - largest element. Which would avoid comparing the size of the > - initialized element against any tail padding in the union. > - Doesn't seem worth the effort... */ > - return simple_cst_equal (TYPE_SIZE (type), TYPE_SIZE (last_type)) == 1; > + if (flag_zero_init_padding_bits >= ZERO_INIT_PADDING_BITS_UNIONS) > + /* ??? We could look at each element of the union, and find the > + largest element. Which would avoid comparing the size of the > + initialized element against any tail padding in the union. > + Doesn't seem worth the effort... */ > + return simple_cst_equal (TYPE_SIZE (type), TYPE_SIZE (last_type)) == > 1; > + > + return true; > } > > return count_type_elements (type, true) == num_elts; > --- gcc/gimple-fold.cc.jj 2024-10-14 10:01:57.610643803 +0200 > +++ gcc/gimple-fold.cc 2024-10-15 13:50:35.249653919 +0200 > @@ -4814,12 +4814,22 @@ type_has_padding_at_level_p (tree type) > return false; > } > case UNION_TYPE: > + case QUAL_UNION_TYPE: > + bool any_fields; > + any_fields = false; > /* If any of the fields is smaller than the whole, there is padding. > */ > for (tree f = TYPE_FIELDS (type); f; f = DECL_CHAIN (f)) > - if (TREE_CODE (f) == FIELD_DECL) > - if (simple_cst_equal (TYPE_SIZE (TREE_TYPE (f)), > - TREE_TYPE (type)) != 1) > - return true; > + if (TREE_CODE (f) != FIELD_DECL) > + continue; > + else if (simple_cst_equal (TYPE_SIZE (TREE_TYPE (f)), > + TYPE_SIZE (type)) != 1) > + return true; > + else > + any_fields = true; > + /* If the union doesn't have any fields and still has non-zero size, > + all of it is padding. */ > + if (!any_fields && !integer_zerop (TYPE_SIZE (type))) > + return true; > return false; > case ARRAY_TYPE: > case COMPLEX_TYPE: > --- gcc/doc/invoke.texi.jj 2024-10-15 08:01:37.925036285 +0200 > +++ gcc/doc/invoke.texi 2024-10-15 13:50:35.259653781 +0200 > @@ -746,7 +746,8 @@ Objective-C and Objective-C++ Dialects}. > -ftrampolines -ftrampoline-impl=@r{[}stack@r{|}heap@r{]} > -ftrapv -fwrapv > -fvisibility=@r{[}default@r{|}internal@r{|}hidden@r{|}protected@r{]} > --fstrict-volatile-bitfields -fsync-libcalls} > +-fstrict-volatile-bitfields -fsync-libcalls > +-fzero-init-padding-bits=@var{value}} > > @item Developer Options > @xref{Developer Options,,GCC Developer Options}. > @@ -19770,6 +19771,43 @@ The default value of this option is enab > of the option is @option{-fno-sync-libcalls}. This option is used in > the implementation of the @file{libatomic} runtime library. > > +@opindex fzero-init-padding-bits=@var{value} > +@item -fzero-init-padding-bits=@var{value} > +Guarantee zero initalization of padding bits in automatic variable > +initializers. > +Certain languages guarantee zero initialization of padding bits in > +certain cases, e.g. C23 when using empty initializers (@code{@{@}}), > +or C++ when using zero-initialization or C guarantees that fields > +not specified in an initializer have their padding bits zero initialized. > +This option allows to change when padding bits in initializers are > +guaranteed to be zero initialized. > +The default is @code{-fzero-init-padding-bits=standard}, which makes > +no further guarantees than the corresponding standard. E.g.@: > + > +@smallexample > + struct A @{ char a; unsigned long long b; char c; @}; > + union B @{ char a; unsigned long long b; @}; > + struct A a = @{@}; // C23 guarantees padding bits are zero. > + struct A b = @{ 1, 2, 3 @}; // No guarantees. > + union B c = @{@}; // C23 guarantees padding bits are zero. > + union B d = @{ 1 @}; // No guarantees. > +@end smallexample > + > +@code{-fzero-init-padding-bits=unions} guarantees zero initialization > +of padding bits in unions on top of what the standards guarantee, > +if the initializer of an union is empty (then all bits of the union > +are zero initialized) or if the initialized member of the union is > +smaller than the size of the union (in that case guarantees padding > +bits outside of the initialized member to be zero initialized). > +This was the GCC behavior before GCC 15 and in the above example guarantees > +zero initialization of last @code{sizeof (unsigned long long) - 1} > +bytes in the union. > + > +@code{-fzero-init-padding-bits=all} guarantees additionally > +zero initialization of padding bits of other aggregates, so > +the padding in between @code{b.a} and @code{b.b} (if any) and > +tail padding in the structure (if any). > + > @end table > > @node Developer Options > --- gcc/c/c-parser.cc.jj 2024-10-12 10:40:16.133531498 +0200 > +++ gcc/c/c-parser.cc 2024-10-15 13:50:35.293653312 +0200 > @@ -6189,6 +6189,7 @@ c_parser_braced_init (c_parser *parser, > gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE)); > bool save_c_omp_array_section_p = c_omp_array_section_p; > c_omp_array_section_p = false; > + bool zero_init_padding_bits = false; > matching_braces braces; > braces.consume_open (parser); > if (nested_p) > @@ -6202,6 +6203,8 @@ c_parser_braced_init (c_parser *parser, > { > pedwarn_c11 (brace_loc, OPT_Wpedantic, > "ISO C forbids empty initializer braces before C23"); > + if (flag_isoc23) > + zero_init_padding_bits = true; > } > else > { > @@ -6242,6 +6245,8 @@ c_parser_braced_init (c_parser *parser, > location_t close_loc = next_tok->location; > c_parser_consume_token (parser); > ret = pop_init_level (brace_loc, 0, &braced_init_obstack, close_loc); > + if (zero_init_padding_bits && TREE_CODE (ret.value) == CONSTRUCTOR) > + CONSTRUCTOR_ZERO_PADDING_BITS (ret.value) = 1; > obstack_free (&braced_init_obstack, NULL); > set_c_expr_source_range (&ret, brace_loc, close_loc); > return ret; > --- gcc/c/c-typeck.cc.jj 2024-10-12 10:40:16.192530678 +0200 > +++ gcc/c/c-typeck.cc 2024-10-15 15:54:37.577783226 +0200 > @@ -9166,6 +9166,9 @@ static int constructor_erroneous; > /* 1 if this constructor is the universal zero initializer { 0 }. */ > static int constructor_zeroinit; > > +/* 1 if this constructor should have padding bits zeroed (C23 {}. */ > +static bool constructor_zero_padding_bits; > + > /* Structure for managing pending initializer elements, organized as an > AVL tree. */ > > @@ -9236,6 +9239,7 @@ struct constructor_stack > char outer; > char incremental; > char designated; > + bool zero_padding_bits; > int designator_depth; > }; > > @@ -9411,6 +9415,7 @@ really_start_incremental_init (tree type > p->outer = 0; > p->incremental = constructor_incremental; > p->designated = constructor_designated; > + p->zero_padding_bits = constructor_zero_padding_bits; > p->designator_depth = designator_depth; > p->next = 0; > constructor_stack = p; > @@ -9424,6 +9429,7 @@ really_start_incremental_init (tree type > constructor_type = type; > constructor_incremental = 1; > constructor_designated = 0; > + constructor_zero_padding_bits = false; > constructor_zeroinit = 1; > designator_depth = 0; > designator_erroneous = 0; > @@ -9559,6 +9565,7 @@ push_init_level (location_t loc, int imp > p->outer = 0; > p->incremental = constructor_incremental; > p->designated = constructor_designated; > + p->zero_padding_bits = constructor_zero_padding_bits; > p->designator_depth = designator_depth; > p->next = constructor_stack; > p->range_stack = 0; > @@ -9573,6 +9580,9 @@ push_init_level (location_t loc, int imp > /* If the upper initializer is designated, then mark this as > designated too to prevent bogus warnings. */ > constructor_designated = p->designated; > + /* If the upper initializer has padding bits zeroed, that includes > + all nested initializers as well. */ > + constructor_zero_padding_bits = p->zero_padding_bits; > constructor_pending_elts = 0; > if (!implicit) > { > @@ -9619,6 +9629,7 @@ push_init_level (location_t loc, int imp > constructor_simple = TREE_STATIC (value); > constructor_nonconst = CONSTRUCTOR_NON_CONST (value); > constructor_elements = CONSTRUCTOR_ELTS (value); > + constructor_zero_padding_bits |= CONSTRUCTOR_ZERO_PADDING_BITS (value); > if (!vec_safe_is_empty (constructor_elements) > && (TREE_CODE (constructor_type) == RECORD_TYPE > || TREE_CODE (constructor_type) == ARRAY_TYPE)) > @@ -9877,6 +9888,8 @@ pop_init_level (location_t loc, int impl > TREE_STATIC (ret.value) = 1; > if (constructor_nonconst) > CONSTRUCTOR_NON_CONST (ret.value) = 1; > + if (constructor_zero_padding_bits) > + CONSTRUCTOR_ZERO_PADDING_BITS (ret.value) = 1; > } > } > > @@ -9902,6 +9915,7 @@ pop_init_level (location_t loc, int impl > constructor_erroneous = p->erroneous; > constructor_incremental = p->incremental; > constructor_designated = p->designated; > + constructor_zero_padding_bits = p->zero_padding_bits; > designator_depth = p->designator_depth; > constructor_pending_elts = p->pending_elts; > constructor_depth = p->depth; > --- gcc/testsuite/gcc.dg/plugin/infoleak-1.c.jj 2024-10-07 11:40:04.520038476 > +0200 > +++ gcc/testsuite/gcc.dg/plugin/infoleak-1.c 2024-10-15 13:50:35.314653022 > +0200 > @@ -123,9 +123,12 @@ void test_union_2a (void __user *dst, u8 > > void test_union_2b (void __user *dst, u8 v) > { > - union un_b u = {0}; > + union un_b u = {0}; /* { dg-message "region created on stack here" "where" > } */ > + /* { dg-message "capacity: 4 bytes" "capacity" { target *-*-* } .-1 } */ > u.j = v; > - copy_to_user(dst, &u, sizeof (union un_b)); > + copy_to_user(dst, &u, sizeof (union un_b)); /* { dg-warning "potential > exposure of sensitive information by copying uninitialized data from stack" > "warning" } */ > + /* { dg-message "3 bytes are uninitialized" "note how much" { target *-*-* > } .-1 } */ > + /* { dg-message "bytes 1 - 3 are uninitialized" "note how much" { target > *-*-* } .-2 } */ > } > > void test_union_3a (void __user *dst, u32 v) > @@ -150,8 +153,11 @@ void test_union_4a (void __user *dst, u8 > > void test_union_4b (void __user *dst, u8 v) > { > - union un_b u = {0}; > - copy_to_user(dst, &u, sizeof (union un_b)); /* { dg-bogus "" } */ > + union un_b u = {0}; /* { dg-message "region created on stack here" "where" > } */ > + /* { dg-message "capacity: 4 bytes" "capacity" { target *-*-* } .-1 } */ > + copy_to_user(dst, &u, sizeof (union un_b)); /* { dg-warning "potential > exposure of sensitive information by copying uninitialized data from stack" > "warning" } */ > + /* { dg-message "3 bytes are uninitialized" "note how much" { target *-*-* > } .-1 } */ > + /* { dg-message "bytes 1 - 3 are uninitialized" "note how much" { target > *-*-* } .-2 } */ > } > > struct st_union_5 > --- gcc/testsuite/gcc.dg/c23-empty-init-4.c.jj 2024-10-15 13:54:17.611583547 > +0200 > +++ gcc/testsuite/gcc.dg/c23-empty-init-4.c 2024-10-15 16:28:45.686909968 > +0200 > @@ -0,0 +1,207 @@ > +/* Test C23 support for empty initializers: valid use cases. */ > +/* { dg-do run } */ > +/* { dg-options "-std=c23 -pedantic-errors" } */ > + > +extern void abort (void); > +extern void *memset (void *, int, __SIZE_TYPE__); > +#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) > + > +struct A { unsigned char a; long long b; }; > +struct B { unsigned char a; long long b; struct A c[3]; }; > +struct C { struct A a; }; > +struct D { unsigned char a; long long b; struct C c; }; > +union U { unsigned char a; long long b; }; > + > +[[gnu::noipa]] void > +check_A_padding (struct A *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct A, a) + 1; q != r + offsetof (struct A, b); ++q) > + if (*q != 0) > + abort (); > +} > + > +[[gnu::noipa]] void > +check_B_padding (struct B *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct B, a) + 1; q != r + offsetof (struct B, b); ++q) > + if (*q != 0) > + abort (); > + for (int i = 0; i < 3; ++i) > + check_A_padding (&p->c[i]); > +} > + > +[[gnu::noipa]] void > +check_D_padding (struct D *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct D, a) + 1; q != r + offsetof (struct D, b); ++q) > + if (*q != 0) > + abort (); > + check_A_padding (&p->c.a); > +} > + > +[[gnu::noipa]] void > +check_U_padding (union U *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += 1; q != r + sizeof (union U); ++q) > + if (*q != 0) > + abort (); > +} > + > +[[gnu::noipa]] void > +check (struct A *a, struct B *b, struct B *c, struct B *d, struct B *e, > + struct B *f, struct B *g, union U *h, union U *i, union U *j, > + union U *k, struct D *l, struct D *m, struct D *n) > +{ > + /* Empty initializer in C23 clears padding and default initializes > + all members. */ > + if (a->a != 0 || a->b != 0) > + abort (); > + check_A_padding (a); > + if (b->a != 0 || b->b != 0) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (b->c[i].a != 0 || b->c[i].b != 0) > + abort (); > + check_B_padding (b); > + /* In *c the padding between c->a and c->b is indeterminate, but > + padding in c->c[0] (and 1 and 2) zero initialized (already since C11). > */ > + if (c->a != 1 || c->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (c->c[i].a != 0 || c->c[i].b != 0) > + abort (); > + else > + check_A_padding (&c->c[i]); > + /* In *d the padding between d->a and d->b is indeterminate, but > + padding in d->c[0] (and 1) zero initialized (already since C11), > + padding in d->c[2] again indeterminate. */ > + if (d->a != 2 || d->b != 1) > + abort (); > + for (int i = 0; i < 2; ++i) > + if (d->c[i].a != 0 || d->c[i].b != 0) > + abort (); > + else > + check_A_padding (&d->c[i]); > + if (d->c[2].a != 3 || d->c[2].b != 4) > + abort (); > + /* In *e the padding between e->a and e->b is indeterminate, > + but padding in e->c[0] (and 2) zero initialized (since C23). */ > + if (e->a != 1 || e->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (e->c[i].a != 3 + 2 * i || e->c[i].b != 4 + 2 * i) > + abort (); > + else if (i != 1) > + check_A_padding (&e->c[i]); > + /* In *f the padding between f->a and f->b is indeterminate, > + but padding in f->c[0] (and 1 and 2) zero initialized (since C23). */ > + if (f->a != 1 || f->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (f->c[i].a != 3 + 2 * i || f->c[i].b != 4 + 2 * i) > + abort (); > + else > + check_A_padding (&f->c[i]); > + /* In *g all padding is indeterminate. */ > + if (g->a != 1 || g->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (g->c[i].a != 3 + 2 * i || g->c[i].b != 4 + 2 * i) > + abort (); > + /* In *h h->a is default initialized and padding cleared. */ > + if (h->a != 0) > + abort (); > + check_U_padding (h); > + /* In *i (and *j) i->a is initialized and padding indeterminate. */ > + if (i->a != 1 || j->a != 1) > + abort (); > + /* In *k k->b is initialized and there is (likely) no padding. */ > + if (k->b != 1) > + abort (); > + /* Empty initializer in C23 clears padding and default initializes > + all members. */ > + if (l->a != 0 || l->b != 0 || l->c.a.a != 0 || l->c.a.b != 0) > + abort (); > + check_D_padding (l); > + /* In *m the padding between m->a and m->b is indeterminate, but > + padding in m->c.a is zero initialized (already since C11). */ > + if (m->a != 1 || m->b != 2 || m->c.a.a != 0 || m->c.a.b != 0) > + abort (); > + check_A_padding (&m->c.a); > + /* In *n the padding between n->a and n->b is indeterminate, > + but padding in n->c.a zero initialized (since C23). */ > + if (n->a != 1 || n->b != 2 || n->c.a.a != 3 || n->c.a.b != 4) > + abort (); > + check_A_padding (&n->c.a); > +} > + > +[[gnu::noipa]] void > +test (void) > +{ > + struct A a = {}; > + struct B b = {}; > + struct B c = { 1, 2 }; > + struct B d = { .b = 1, .a = 2, .c[2].a = 3, .c[2].b = 4 }; > + struct B e = { 1, 2, .c[2] = {}, .c[1] = { 9 }, .c[0] = {}, > + .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + struct B f = { 1, 2, {}, > + .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + struct B g = { 1, 2, .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + union U h = {}; > + union U i = { 1 }; > + union U j = { .a = 1 }; > + union U k = { .b = 1 }; > + struct D l = {}; > + struct D m = { 1, 2 }; > + struct D n = { 1, 2, {}, .c.a.a = 3, .c.a.b = 4 }; > + check (&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); > +} > + > +[[gnu::noipa]] void > +set (struct A *a, struct B *b, struct B *c, struct B *d, struct B *e, > + struct B *f, struct B *g, union U *h, union U *i, union U *j, > + union U *k, struct D *l, struct D *m, struct D *n) > +{ > + memset (a, ~0, sizeof (*a)); > + memset (b, ~0, sizeof (*b)); > + memset (c, ~0, sizeof (*c)); > + memset (d, ~0, sizeof (*d)); > + memset (e, ~0, sizeof (*e)); > + memset (f, ~0, sizeof (*f)); > + memset (g, ~0, sizeof (*g)); > + memset (h, ~0, sizeof (*h)); > + memset (i, ~0, sizeof (*i)); > + memset (j, ~0, sizeof (*j)); > + memset (k, ~0, sizeof (*k)); > + memset (l, ~0, sizeof (*l)); > + memset (m, ~0, sizeof (*m)); > + memset (n, ~0, sizeof (*n)); > +} > + > +[[gnu::noipa]] void > +prepare (void) > +{ > + struct A a; > + struct B b, c, d, e, f, g; > + union U h, i, j, k; > + struct D l, m, n; > + set (&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); > +} > + > +int > +main () > +{ > + prepare (); > + test (); > +} > --- gcc/testsuite/gcc.dg/gnu11-empty-init-1.c.jj 2024-10-15 > 16:14:23.411063701 +0200 > +++ gcc/testsuite/gcc.dg/gnu11-empty-init-1.c 2024-10-15 16:31:02.302984714 > +0200 > @@ -0,0 +1,199 @@ > +/* Test GNU C11 support for empty initializers. */ > +/* { dg-do run } */ > +/* { dg-options "-std=gnu23" } */ > + > +extern void abort (void); > +extern void *memset (void *, int, __SIZE_TYPE__); > +#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) > + > +struct A { unsigned char a; long long b; }; > +struct B { unsigned char a; long long b; struct A c[3]; }; > +struct C { struct A a; }; > +struct D { unsigned char a; long long b; struct C c; }; > +union U { unsigned char a; long long b; }; > + > +__attribute__((noipa)) void > +check_A_padding (struct A *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct A, a) + 1; q != r + offsetof (struct A, b); ++q) > + if (*q != 0) > + abort (); > +} > + > +__attribute__((noipa)) void > +check_B_padding (struct B *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct B, a) + 1; q != r + offsetof (struct B, b); ++q) > + if (*q != 0) > + abort (); > + for (int i = 0; i < 3; ++i) > + check_A_padding (&p->c[i]); > +} > + > +__attribute__((noipa)) void > +check_D_padding (struct D *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct D, a) + 1; q != r + offsetof (struct D, b); ++q) > + if (*q != 0) > + abort (); > + check_A_padding (&p->c.a); > +} > + > +__attribute__((noipa)) void > +check_U_padding (union U *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += 1; q != r + sizeof (union U); ++q) > + if (*q != 0) > + abort (); > +} > + > +__attribute__((noipa)) void > +check (struct A *a, struct B *b, struct B *c, struct B *d, struct B *e, > + struct B *f, struct B *g, union U *h, union U *i, union U *j, > + union U *k, struct D *l, struct D *m, struct D *n) > +{ > + /* Empty initializer in GNU C11 doesn't guarantee anything about > + padding bits in the initializer directly, but padding in omitted members > + is guaranteed to be zero initialized since C11. */ > + if (a->a != 0 || a->b != 0) > + abort (); > + if (b->a != 0 || b->b != 0) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (b->c[i].a != 0 || b->c[i].b != 0) > + abort (); > + else > + check_A_padding (&b->c[i]); > + /* In *c the padding between c->a and c->b is indeterminate, but > + padding in c->c[0] (and 1 and 2) zero initialized (already since C11). > */ > + if (c->a != 1 || c->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (c->c[i].a != 0 || c->c[i].b != 0) > + abort (); > + else > + check_A_padding (&c->c[i]); > + /* In *d the padding between d->a and d->b is indeterminate, but > + padding in d->c[0] (and 1) zero initialized (already since C11), > + padding in d->c[2] again indeterminate. */ > + if (d->a != 2 || d->b != 1) > + abort (); > + for (int i = 0; i < 2; ++i) > + if (d->c[i].a != 0 || d->c[i].b != 0) > + abort (); > + else > + check_A_padding (&d->c[i]); > + if (d->c[2].a != 3 || d->c[2].b != 4) > + abort (); > + /* In *e all padding is indeterminate. */ > + if (e->a != 1 || e->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (e->c[i].a != 3 + 2 * i || e->c[i].b != 4 + 2 * i) > + abort (); > + /* In *f likewise. */ > + if (f->a != 1 || f->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (f->c[i].a != 3 + 2 * i || f->c[i].b != 4 + 2 * i) > + abort (); > + /* In *g all padding is indeterminate. */ > + if (g->a != 1 || g->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (g->c[i].a != 3 + 2 * i || g->c[i].b != 4 + 2 * i) > + abort (); > + /* In *h h->a is default initialized and padding indeterminate. */ > + if (h->a != 0) > + abort (); > + /* In *i (and *j) i->a is initialized and padding indeterminate. */ > + if (i->a != 1 || j->a != 1) > + abort (); > + /* In *k k->b is initialized and there is (likely) no padding. */ > + if (k->b != 1) > + abort (); > + /* Padding in omitted members is zero initialized since C11. */ > + if (l->a != 0 || l->b != 0 || l->c.a.a != 0 || l->c.a.b != 0) > + abort (); > + check_A_padding (&l->c.a); > + /* In *m the padding between m->a and m->b is indeterminate, but > + padding in m->c.a is zero initialized (already since C11). */ > + if (m->a != 1 || m->b != 2 || m->c.a.a != 0 || m->c.a.b != 0) > + abort (); > + check_A_padding (&m->c.a); > + /* In *n the padding between n->a and n->b is indeterminate, > + and padding in n->c.a too. */ > + if (n->a != 1 || n->b != 2 || n->c.a.a != 3 || n->c.a.b != 4) > + abort (); > +} > + > +__attribute__((noipa)) void > +test (void) > +{ > + struct A a = {}; > + struct B b = {}; > + struct B c = { 1, 2 }; > + struct B d = { .b = 1, .a = 2, .c[2].a = 3, .c[2].b = 4 }; > + struct B e = { 1, 2, .c[2] = {}, .c[1] = { 9 }, .c[0] = {}, > + .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + struct B f = { 1, 2, {}, > + .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + struct B g = { 1, 2, .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + union U h = {}; > + union U i = { 1 }; > + union U j = { .a = 1 }; > + union U k = { .b = 1 }; > + struct D l = {}; > + struct D m = { 1, 2 }; > + struct D n = { 1, 2, {}, .c.a.a = 3, .c.a.b = 4 }; > + check (&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); > +} > + > +__attribute__((noipa)) void > +set (struct A *a, struct B *b, struct B *c, struct B *d, struct B *e, > + struct B *f, struct B *g, union U *h, union U *i, union U *j, > + union U *k, struct D *l, struct D *m, struct D *n) > +{ > + memset (a, ~0, sizeof (*a)); > + memset (b, ~0, sizeof (*b)); > + memset (c, ~0, sizeof (*c)); > + memset (d, ~0, sizeof (*d)); > + memset (e, ~0, sizeof (*e)); > + memset (f, ~0, sizeof (*f)); > + memset (g, ~0, sizeof (*g)); > + memset (h, ~0, sizeof (*h)); > + memset (i, ~0, sizeof (*i)); > + memset (j, ~0, sizeof (*j)); > + memset (k, ~0, sizeof (*k)); > + memset (l, ~0, sizeof (*l)); > + memset (m, ~0, sizeof (*m)); > + memset (n, ~0, sizeof (*n)); > +} > + > +__attribute__((noipa)) void > +prepare (void) > +{ > + struct A a; > + struct B b, c, d, e, f, g; > + union U h, i, j, k; > + struct D l, m, n; > + set (&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); > +} > + > +int > +main () > +{ > + prepare (); > + test (); > +} > --- gcc/testsuite/gcc.dg/gnu11-empty-init-2.c.jj 2024-10-15 > 16:31:28.751611991 +0200 > +++ gcc/testsuite/gcc.dg/gnu11-empty-init-2.c 2024-10-15 16:32:39.637613033 > +0200 > @@ -0,0 +1,5 @@ > +/* Test GNU C11 support for empty initializers. */ > +/* { dg-do run } */ > +/* { dg-options "-std=gnu23 -fzero-init-padding-bits=standard" } */ > + > +#include "gnu11-empty-init-1.c" > --- gcc/testsuite/gcc.dg/gnu11-empty-init-3.c.jj 2024-10-15 > 16:32:05.274097299 +0200 > +++ gcc/testsuite/gcc.dg/gnu11-empty-init-3.c 2024-10-15 16:39:08.618131348 > +0200 > @@ -0,0 +1,204 @@ > +/* Test GNU C11 support for empty initializers. */ > +/* { dg-do run } */ > +/* { dg-options "-std=gnu23 -fzero-init-padding-bits=unions" } */ > + > +extern void abort (void); > +extern void *memset (void *, int, __SIZE_TYPE__); > +#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) > + > +struct A { unsigned char a; long long b; }; > +struct B { unsigned char a; long long b; struct A c[3]; }; > +struct C { struct A a; }; > +struct D { unsigned char a; long long b; struct C c; }; > +union U { unsigned char a; long long b; }; > + > +__attribute__((noipa)) void > +check_A_padding (struct A *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct A, a) + 1; q != r + offsetof (struct A, b); ++q) > + if (*q != 0) > + abort (); > +} > + > +__attribute__((noipa)) void > +check_B_padding (struct B *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct B, a) + 1; q != r + offsetof (struct B, b); ++q) > + if (*q != 0) > + abort (); > + for (int i = 0; i < 3; ++i) > + check_A_padding (&p->c[i]); > +} > + > +__attribute__((noipa)) void > +check_D_padding (struct D *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct D, a) + 1; q != r + offsetof (struct D, b); ++q) > + if (*q != 0) > + abort (); > + check_A_padding (&p->c.a); > +} > + > +__attribute__((noipa)) void > +check_U_padding (union U *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += 1; q != r + sizeof (union U); ++q) > + if (*q != 0) > + abort (); > +} > + > +__attribute__((noipa)) void > +check (struct A *a, struct B *b, struct B *c, struct B *d, struct B *e, > + struct B *f, struct B *g, union U *h, union U *i, union U *j, > + union U *k, struct D *l, struct D *m, struct D *n) > +{ > + /* Empty initializer in GNU C11 doesn't guarantee anything about > + padding bits in the initializer directly, but padding in omitted members > + is guaranteed to be zero initialized since C11. */ > + if (a->a != 0 || a->b != 0) > + abort (); > + if (b->a != 0 || b->b != 0) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (b->c[i].a != 0 || b->c[i].b != 0) > + abort (); > + else > + check_A_padding (&b->c[i]); > + /* In *c the padding between c->a and c->b is indeterminate, but > + padding in c->c[0] (and 1 and 2) zero initialized (already since C11). > */ > + if (c->a != 1 || c->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (c->c[i].a != 0 || c->c[i].b != 0) > + abort (); > + else > + check_A_padding (&c->c[i]); > + /* In *d the padding between d->a and d->b is indeterminate, but > + padding in d->c[0] (and 1) zero initialized (already since C11), > + padding in d->c[2] again indeterminate. */ > + if (d->a != 2 || d->b != 1) > + abort (); > + for (int i = 0; i < 2; ++i) > + if (d->c[i].a != 0 || d->c[i].b != 0) > + abort (); > + else > + check_A_padding (&d->c[i]); > + if (d->c[2].a != 3 || d->c[2].b != 4) > + abort (); > + /* In *e all padding is indeterminate. */ > + if (e->a != 1 || e->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (e->c[i].a != 3 + 2 * i || e->c[i].b != 4 + 2 * i) > + abort (); > + /* In *f likewise. */ > + if (f->a != 1 || f->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (f->c[i].a != 3 + 2 * i || f->c[i].b != 4 + 2 * i) > + abort (); > + /* In *g all padding is indeterminate. */ > + if (g->a != 1 || g->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (g->c[i].a != 3 + 2 * i || g->c[i].b != 4 + 2 * i) > + abort (); > + /* In *h h->a is default initialized and padding indeterminate. */ > + if (h->a != 0) > + abort (); > + /* But -fzero-init-padding-bits=unions overrides that. */ > + check_U_padding (h); > + /* In *i (and *j) i->a is initialized and padding indeterminate. */ > + if (i->a != 1 || j->a != 1) > + abort (); > + /* But -fzero-init-padding-bits=unions overrides that. */ > + check_U_padding (i); > + check_U_padding (j); > + /* In *k k->b is initialized and there is (likely) no padding. */ > + if (k->b != 1) > + abort (); > + /* Padding in omitted members is zero initialized since C11. */ > + if (l->a != 0 || l->b != 0 || l->c.a.a != 0 || l->c.a.b != 0) > + abort (); > + check_A_padding (&l->c.a); > + /* In *m the padding between m->a and m->b is indeterminate, but > + padding in m->c.a is zero initialized (already since C11). */ > + if (m->a != 1 || m->b != 2 || m->c.a.a != 0 || m->c.a.b != 0) > + abort (); > + check_A_padding (&m->c.a); > + /* In *n the padding between n->a and n->b is indeterminate, > + and padding in n->c.a too. */ > + if (n->a != 1 || n->b != 2 || n->c.a.a != 3 || n->c.a.b != 4) > + abort (); > +} > + > +__attribute__((noipa)) void > +test (void) > +{ > + struct A a = {}; > + struct B b = {}; > + struct B c = { 1, 2 }; > + struct B d = { .b = 1, .a = 2, .c[2].a = 3, .c[2].b = 4 }; > + struct B e = { 1, 2, .c[2] = {}, .c[1] = { 9 }, .c[0] = {}, > + .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + struct B f = { 1, 2, {}, > + .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + struct B g = { 1, 2, .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + union U h = {}; > + union U i = { 1 }; > + union U j = { .a = 1 }; > + union U k = { .b = 1 }; > + struct D l = {}; > + struct D m = { 1, 2 }; > + struct D n = { 1, 2, {}, .c.a.a = 3, .c.a.b = 4 }; > + check (&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); > +} > + > +__attribute__((noipa)) void > +set (struct A *a, struct B *b, struct B *c, struct B *d, struct B *e, > + struct B *f, struct B *g, union U *h, union U *i, union U *j, > + union U *k, struct D *l, struct D *m, struct D *n) > +{ > + memset (a, ~0, sizeof (*a)); > + memset (b, ~0, sizeof (*b)); > + memset (c, ~0, sizeof (*c)); > + memset (d, ~0, sizeof (*d)); > + memset (e, ~0, sizeof (*e)); > + memset (f, ~0, sizeof (*f)); > + memset (g, ~0, sizeof (*g)); > + memset (h, ~0, sizeof (*h)); > + memset (i, ~0, sizeof (*i)); > + memset (j, ~0, sizeof (*j)); > + memset (k, ~0, sizeof (*k)); > + memset (l, ~0, sizeof (*l)); > + memset (m, ~0, sizeof (*m)); > + memset (n, ~0, sizeof (*n)); > +} > + > +__attribute__((noipa)) void > +prepare (void) > +{ > + struct A a; > + struct B b, c, d, e, f, g; > + union U h, i, j, k; > + struct D l, m, n; > + set (&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); > +} > + > +int > +main () > +{ > + prepare (); > + test (); > +} > --- gcc/testsuite/gcc.dg/gnu11-empty-init-4.c.jj 2024-10-15 > 16:33:59.011494457 +0200 > +++ gcc/testsuite/gcc.dg/gnu11-empty-init-4.c 2024-10-15 16:39:32.401796175 > +0200 > @@ -0,0 +1,187 @@ > +/* Test GNU C11 support for empty initializers. */ > +/* { dg-do run } */ > +/* { dg-options "-std=gnu23 -fzero-init-padding-bits=all" } */ > + > +extern void abort (void); > +extern void *memset (void *, int, __SIZE_TYPE__); > +#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) > + > +struct A { unsigned char a; long long b; }; > +struct B { unsigned char a; long long b; struct A c[3]; }; > +struct C { struct A a; }; > +struct D { unsigned char a; long long b; struct C c; }; > +union U { unsigned char a; long long b; }; > + > +__attribute__((noipa)) void > +check_A_padding (struct A *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct A, a) + 1; q != r + offsetof (struct A, b); ++q) > + if (*q != 0) > + abort (); > +} > + > +__attribute__((noipa)) void > +check_B_padding (struct B *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct B, a) + 1; q != r + offsetof (struct B, b); ++q) > + if (*q != 0) > + abort (); > + for (int i = 0; i < 3; ++i) > + check_A_padding (&p->c[i]); > +} > + > +__attribute__((noipa)) void > +check_D_padding (struct D *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += offsetof (struct D, a) + 1; q != r + offsetof (struct D, b); ++q) > + if (*q != 0) > + abort (); > + check_A_padding (&p->c.a); > +} > + > +__attribute__((noipa)) void > +check_U_padding (union U *p) > +{ > + unsigned char *q = (unsigned char *) p; > + unsigned char *r = (unsigned char *) p; > + for (q += 1; q != r + sizeof (union U); ++q) > + if (*q != 0) > + abort (); > +} > + > +__attribute__((noipa)) void > +check (struct A *a, struct B *b, struct B *c, struct B *d, struct B *e, > + struct B *f, struct B *g, union U *h, union U *i, union U *j, > + union U *k, struct D *l, struct D *m, struct D *n) > +{ > + /* All padding bits are well defined with -fzero-init-padding-bits=all. */ > + if (a->a != 0 || a->b != 0) > + abort (); > + check_A_padding (a); > + if (b->a != 0 || b->b != 0) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (b->c[i].a != 0 || b->c[i].b != 0) > + abort (); > + check_B_padding (b); > + if (c->a != 1 || c->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (c->c[i].a != 0 || c->c[i].b != 0) > + abort (); > + check_B_padding (c); > + if (d->a != 2 || d->b != 1) > + abort (); > + for (int i = 0; i < 2; ++i) > + if (d->c[i].a != 0 || d->c[i].b != 0) > + abort (); > + if (d->c[2].a != 3 || d->c[2].b != 4) > + abort (); > + check_B_padding (d); > + if (e->a != 1 || e->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (e->c[i].a != 3 + 2 * i || e->c[i].b != 4 + 2 * i) > + abort (); > + check_B_padding (e); > + if (f->a != 1 || f->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (f->c[i].a != 3 + 2 * i || f->c[i].b != 4 + 2 * i) > + abort (); > + check_B_padding (f); > + if (g->a != 1 || g->b != 2) > + abort (); > + for (int i = 0; i < 3; ++i) > + if (g->c[i].a != 3 + 2 * i || g->c[i].b != 4 + 2 * i) > + abort (); > + check_B_padding (g); > + if (h->a != 0) > + abort (); > + check_U_padding (h); > + if (i->a != 1 || j->a != 1) > + abort (); > + check_U_padding (i); > + check_U_padding (j); > + /* In *k k->b is initialized and there is (likely) no padding. */ > + if (k->b != 1) > + abort (); > + if (l->a != 0 || l->b != 0 || l->c.a.a != 0 || l->c.a.b != 0) > + abort (); > + check_D_padding (l); > + if (m->a != 1 || m->b != 2 || m->c.a.a != 0 || m->c.a.b != 0) > + abort (); > + check_D_padding (m); > + if (n->a != 1 || n->b != 2 || n->c.a.a != 3 || n->c.a.b != 4) > + abort (); > + check_D_padding (n); > +} > + > +__attribute__((noipa)) void > +test (void) > +{ > + struct A a = {}; > + struct B b = {}; > + struct B c = { 1, 2 }; > + struct B d = { .b = 1, .a = 2, .c[2].a = 3, .c[2].b = 4 }; > + struct B e = { 1, 2, .c[2] = {}, .c[1] = { 9 }, .c[0] = {}, > + .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + struct B f = { 1, 2, {}, > + .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + struct B g = { 1, 2, .c[0].a = 3, .c[0].b = 4, .c[1].a = 5, .c[1].b = 6, > + .c[2].a = 7, .c[2].b = 8 }; > + union U h = {}; > + union U i = { 1 }; > + union U j = { .a = 1 }; > + union U k = { .b = 1 }; > + struct D l = {}; > + struct D m = { 1, 2 }; > + struct D n = { 1, 2, {}, .c.a.a = 3, .c.a.b = 4 }; > + check (&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); > +} > + > +__attribute__((noipa)) void > +set (struct A *a, struct B *b, struct B *c, struct B *d, struct B *e, > + struct B *f, struct B *g, union U *h, union U *i, union U *j, > + union U *k, struct D *l, struct D *m, struct D *n) > +{ > + memset (a, ~0, sizeof (*a)); > + memset (b, ~0, sizeof (*b)); > + memset (c, ~0, sizeof (*c)); > + memset (d, ~0, sizeof (*d)); > + memset (e, ~0, sizeof (*e)); > + memset (f, ~0, sizeof (*f)); > + memset (g, ~0, sizeof (*g)); > + memset (h, ~0, sizeof (*h)); > + memset (i, ~0, sizeof (*i)); > + memset (j, ~0, sizeof (*j)); > + memset (k, ~0, sizeof (*k)); > + memset (l, ~0, sizeof (*l)); > + memset (m, ~0, sizeof (*m)); > + memset (n, ~0, sizeof (*n)); > +} > + > +__attribute__((noipa)) void > +prepare (void) > +{ > + struct A a; > + struct B b, c, d, e, f, g; > + union U h, i, j, k; > + struct D l, m, n; > + set (&a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); > +} > + > +int > +main () > +{ > + prepare (); > + test (); > +} > > Jakub >