Let's take the following basic C function and the intentionally unoptimized assembly it produces:
int main() {
int i = 4;
return 3;
}
It produces the following (unoptimized) assembly, which all makes sense to me:
main:
pushq %rbp
movq %rsp, %rbp
movl $4, -4(%rbp)
movl $3, %eax
popq %rbp
ret
However, as soon as I add in a function call, there are two instructions that I don't quite understand:
void call() {};
int main() {
int i = 4;
call();
return 3;
}
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp <-- why is it subtracting 16?
movl $4, -4(%rbp)
movl $0, %eax <-- why is it first clearing the %eax register?
call call
movl $3, %eax
leave
ret
If the stackframe needs to be 16-byte aligned, how does the subq $16, %rsp help with that? Doesn't the initial pushq %rbp instruction already offset it by 8 and now it's at +24 ? Or what are the points of those two lines in question above?
The first variant stores the local variable in the red zone, a 128 byte area below the stack pointer that is not changed by signal handlers. The second variant cannot use the red zone because the callq instruction writes to the (original) red zone, clobbering the local variable stored there. (The called function could write to the original red zone as well, of course.)
%eax is set to zero because the function definition declares no prototype, so the compiler has to assume it is a variadic function. The %eax (actually %al) is used to optimize the implementation of variadic functions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With