9

我不明白这应该如何工作。

GCC 内联汇编器很难做到正确,但非常具体地标记破坏信息,以便编译器知道你在做什么。

Microsoft Visual C++ 的内联汇编器真的很容易使用(它似乎总是 Just Work),但我不知道它对你的代码有什么样的保证或假设。

VC++ 是否尝试“自动检测”哪些寄存器被破坏?它如何知道寄存器和堆栈指针将如何更改?它会做任何假设吗?如果是这样,你如何绕过这些假设?

4

2 回答 2

20

至于为什么 GCC 不像 MSVC 那样做,有几个原因:

  1. GCC 是一个可重定向的编译器,但汇编语法只是原始文本。为了使破坏检测是自动的,GCC 必须解析汇编语言以了解正在破坏的寄存器(包括由操作码未命名寄存器的指令隐式破坏的寄存器)。这必须适用于所有架构。目前,GCC 不解析汇编语言;%它只是在执行替换后将其粘贴到程序集输出中。这个想法是生成并避免解析。

  2. 在 GCC 内联汇编语言中,破坏寄存器是例外而不是规则。原因是它是一种比 MSVC 更复杂的语言。GCC 的内联汇编语言为您分配寄存器。所以你通常不%eax直接使用类似的东西,而是使用%0GCC 替换可用寄存器的代码。(要做到这一点,编译器不必理解汇编语言!您表达了确保 GCC 替换%0适合使用的适当寄存器的约束。) 如果您的汇编代码覆盖硬编码,您只需要 clobber寄存器,而不是覆盖 GCC 为您分配的输出操作数

Note that with GCC inline assembly, you don't have to write the code which loads your assembly language operands from the C expressions that produce their initial values, or which stores your result operands into the C destinations. For instance you just express that there is to be an input operand of type "r" (register) which is derived from the expression foo->bar + 1. GCC allocates the register and generates the code to load it from foo->bar + 1, and then replaces occurences of %0 in your assembly template with the name of that register.

于 2012-05-08T20:02:17.470 回答
5

从文档中引用:

使用 __asm 在 C/C++ 函数中编写汇编语言时,不需要保留 EAX、EBX、ECX、EDX、ESI 或 EDI 寄存器。例如,在使用内联汇编编写函数的 POWER2.C 示例中,power2 函数不保留 EAX 寄存器中的值。但是,使用这些寄存器会影响代码质量,因为寄存器分配器不能使用它们来跨 __asm 块存储值。此外,通过在内联汇编代码中使用 EBX、ESI 或 EDI,您可以强制编译器在函数序言和结尾处保存和恢复这些寄存器。

您应该为 __asm 块的范围保留您使用的其他寄存器(例如 DS、SS、SP、BP 和标志寄存器)。您应该保留 ESP 和 EBP 寄存器,除非您有理由更改它们(例如,切换堆栈)。另请参阅优化内联汇编

于 2012-05-08T19:20:37.433 回答