为什么发出空的 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 噪音。
所以我在弄乱内联汇编并使用 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 噪音。