]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
expr, c: Don't clear whole unions [PR116416]
authorJakub Jelinek <jakub@redhat.com>
Thu, 28 Nov 2024 10:18:07 +0000 (11:18 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 28 Nov 2024 10:38:08 +0000 (11:38 +0100)
As discussed earlier, we currently clear padding bits even when we
don't have to and that causes pessimization of emitted code,
e.g. for
union U { int a; long b[64]; };
void bar (union U *);
void
foo (void)
{
  union U u = { 0 };
  bar (&u);
}
we need to clear just u.a, not the whole union, but on the other side
in cases where the standard requires padding bits to be zeroed, like for
C23 {} initializers of aggregates with padding bits, or for C++11 zero
initialization we don't do that.

This patch
a) moves some of the stuff into complete_ctor_at_level_p (but not
   all the *p_complete = 0; case, for that it would need to change
   so that it passes around the ctor rather than just its type) and
   changes the handling of unions
b) introduces a new option, so that users can either get the new
   behavior (only what is guaranteed by the standards, the default),
   or previous behavior (union padding zero initialization, no such
   guarantees in structures) or also a guarantee in structures
c) introduces a new CONSTRUCTOR flag which says that the padding bits
   (if any) should be zero initialized (and sets it for now in the C
   FE for C23 {} initializers).

Am not sure the CONSTRUCTOR_ZERO_PADDING_BITS flag is really needed
for C23, if there is just empty initializer, I think we already mark
it as incomplete if there are any missing initializers.  Maybe with
some designated initializer games, say
void foo () {
  struct S { char a; long long b; };
  struct T { struct S c; } t = { .c = {}, .c.a = 1, .c.b = 2 };
...
}
Is this supposed to initialize padding bits in C23 and then the .c.a = 1
and .c.b = 2 stores preserve those padding bits, so is that supposed
to be different from struct T t2 = { .c = { 1, 2 } };
?  What about just struct T t3 = { .c.a = 1, .c.b = 2 }; ?

And I haven't touched the C++ FE for the flag, because I'm afraid I'm lost
on where exactly is zero-initialization done (vs. other types of
initialization) and where is e.g. zero-initialization of a temporary then
(member-wise) copied.
Say
struct S { char a; long long b; };
struct T { constexpr T (int a, int b) : c () { c.a = a; c.b = b; } S c; };
void bar (T *);

void
foo ()
{
  T t (1, 2);
  bar (&t);
}
Is the c () value-initialization of t.c followed by c.a and c.b updates
which preserve the zero initialized padding bits?  Or is there some
copy construction involved which does member-wise copying and makes the
padding bits undefined?
Looking at (older) clang++ with -O2, it initializes also the padding bits
when c () is used and doesn't with c {}.
For GCC, note that there is that optimization from Alex to zero padding bits
for optimization purposes for small aggregates, so either one needs to look
at -O0 -fdump-tree-gimple dumps, or use larger structures which aren't
optimized that way.

2024-11-28  Jakub Jelinek  <jakub@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-5.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.

14 files changed:
gcc/c/c-parser.cc
gcc/c/c-typeck.cc
gcc/common.opt
gcc/doc/invoke.texi
gcc/expr.cc
gcc/flag-types.h
gcc/gimple-fold.cc
gcc/testsuite/gcc.dg/c23-empty-init-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-empty-init-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-empty-init-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-empty-init-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/gnu11-empty-init-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/plugin/infoleak-1.c
gcc/tree.h

index 6eaea2cf1ca7d5a44ffc0a6f60304977b6243f57..0acba0542df6d033f726c4161fda19a29ee2ef10 100644 (file)
@@ -6281,6 +6281,7 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
   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)
@@ -6294,6 +6295,8 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
     {
       pedwarn_c11 (brace_loc, OPT_Wpedantic,
                   "ISO C forbids empty initializer braces before C23");
+      if (flag_isoc23)
+       zero_init_padding_bits = true;
     }
   else
     {
@@ -6353,6 +6356,10 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
   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
+      && ret.value
+      && 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;
index e429ce9d1761e7d36e229a8180a623d4411a2088..23ce0fc788dcf87603522dea5ab48b6feaf799f8 100644 (file)
@@ -9489,6 +9489,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.  */
 
@@ -9559,6 +9562,7 @@ struct constructor_stack
   char outer;
   char incremental;
   char designated;
+  bool zero_padding_bits;
   int designator_depth;
 };
 
