为什么发出空的asm命令会交换变量? [英] Why does issuing empty asm commands swap variables?

查看:40
本文介绍了为什么发出空的asm命令会交换变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我搞乱了内联汇编,并使用 GCC 9 对其进行了编译.结果是两个变量a和b被交换而没有实际发出任何直接命令.

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);
}

有人可以解释这里发生了什么吗?

Can somebody explain what is going on here?

推荐答案

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

Basically random chance of variable allocation. (Or actually GCC's internal machinery's first choices).

您使用了仅输出"= r" 的asm操作数,但是asm模板实际上并未写入该寄存器,因此您可以得到GCC选择的寄存器中包含的任何值./strong>

You used output-only "=r" asm operands but then your asm template doesn't actually write that register so you get whatever value was sitting in the register GCC picked.

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

This is a lot like C undefined behaviour for using an uninitialized varible.

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

To see what happened, put asm comments in the asm template that expand %0 and %1 inside the template inside an asm comment. This will have no impact on how GCC does register allocation: it doesn't care if the template uses the registers it picks implicitly or explicitly; it's up to you to write a useful template and match that to the operand constraints.

将您的代码放在

With your code on the 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     

(我将注释放在诸如
的NOP指令上,而不是裸露的asm注释 asm("nop#%0%1":"= r"(c):"r"(a)); 因此编译器-浏览器过滤不会删除它们.在将asm模板提供给汇编程序之前,它们是纯文本替换,因此,这仍然完全等同于GCC如何使用相同的选项来编译原始源代码.)

(Instead of a bare asm comment, I put the comments on NOP instructions like
asm ("nop #%0 %1": "=r"(c):"r"(a)); so compiler-explorer filtering wouldn't remove them. asm templates are pure text substitution before feeding it to the assembler so again this is still exactly equivalent to how GCC would have compiled your original source with the same options.)

在前两种情况下,gcc决定选择与输入相同的寄存器作为输出,以便它们碰巧像分配一样工作.

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

In the 3rd case, c was already in a register, and putting a 3 anywhere was optimized away because "=r"(c) overwrite that value before it was ever read.

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

Perhaps you were compiling with optimization disabled? You could do that too, and follow what happened. (Probably GCC would pick eax for both input and output each time). I normally don't bother looking at anti-optimized -O0 asm because its full of store/reload noise.

这篇关于为什么发出空的asm命令会交换变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