Say I have a function where depending on some runtime condition an expensive automatic object is created or a cheap automatic object is created:
void foo() {
if (runtimeCondition) {
int x = 0;
} else {
SuperLargeObject y;
}
}
When the compiler allocates memory for a stack frame for this function, will it just allocate enough memory to store the SuperLargeObject
, and if the condition leading to the int
is true that extra memory will just be unused? Or will it allocate memory some other way?
>Solution :
It depends on your compiler and on the optimization settings. In unoptimized builds most C++ compilers will probably allocate stack memory for both objects and use one or the other based on which branch is taken. In optimized builds things get more interesting:
If both objects (the int
and the SuperLargeObject
are not used and the compiler can prove that constructing SuperLargeObject
does not have side effects, both allocations will be elided.
If the objects escape the function, i.e. their addresses are passed to another function, the compiler has to provide memory for them. But since their lifetimes don’t overlap, they can be stored in overlapping memory regions. It is up to the compiler if that actually happens or not.
As you can see here, different compilers generate different code:
ICC
f(bool):
sub rsp, 408
test dil, dil
lea rax, QWORD PTR [400+rsp]
lea rdx, QWORD PTR [rsp]
cmovne rdx, rax
mov rdi, rdx
call escape(void const*)
add rsp, 408
ret
ICC allocates enough memory two store both objects and then selects between the two non-overlapping regions based on the runtime condition (using cmov
) and passes the selected pointer to the escaping function.
GCC
f(bool):
sub rsp, 408
mov rdi, rsp
call escape(void const*)
add rsp, 408
ret
GCC is a bit smarter. It also allocates a region of 408 bytes, but passes the same pointer to escape
and emits no cmov
.
Clang
f(bool):
sub rsp, 408
test edi, edi
lea rdi, [rsp + 8]
call escape(void const*)@PLT
add rsp, 408
ret
Clang does the same, but for reasons I don’t understand it still tests the condition.
You should also note, that when the compiler can place either or both of the variables in registers, no memory will be allocated at all. For int
‘s and long
‘s and other small objects that is most often the case, if their addresses to not escape the function.