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
>

Reply via email to