I've tossed this into godbolt
On gcc (8.3) and clang (8.0.0) with -O3
optimizations, that function compiles into a no-op function. Impressively, this is even true for MSVC v19.20 with /O2
optimizations.
GCC 8.3 / Clang 8.0.0:
a(int):
ret
MSVC v19.20 (x64):
i$ = 8
void a(int) PROC ; a, COMDAT
ret 0
void a(int) ENDP ; a
I've also taken the liberty of making the example non-trivial. The code I'm using from here on is:
#include <iostream>
void a(int i)
{
std::cout << "hello\n";
if (i > 0)
a(i - 1);
}
The compiler output for this with gcc trunc with -O3
optimizations enabled is the following:
.LC0:
.string "hello\n"
a(int):
push rbx
mov ebx, edi
jmp .L3
.L6:
sub ebx, 1
.L3:
mov edx, 6
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
test ebx, ebx
jg .L6
pop rbx
ret
_GLOBAL__sub_I_a(int):
sub rsp, 8
mov edi, OFFSET FLAT:_ZStL8__ioinit
call std::ios_base::Init::Init() [complete object constructor]
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:_ZStL8__ioinit
mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
add rsp, 8
jmp __cxa_atexit
From careful examination, the only call
instruction is to the io method to write the message on each iteration. A test
is then performed (the if statement). If i > 0
, control jumps up, decrements i
and does it all again. The other branch of the if statement (false case) simply returns (after stack cleanup).
Thus, there is no buildup of stack frames even in this non-trivial example. It is performed via the jmp
instruction because (as you said) the previous execution information is irrelevant.