为什么发出空的 asm 命令交换变量?

Why does issuing empty asm commands swap variables?

所以我在弄乱内联汇编并使用 GCC 9 编译它。 结果是两个变量 a 和 b 被交换了,实际上没有发出任何直接命令。

#include<cstdio>
int main(){
    int a(1),b(2),c(3);
    asm ("": "=r"(c):"r"(a));
    asm ("": "=r"(a):"r"(b));
    asm ("": "=r"(b):"r"(c));
    printf("%d %d %d", a,b,c);
}

有人能解释一下这是怎么回事吗?

变量分配基本上是随机的。 (或者实际上是 GCC 内部机制的首选)。

您使用了仅输出 "=r" asm 操作数,但随后您的 asm 模板实际上并未写入该寄存器,因此您获得了 GCC 选择的寄存器中的任何值。

这很像使用未初始化变量的 C 未定义行为。

要查看发生了什么,将 asm 注释放入 asm 模板中,在 asm 注释中扩展模板内的 %0%1。这将 no 影响 GCC 如何进行寄存器分配:它不关心模板是隐式还是显式地使用它选择的寄存器;您可以编写一个有用的模板并将其与操作数约束相匹配。

将您的代码放在 Godbolt compiler explorer, with gcc9.2 -O3 -fverbose-asm:

.intel_syntax noprefix
.LC0:
        .string "%d %d %d"
main:
        sub     rsp, 8    #,
        mov     edi, OFFSET FLAT:.LC0     #,
        xor     eax, eax  #
        mov     ecx, 1    # tmp87,
        mov     esi, 2    # tmp89,
        nop  #ecx ecx   # c, tmp87
        nop  #esi esi   # a, tmp89
        nop  #edx ecx   # b, c
        call    printf  #
        xor     eax, eax  #
        add     rsp, 8    #,
        ret     

(而不是一个简单的 asm 评论,我把评论放在 NOP 指令上,比如
asm ("nop #%0 %1": "=r"(c):"r"(a)); 所以编译器浏览器过滤不会删除它们。 asm 模板在将其提供给汇编器之前是纯文本替换,因此这仍然完全等同于 GCC 使用相同选项编译原始源代码的方式。)

在前两种情况下,gcc 决定为输出选择与输入相同的寄存器,因此它们恰好像赋值一样工作。

在第三种情况下,c 已经在寄存器中,将 3 放在任何地方都被优化掉了,因为 "=r"(c) 在读取之前覆盖了该值。

也许您在编译时禁用了优化?你也可以这样做,然后跟随发生的事情。 (可能 GCC 每次都会为输入和输出选择 eax)。我通常不会费心去查看反优化的 -O0 asm,因为它充满了 store/reload 噪音。