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 fooplaces an 8 bytes return address on the stack, so the stack gets misaligned;- But
pushq %rbpatfoo'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%rspby 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