From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 48) id 1CE8F3858D35; Wed, 22 Nov 2023 11:40:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1CE8F3858D35 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1700653238; bh=1gswZtrhoPPfEziEk1aZu4S7MG9Lo8JMqFx8gvii9Dg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=aJBr5jX+bLNsUy3FZ4PZMUATeklvnrvFznDB6l+SfSdX1kx/DI5SLd5sS4vkIuu2s AquUMEh1bqmDrrFhOykfsl17sDfHDcHEpNmZXCCSIo/04Qam+E++3n1GHvjpPZlf0x iAVYfqvfR5P0ezEeq2ETsWZHtLXI7loTvvLu4Dac= From: "redi at gcc dot gnu.org" To: gcc-bugs@gcc.gnu.org Subject: [Bug c++/112666] Missed optimization: Value initialization zero-initializes members with user-defined constructor Date: Wed, 22 Nov 2023 11:40:37 +0000 X-Bugzilla-Reason: CC X-Bugzilla-Type: changed X-Bugzilla-Watch-Reason: None X-Bugzilla-Product: gcc X-Bugzilla-Component: c++ X-Bugzilla-Version: 11.4.0 X-Bugzilla-Keywords: X-Bugzilla-Severity: normal X-Bugzilla-Who: redi at gcc dot gnu.org X-Bugzilla-Status: UNCONFIRMED X-Bugzilla-Resolution: X-Bugzilla-Priority: P3 X-Bugzilla-Assigned-To: unassigned at gcc dot gnu.org X-Bugzilla-Target-Milestone: --- X-Bugzilla-Flags: X-Bugzilla-Changed-Fields: Message-ID: In-Reply-To: References: Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Bugzilla-URL: http://gcc.gnu.org/bugzilla/ Auto-Submitted: auto-generated MIME-Version: 1.0 List-Id: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D112666 --- Comment #1 from Jonathan Wakely --- (In reply to Francisco Paisana from comment #0) > The struct "C" which is just "B" and an int is much slower at being > initialized than B when value initialization (via {}) is used. However, my > understanding of the C++ standard is that members with a user-defined > default constructor do not need to be zero-initialized in this situation. I think that's not quite right. Types with a user-provided default construc= tor will not be zero-initialized when value-init is used. B does have a user-provided default constructor, so value-init for an object of type B do= es not perform zero-init first. But that applies when constructing a complete B object, not when constructi= ng a member subobject. C does not have a user-provided default constructor, so value-initialization means: "- the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;" So first it's zero-initialized, which means: "- if T is a (possibly cv-qualified) non-union class type, its padding bits (6.8.1) are initialized to zero bits and each non-static data member, each non-virtual base class subobject, and, if the object is not a base class subobject, each virtual base class subobject is zero-initialized;" This specifically says that *each non-static data member ... is zero-initialized." So the B subobject must be zero-initialized. That's not = the same as when you value-init a B object. > Looking at the godbolt assembly output, I see that both `A a{}` and `C c{= }` > generate a memset instruction, while `B b{}` doesn't. Clang, on the other > hand, seems to initialize C almost as fast as B. I don't know whether Clang considers the zero-init to be dead stores that a= re clobbered by B() and so can be eliminated, or something else. But my understanding of the standard is that requiring zero-init of B's members is very intentional here. > This potentially missed optimization in gcc is particularly nasty for > structs with large embedded storage (e.g. structs that contain C-arrays, > std::arrays, or static_vectors). Arguably, the problem here is that B has a default ctor that intentionally leaves members uninitialized. If you want to preserve that behaviour in typ= es that contain a B subobject, then you also need to give those types (e.g. C = in your example) a user-provided default ctor.=