From patchwork Tue Oct 15 17:58:30 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jakub Jelinek X-Patchwork-Id: 1997598 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=JWkjY8XO; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XShgF4rDZz1xvK for ; Wed, 16 Oct 2024 04:59:05 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 9D5793858405 for ; Tue, 15 Oct 2024 17:59:03 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTP id 93C243858D21 for ; Tue, 15 Oct 2024 17:58:39 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 93C243858D21 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 93C243858D21 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729015124; cv=none; b=NeoFqryOFFA7PsM4rBLJn4is7Gx5mbweqx9ZgvVOQosIkIAa3+RhBfu2YL4AoO7nohNFr3otwGPfXYefIqZqQJ7OcdX/SaHZ+8IJaTL2Js6igmEI31ZmCl3CuooxVZfrNtphI8URQLuB9W0Wxws1G94W0MLJBryj2iG3MnuWAk4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729015124; c=relaxed/simple; bh=FCKKqqXeTmOV0DWkakFZMbg5kqfFSPAZjWN6TfG21hQ=; h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version; b=pg8big5Pq1AsTmOh3sFCGZbYLYlvi6NZRhzgb7ognHzjeTta1nRJowAkN3p+y1ik3HLcqKNqrsjbXu8WtLkeZILbCN1Pldka+/7ki3Z3WBAlVBKXzDXh/WXCCC1uTUbVfHlCOlRQQzXfpi/zl48JAe078ZsoPkLh8vhgem9mi14= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1729015119; h=from:from:reply-to:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references; bh=FMRgVg3hKKjmVoefxXVMCuW86JWK27kKlDsQa3jsnY8=; b=JWkjY8XOEiwlZKdGX9DAQlpdkvDCOUAf9aNZwzbh2FARqc1q3YSrHPDZbUcESH/RKyPd+j FDiiChJTSwDwcTelrEUsIpq6GJ1Zo55GxnBCXMf5JBiYMCMLTWsDQ2feFmqEoswRy4h16G zcRn3rOQqaJTozkJnic93T4CoNgQaZM= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-113-Wtj3MuNYMX-1cBT2DE1I2Q-1; Tue, 15 Oct 2024 13:58:36 -0400 X-MC-Unique: Wtj3MuNYMX-1cBT2DE1I2Q-1 Received: from mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.15]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 24EA7195609D; Tue, 15 Oct 2024 17:58:35 +0000 (UTC) Received: from tucnak.zalov.cz (unknown [10.45.224.16]) by mx-prod-int-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 0E2021956086; Tue, 15 Oct 2024 17:58:33 +0000 (UTC) Received: from tucnak.zalov.cz (localhost [127.0.0.1]) by tucnak.zalov.cz (8.17.1/8.17.1) with ESMTPS id 49FHwVpg1563039 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Tue, 15 Oct 2024 19:58:31 +0200 Received: (from jakub@localhost) by tucnak.zalov.cz (8.17.1/8.17.1/Submit) id 49FHwUI21563038; Tue, 15 Oct 2024 19:58:30 +0200 Date: Tue, 15 Oct 2024 19:58:30 +0200 From: Jakub Jelinek To: "Joseph S. Myers" , Jason Merrill Cc: Marek Polacek , Richard Biener , Alexandre Oliva , gcc-patches@gcc.gnu.org Subject: [PATCH] expr, c, gimplify, v3: Don't clear whole unions [PR116416] Message-ID: References: <0ea08c56-6241-46a8-9c78-60525270233f@redhat.com> <36q63814-8n8n-3nro-1s6q-29s3qp7nr1s8@fhfr.qr> <0e004a7c-e30c-4d70-b4ed-a6472361d4c2@redhat.com> MIME-Version: 1.0 In-Reply-To: <0e004a7c-e30c-4d70-b4ed-a6472361d4c2@redhat.com> X-Scanned-By: MIMEDefang 3.0 on 10.30.177.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-0.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, RCVD_IN_SBL_CSS, SCC_5_SHORT_WORD_LINES, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Jakub Jelinek Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org 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? 2024-10-15 Jakub Jelinek 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. Jakub --- 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 (); +}