Why does issuing empty asm commands swap variables?

A.H*_*tov 4 c++ assembly gcc inline-assembly

So I was messing around with inline assembly and compiled this using GCC 9. The result was that the two variables a and b were swapped without actually issuing any direct commands.

#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);
}
Run Code Online (Sandbox Code Playgroud)

Can somebody explain what is going on here?

Pet*_*des 6

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

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

这与使用未初始化变量的C未定义行为非常相似。

要查看发生了什么,请将asm注释放在展开的asm模板中,%0%1在该模板内的asm注释内。这不会对GCC进行寄存器分配产生任何影响:它不在乎模板是否使用它隐式或显式选择的寄存器。您需要编写一个有用的模板并将其与操作数约束匹配。

Godbolt编译器资源管理器上使用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     
Run Code Online (Sandbox Code Playgroud)

(我没有在asm上发表裸露的注释,而是在NOP指令上添加了注释,
asm ("nop #%0 %1": "=r"(c):"r"(a)); 这样编译器-浏览器过滤不会删除它们。asm模板是纯文本替换,然后再提供给汇编器,因此,这仍然与GCC完全等效使用相同的选项编译了原始源代码。)

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

在第3种情况下,该值c已经在寄存器中,并且将3任何位置都优化了,因为"=r"(c)在读取之前会覆盖该值。

也许您在禁用优化的情况下进行编译?您也可以这样做,然后跟踪发生的事情。(也许GCC eax每次都会选择输入和输出)。我通常不介意查看反优化的-O0asm,因为它充满了存储/重新加载的噪音。