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

            Bug ID: 127121
           Summary: [C++][Optimization] Unnecessary zero-initialization of
                    a parameter from a same-type prvalue with a
                    user-provided default constructor
           Product: gcc
           Version: 16.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: danny321974345 at gmail dot com
  Target Milestone: ---

Reproducer:
struct S {
    S() {}
    int a[5];
};

void foo(int*);

int callee(S s) {
    foo(s.a);
    return s.a[0];
}

int caller() {
    return callee(S{});
}

int local() {
    S s{};
    foo(s.a);
    return s.a[0];
}

Observed output:
"callee(S)":
        sub     rsp, 8
        lea     rdi, [rsp+16]
        call    "foo(int*)"
        mov     eax, DWORD PTR [rsp+16]
        add     rsp, 8
        ret
"caller()":
        sub     rsp, 40
        mov     rdi, rsp
        mov     DWORD PTR [rsp], 0
        mov     DWORD PTR [rsp+4], 0
        mov     DWORD PTR [rsp+8], 0
        mov     DWORD PTR [rsp+12], 0
        mov     DWORD PTR [rsp+16], 0
        call    "foo(int*)"
        mov     eax, DWORD PTR [rsp]
        add     rsp, 40
        ret
"local()":
        sub     rsp, 40
        mov     rdi, rsp
        call    "foo(int*)"
        mov     eax, DWORD PTR [rsp]
        add     rsp, 40
        ret

link: https://godbolt.org/z/jvvWbs3Ma
Options: -std=c++23 -O2

The initialization rules in [dcl.init] apply to all initializations regardless
of syntactic context, including the initialization of a function parameter
([dcl.init.general]/1). https://eel.is/c%2B%2Bdraft/dcl.init.general?#1.

For the S parameter of callee, the initializer expression is the S{} argument.
Since S{} is a prvalue of type S and the destination type is also S,
[dcl.init.general]/15.6.1 specifies that the initializer expression is used to
initialize the destination object.
https://eel.is/c++draft/dcl.init.general?#15.6.1

Both S{} in callee(S{}) and S s{} in local() use a braced-init-list as the
initializer, and are therefore list-initialization ([dcl.init.general]/15.1).
https://eel.is/c++draft/dcl.init.general?#15.1

For an empty initializer list initializing a class with a default constructor,
list-initialization specifies value-initialization ([dcl.init.list]/3.5). Thus,
both cases perform value-initialization of S.
https://eel.is/c%2B%2Bdraft/dcl.init#list-3.5

According to [dcl.init.general]/8, value-initialization of a class first
zero-initializes the object only if the selected default constructor is not
user-provided, and then default-initializes it. Here S::S() is user-provided,
so value-initialization does not perform the preliminary zero-initialization;
it only performs default-initialization, which calls S::S(). Since S::S() does
not initialize a, the five zero stores emitted when passing S{} to callee
appear unnecessary. https://eel.is/c%2B%2Bdraft/dcl.init.general?#8

Reply via email to