所以我在处理内联汇编,并使用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
(。我通常不会去看反优化的-O0
asm,因为它充满了存储/重新加载噪音。