扩展GCC asm中多个输入和输出操作数的正确用法是什么?
在寄存器约束下的扩展GCC asm中,多个输入和输出操作数的正确用法是什么?考虑一下这个问题的最小版本。以下是GCC、AT&T语法中的简短扩展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
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重写它。