为什么GCC内联汇编程序需要blobbering信息,但MSVC不需要

时间:2012-05-08 19:14:40

标签: c visual-c++ gcc inline-assembly

我不明白这是怎么回事。

GCC内联汇编程序很难做到,但是对于标记符号信息非常具体,因此编译器知道你正在做什么。

Microsoft Visual C ++的inline assember非常容易使用(它似乎总是Just Work),但我不知道它对你的代码有什么样的保证或假设。

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

2 个答案:

答案 0 :(得分:19)

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

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

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

  3. 请注意,使用GCC内联汇编时,您不必编写从生成其初始值的C表达式加载汇编语言操作数的代码,也不必将结果操作数存储到C目标中。例如,您只是表示存在类型为"r"(寄存器)的输入操作数,该操作数派生自表达式foo->bar + 1。 GCC分配寄存器并生成代码以从foo->bar + 1加载它,然后将汇编模板中%0的出现替换为该寄存器的名称。

答案 1 :(得分:4)

引自the docs

  

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

     

您应该保留您使用的其他寄存器(例如DS,SS,SP,BP和标志寄存器)作为__asm块的范围。您应该保留ESP和EBP寄存器,除非您有某些理由更改它们(例如,切换堆栈)。另请参阅Optimizing Inline Assembly