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

Why does issuing empty asm commands swap variables?

本文关键字:交换 变量 命令 asm 为什么      更新时间:2023-10-16

所以我在处理内联汇编,并使用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模板中,将%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     

(我把注释放在NOP指令上,而不是简单的asm注释,比如
asm ("nop #%0 %1": "=r"(c):"r"(a));,这样编译器资源管理器过滤就不会删除它们。asm模板在将其提供给汇编程序之前是纯文本替换,所以这仍然与GCC使用相同选项编译原始源代码的方式完全相同。(

在前两种情况下,gcc决定为输出和输入选择相同的寄存器,这样这些寄存器的工作方式就像赋值一样

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

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