【问题标题】:Decrementing stack by 24 when only 8 bytes are needed?当只需要 8 个字节时将堆栈减 24?
【发布时间】: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 以便于查看。)

标签: c gcc assembly callstack


【解决方案1】:

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

Changing optimization to -O3 results in GCC subtracting only eight from the stack pointer.

【讨论】:

  • 谢谢你!但是“这让人们更容易看到 a、b 和 c 在汇编代码中的使用位置”——如果是我的情况,它如何帮助我查看在汇编代码中使用的位置?跨度>
  • @Dip:你有一个很简单的功能。假设它是一个更大的函数。在汇编代码的不同位置,它存储和加载来自rbp 下方的 36 个字节的内容。有时是a,有时是b,有时是c。您如何从加载或存储指令中知道它正在加载或存储哪个?您不会,您必须查看其余代码才能弄清楚它在做什么。但是如果它只加载和存储来自rbp以下36的a、来自40的b和来自44的c,那么你可以很容易地看到哪个是哪个。
猜你喜欢
  • 2015-04-15
  • 2015-08-23
  • 2023-01-13
  • 2015-05-31
  • 2020-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
相关资源
最近更新 更多