【发布时间】:2020-07-21 09:00:37
【问题描述】:
我有 C 代码:
long fib(long n) {
if (n < 2) return 1;
return fib(n-1) + fib(n-2);
}
int main(int argc, char** argv) {
return 0;
}
我通过运行 gcc -O0 -fno-optimize-sibling-calls -S file.c 编译得到的汇编代码尚未优化:
.file "long.c"
.text
.globl fib
.type fib, @function
fib:
.LFB5:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $24, %rsp
.cfi_offset 3, -24
movq %rdi, -24(%rbp)
cmpq $1, -24(%rbp)
jg .L2
movl $1, %eax
jmp .L3
.L2:
movq -24(%rbp), %rax
subq $1, %rax
movq %rax, %rdi
call fib
movq %rax, %rbx
movq -24(%rbp), %rax
subq $2, %rax
movq %rax, %rdi
call fib
addq %rbx, %rax
.L3:
addq $24, %rsp
popq %rbx
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE5:
.size fib, .-fib
.globl main
.type main, @function
main:
.LFB6:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size main, .-main
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits
我的问题是:
为什么我们将堆栈指针减 24 subq $24, %rsp?正如我所看到的,我们只存储一个元素,第一个参数n 在%rdi 中,在最初的两次推送之后在堆栈上。那么我们为什么不直接将堆栈指针减 8,然后将 n 移动到 -8(%rbp) 呢?所以
subq $8, %rsp
movq %rdi, -8(%rbp)
【问题讨论】:
-
这不是 ABI 要求,因为它只需要 16 字节对齐,因此 8 与 24 一样好。这是未优化的代码。你不应该期望高效的输出。
-
GCC 有时会浪费 16 字节的堆栈空间,即使启用了优化也是如此。如果您查看
-O3编译器生成的 asm,这只是您可以发现的少量遗漏优化之一。 (我推荐godbolt.org 以便于查看。)