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