【问题标题】:does gcc preserve callee save registersgcc 是否保留被调用者保存寄存器
【发布时间】:2016-03-31 20:22:22
【问题描述】:

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

void foo(void) {
    __asm__ volatile ("mov $123, %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    $0x7b,%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()之后没有在main函数中使用%rbx还是因为gcc没有提供这样的保证,我必须在修改前自己保存在foo中?

【问题讨论】:

  • GCC 不注意你的内联汇编,它只是逐字发出。做正确的事取决于你。
  • 这将是一个相当笨拙的代码,它会死记硬背地保存和恢复每个寄存器。
  • 正如@OliverCharlesworth 所写。你一个人在这里,gcc不知道你的意图是什么。
  • 它将保存受其编译的 C 代码影响的寄存器。组装机可以让您进入引擎盖,但它不会为您更换火花塞。
  • 您可以使用extended inline assembly 告诉编译器您破坏了哪些寄存器,然后它将保存它们。

标签: c gcc x86-64 abi


【解决方案1】:

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

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

现在编译器知道你正在使用/修改 rbx,所以它会在需要时保存它。

请注意,您确实希望这样做而不是尝试自己保存它,因为如果 gcc 也想将此寄存器用于此函数中的某些内容,它只会保存一次。

【讨论】:

  • asm 关键字比 __asm__ 更受欢迎,除非您编写的库代码即使用户执行 #define asm 1 之类的操作也需要工作。哦,但是 OP 使用了__asm__,所以你只是在关注它。
【解决方案2】:

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

组装机允许您进入引擎盖,但它不会为您更换火花塞。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-27
    • 2019-08-18
    • 2012-03-05
    • 2011-10-15
    • 1970-01-01
    • 1970-01-01
    • 2019-05-13
    相关资源
    最近更新 更多