https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128185

            Bug ID: 128185
           Summary: auto-var-init memset not removed when a non-inlined
                    callee fully initializes the object
           Product: gcc
           Version: 17.0
               URL: https://godbolt.org/z/bechb55hx
            Status: UNCONFIRMED
          Keywords: missed-optimization
          Severity: normal
          Priority: P3
         Component: ipa
          Assignee: unassigned at gcc dot gnu.org
          Reporter: jmcruz at uma dot es
  Target Milestone: ---
              Host: x86_64-linux-gnu
            Target: x86_64-linux-gnu
             Build: x86_64-linux-gnu

With -std=c++26, a local object whose non-inlined constructor writes every
byte before any read is still zero-initialized by the caller first.

Testcase (no includes):

  void use(const void*);
  struct A {
    char buf[1024];
    [[gnu::noinline]] A();
  };
  A::A() { __builtin_memset(buf, 'x', sizeof buf); }
  void test_a() { A a; use(&a); }

g++ -std=c++26 -O2 (also -O3), GCC 16.2 and trunk:

  test_a():
    memset(rsp, 0, 1024)
    call A::A()
    call use

A::A() is an exact definition in the same TU. -fdump-tree-optimized shows
that it writes the whole object before any read:

  A::A (struct A * const this)
    _1 = &this_2(D)->buf;
    __builtin_memset (_1, 120, 1024); [tail call]

while the caller still keeps the opaque auto-init:

  a = .DEFERRED_INIT (1024, 4, &"a"[0]);
  A::A (&a);

Clang (-ftrivial-auto-var-init=zero -O2) removes the store entirely. In its
LLVM IR the `this` parameter of A::A() carries `initializes((0, 1024))`, a
parameter attribute that LLVM infers (FunctionAttrs) and that DSE uses across
calls. It was proposed as "[RFC] LLVM: new `initialized` parameter attribute
for improved interprocedural DSE" (March 2024) and committed under the name
`initializes`:
 
https://discourse.llvm.org/t/rfc-llvm-new-initialized-parameter-attribute-for-improved-interprocedural-dse/77337
  https://github.com/llvm/llvm-project/commit/5ece35df8586
It also trims the partial case (constructor writing only part of the object);
a testcase for that case has been added to PR121894.

As in PR121894 (comment #0, partial overwrite case), the auto-init remains a
.DEFERRED_INIT until RTL expansion, so a fix probably needs DSE to handle
.DEFERRED_INIT against callee kill summaries (ipa-modref).

With C++26 enabling auto-init by default, every non-inlined constructor call
on a local object pays this double initialization. WG21 paper P4424R0
(B. Revzin) reports out-of-line constructors as a source of such double
initializations when moving a large codebase to C++26 with GCC 16:
https://wg21.link/p4424r0

Compiler (gcc -v):
  gcc version 17.0.0 20261025 (experimental), revision
  cd7a85774a992c4759ebd03a26ce03cbb934e4d9 (Compiler Explorer gcc-snapshot)
  Target: x86_64-linux-gnu
  Command: g++ -std=c++26 -O2 -S test.cc

Compiler Explorer (GCC trunk, Clang trunk and Clang's LLVM IR side by side):
https://godbolt.org/z/bechb55hx

Reply via email to