gcc 是否保留被调用者保存寄存器

does gcc preserve callee save registers

您可能已经猜到了,问题是 gcc 会自动保存被调用者保存寄存器还是我应该自己做?我以为 gcc 会为我做那件事但是当我写这段代码时

void foo(void) {
    __asm__ volatile ("mov 3, %rbx");
}

void main(void) {
    foo();
}

gcc a.c && objdump -d a.out 之后我看到了这个

00000000004004f6 <foo>:
  4004f6:   55                      push   %rbp
  4004f7:   48 89 e5                mov    %rsp,%rbp
  4004fa:   48 c7 c3 7b 00 00 00    mov    [=11=]x7b,%rbx
  400501:   90                      nop
  400502:   5d                      pop    %rbp
  400503:   c3                      retq   

0000000000400504 <main>:
  400504:   55                      push   %rbp
  400505:   48 89 e5                mov    %rsp,%rbp
  400508:   e8 e9 ff ff ff          callq  4004f6 <foo>
  40050d:   90                      nop
  40050e:   5d                      pop    %rbp
  40050f:   c3                      retq

根据 x86-64 ABI %rbx 是一个被调用者保存寄存器,但在这段代码中 gcc 在修改之前没有将其保存在 foo 中。是因为我调用foo()后没有在主函数中使用%rbx还是因为gcc没有提供这样的保证,我必须自己保存在[=17=中] 修改之前?

死记硬背地保存和恢复每个寄存器将是相当笨拙的代码。编译器在它编译的 C 代码中保存寄存器,但是你在这里是你自己的,gcc 不知道你的意图是什么。

组装器可让您深入了解引擎盖,但不会为您更换火花塞。

Gcc 将自动保存和恢复所有被调用者保存的寄存器它知道被使用。它知道自己使用的寄存器,但如果您告诉它,它只会知道内联汇编中使用的寄存器。这就是 'clobbers' 列表的用途:

void foo(void) {
    __asm__ volatile ("mov 3, %%rbx" : : : "rbx");
}

现在编译器知道你是using/modifying rbx,所以如果需要它会保存它。

请注意,你真的想这样做而不是试图自己保存它,因为如果 gcc 也想在这个函数中使用寄存器,这样它只会被保存一次。