For the C code below, GCC x86-64 10.2 from Compiler Explorer emits assembly that I pasted further below.
One instruction is subq $40, %rsp
. The question is, how come subtracting 40 bytes from %rsp
does not make the stack misaligned?
My understanding is:
- Right before
call foo
, the stack is 16 bytes aligned; call foo
places an 8 bytes return address on the stack, so the stack gets misaligned;- But
pushq %rbp
atfoo
's start places another 8 bytes on the stack, so it gets 16 bytes aligned again; - So the stack is 16 bytes aligned right before
subq $40, %rsp
. As a result, decreasing%rsp
by 40 bytes must break the alignment?
Obviously, GCC emits valid assembly in terms of keeping the stack aligned, so I must be missing something.
(I tried replacing GCC with CLANG, and CLANG emits subq $48, %rsp
— just as I'd intuitively expect.)
So, what am I missing in the GCC-generated assembly? How does it keep the stack 16 bytes aligned?
int bar(int i) { return i; }
int foo(int p0, int p1, int p2, int p3, int p4, int p5, int p6) {
int sum = p0 + p1 + p2 + p3 + p4 + p5 + p6;
return bar(sum);
}
int main() {
return foo(0, 1, 2, 3, 4, 5, 6);
}
bar:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
popq %rbp
ret
foo:
pushq %rbp
movq %rsp, %rbp
subq $40, %rsp
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)
movl %edx, -28(%rbp)
movl %ecx, -32(%rbp)
movl %r8d, -36(%rbp)
movl %r9d, -40(%rbp)
movl -20(%rbp), %edx
movl -24(%rbp), %eax
addl %eax, %edx
movl -28(%rbp), %eax
addl %eax, %edx
movl -32(%rbp), %eax
addl %eax, %edx
movl -36(%rbp), %eax
addl %eax, %edx
movl -40(%rbp), %eax
addl %eax, %edx
movl 16(%rbp), %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %edi
call bar
leave
ret
main:
pushq %rbp
movq %rsp, %rbp
pushq $6
movl $5, %r9d
movl $4, %r8d
movl $3, %ecx
movl $2, %edx
movl $1, %esi
movl $0, %edi
call foo
addq $8, %rsp
leave
ret
bar
does not require stack alignment so it didn't bother. If you make itextern int bar(int i);
then the stack will be properly aligned. – Jesterbar
so it does require alignment, for example because it calls another function itself, the compiler notices that too. – Jester-O0
. Apparently it is a feature of ipa stack alignment which is the default in GCC. You can turn it on/off with-fipa-stack-alignment
and-fno-ipa-stack-alignment
in GCC versions >= 9.0. A comparison of the output with the option on/off in GCC: godbolt.org/z/a1YdjG – Michael Petchgcc
can see that all functions below foo have no alignment requirements, it deems it unnecessary. – paxdiablo