@@ -9734,6 +9738,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;
@@ -9747,6 +9752,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;
@@ -9882,6 +9888,7 @@ push_init_level (location_t loc, int implicit,
   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;
@@ -9896,6 +9903,9 @@ push_init_level (location_t loc, int implicit,
   /* 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)
     {
@@ -9942,6 +9952,7 @@ push_init_level (location_t loc, int implicit,
       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))
@@ -10200,6 +10211,8 @@ pop_init_level (location_t loc, int implicit,
            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;
        }
     }
 
@@ -10225,6 +10238,7 @@ pop_init_level (location_t loc, int implicit,
   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;
index 000cc86e7ac6c39777ee5b45021a2d582dafcf7f..bb226ac61e6a12c2a3edd52cb7e5923c53122867 100644 (file)
@@ -3520,6 +3520,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.
index 51dc871e6bc157adffa34df26de2490421841a1b..1b7b712085f0a9e3f84a2ce1d99850927c614e09 100644 (file)
@@ -751,7 +751,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}.
@@ -19990,6 +19991,43 @@ The default value of this option is enabled, thus the only useful form
 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
index cf87167ec0cdb55c81723ac07cb308d32baf675c..f4939140bb51d86f9d094313c83122a7855b494b 100644 (file)
@@ -7226,6 +7226,28 @@ categorize_ctor_elements_1 (const_tree ctor, HOST_WIDE_INT *p_nz_elts,
   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;
@@ -7300,19 +7322,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;
index df56337f7e841014d82de7351b9716a0c9f6213d..cbf078799f9bd1e79fdc7d776fb21acac0ee9630 100644 (file)
@@ -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.  */
index 39112379c5926254a95b39248ac1f5372860d9f6..e85744651d089d1be6f87c02dfa657338d2f8822 100644 (file)
@@ -4858,12 +4858,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:
diff --git a/gcc/testsuite/gcc.dg/c23-empty-init-5.c b/gcc/testsuite/gcc.dg/c23-empty-init-5.c
new file mode 100644 (file)
index 0000000..495c2c1
--- /dev/null
@@ -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 ();
+}
diff --git a/gcc/testsuite/gcc.dg/gnu11-empty-init-1.c b/gcc/testsuite/gcc.dg/gnu11-empty-init-1.c
new file mode 100644 (file)
index 0000000..1d083de
--- /dev/null
@@ -0,0 +1,199 @@
+/* Test GNU C11 support for empty initializers.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu11" } */
+
+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 ();
+}
diff --git a/gcc/testsuite/gcc.dg/gnu11-empty-init-2.c b/gcc/testsuite/gcc.dg/gnu11-empty-init-2.c
new file mode 100644 (file)
index 0000000..0d9d874
--- /dev/null
@@ -0,0 +1,5 @@
+/* Test GNU C11 support for empty initializers.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu11 -fzero-init-padding-bits=standard" } */
+
+#include "gnu11-empty-init-1.c"
diff --git a/gcc/testsuite/gcc.dg/gnu11-empty-init-3.c b/gcc/testsuite/gcc.dg/gnu11-empty-init-3.c
new file mode 100644 (file)
index 0000000..13ad8bf
--- /dev/null
@@ -0,0 +1,204 @@
+/* Test GNU C11 support for empty initializers.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu11 -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 ();
+}
diff --git a/gcc/testsuite/gcc.dg/gnu11-empty-init-4.c b/gcc/testsuite/gcc.dg/gnu11-empty-init-4.c
new file mode 100644 (file)
index 0000000..410e3e0
--- /dev/null
@@ -0,0 +1,187 @@
+/* Test GNU C11 support for empty initializers.  */
+/* { dg-do run } */
+/* { dg-options "-std=gnu11 -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 ();
+}
index b4958e7bbb6394f9ca617f0e569fc46073d6f1e3..4c5a86aeb0aade5e5cbbb60c1671452f7d3f05c6 100644 (file)
@@ -123,9 +123,12 @@ void test_union_2a (void __user *dst, u8 v)
 
 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 v)
 
 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
index 785ff071a139737170d5b1ed4a968431a0f55f6e..4437ab92355750ad2fc6cf1e14cb8a459bb26778 100644 (file)
@@ -1233,6 +1233,9 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
   (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