@@ -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. */
@@ -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
@@ -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.
@@ -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;
@@ -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:
@@ -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
@@ -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;
@@ -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;
@@ -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
@@ -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 ();
+}
@@ -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 ();
+}
@@ -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"
@@ -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 ();
+}
@@ -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 ();
+}