Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
扩展GCC asm中多个输入和输出操作数的正确用法是什么?_Gcc_Assembly - Fatal编程技术网

扩展GCC asm中多个输入和输出操作数的正确用法是什么?

扩展GCC asm中多个输入和输出操作数的正确用法是什么?,gcc,assembly,Gcc,Assembly,在寄存器约束下的扩展GCC asm中,多个输入和输出操作数的正确用法是什么?考虑一下这个问题的最小版本。以下是GCC、AT&T语法中的简短扩展asm代码: int input0 = 10; int input1 = 15; int output0 = 0; int output1 = 1; asm volatile("mov %[input0], %[output0]\t\n" "mov %[input1], %[out

在寄存器约束下的扩展GCC asm中,多个输入和输出操作数的正确用法是什么?考虑一下这个问题的最小版本。以下是GCC、AT&T语法中的简短扩展asm代码:

    int input0 = 10;
    int input1 = 15;
    int output0 = 0;
    int output1 = 1;

    asm volatile("mov %[input0], %[output0]\t\n"
                 "mov %[input1], %[output1]\t\n"
                 : [output0] "=r" (output0), [output1] "=r" (output1)
                 : [input0] "r" (input0), [input1] "r" (input1)
                 :);

    printf("output0: %d\n", output0);
    printf("output1: %d\n", output1);
语法看起来是正确的,但是,我一定是忽略了什么,或者犯了一些我因为某种原因看不到的小错误

GCC 5.3.0 p1.0(无编译器参数)的输出为:

输出0:10
产出1:10

预期产出为:

输出0:10
产出1:15

在GDB中查看它显示:

0x0000000000400581:mov eax,DWORD PTR[rbp-0x10]
0x0000000000400584:mov edx,DWORD PTR[rbp-0xc]
0x0000000000400587:mov edx,eax
0x0000000000400589:mov eax,edx
0x000000000040058b:mov DWORD PTR[rbp-0x8],edx
0x000000000040058e:mov DWORD PTR[rbp-0x4],eax

从我所看到的,它用input0加载eax,用input1加载edx。然后用eax覆盖edx,用edx覆盖eax,使两者相等。然后,它将这些数据写回output0和output1


如果我对输出使用内存约束(=m)而不是寄存器约束(=r),它将提供预期的输出,并且程序集看起来更合理。

问题在于GCC假设所有输出操作数仅在消耗所有输入操作数之后,才在指令末尾写入。这意味着它可以使用相同的操作数(如寄存器)作为输入操作数和输出操作数,这就是此处发生的情况。解决方案是使用标记来标记
[output0]
,以便GCC知道它在asm语句结束之前已写入

例如:

 asm volatile("mov %[input0], %[output0]\t\n"
              "mov %[input1], %[output1]\t\n"
              : [output0] "=&r" (output0), [output1] "=r" (output1)
              : [input0] "r" (input0), [input1] "r" (input1)
              :);

您不需要将
[output1]
标记为早期的clobber,因为它只在指令末尾写入,所以它是否使用与您可能想查看的
[input0]
[input1]
相同的寄存器并不重要。特别是,我觉得您的output0操作数需要
=&r
,因为该寄存器是在汇编程序模板的最后一条指令之前修改的。GCC认为它也可以将该寄存器重新用作输入<代码>和代码>将防止早期登记的寄存器被用作输入寄存器,您也可能想考虑在输入操作数上使用<代码> g <代码>约束而不是代码> R>代码>。由于输出仅定义为寄存器,并且模板中的
mov
指令可以至少使用一个内存或立即值操作数,因此编译器有机会使用
g
执行其他优化
g
约束被记录为允许使用任何寄存器、内存或立即数整数操作数,但非通用寄存器的寄存器除外。特别是,如果使用
g
作为输入操作数约束,编译器应该能够意识到某些输入实际上是常量(立即数)值,这应该可以减少一些代码。如果您使用GCC编译时使用的优化级别为
-O3
@MichaelPetch,那么您可以更好地看到这些优化。如果您希望完全枚举允许的操作数,并为编译器提供您使用的最大灵活性,
“=r,r,rm,rm”,“=r,rm,r,rm”:“g,g,ri,ri”,“g,g,ri”
,还有我最喜欢的派对技巧:
asm(“:”=r(输出0),“=r(输出1):“0”(输入0),“1”(输入1))。没错,将输入“移动”到输出不需要asm。好吧,我没有被邀请参加很多聚会…@Attributedensorfield和Ross:这不需要是易变的。它没有副作用,只是一个纯粹的“函数”,产生的输出值仅取决于输入。@PeterCordes它也不需要是asm语句。@RossRidge:我假设OP计划在将这个实验作为构建块/垫脚石工作后,用真实的指令替换
mov
指令。我喜欢@David Wohlferd的评论,因为问题中的代码经常犯错误,将mov指令硬编码到内联asm中,而不是让编译器为其执行。@PeterCordes我也是。这就是为什么我认为没有必要提出改进建议,无论是删除volatile关键字,还是完全枚举约束,消除MOV指令,或者最好的改进是,完全用纯C重写它。