首页 > 解决方案 > 当只需要 8 个字节时将堆栈减 24?

问题描述

我有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,?正如我所看到的,在最初的两次推送之后,我们只在堆栈中存储一个元素,第一个参数nin 。%rdi那么我们为什么不直接将堆栈指针减 8,然后移动n-8(%rbp)? 所以

subq    $8, %rsp
movq    %rdi, -8(%rbp)

标签: cgccassemblycallstack

解决方案


GCC 没有完全优化-O0,甚至没有使用它的堆栈。(这可以通过使堆栈的某些使用对人类更透明来帮助调试。例如,如果对象a的活动生命周期(由程序中的使用定义bc而不是由C 标准中的生命周期)-O3,但可能在堆栈中单独保留了位置-O0,这使得人们更容易看到在汇编代码中使用的位置,a浪费的 16 个字节可能是副作用这个,因为这些空间可能被保留用于这个小功能没有使用的某些目的,例如在需要时保存某些寄存器的空间。)bc

将优化更改为-O3导致 GCC 仅从堆栈指针中减去 8。


推荐阅读