【发布时间】:2016-06-30 09:07:07
【问题描述】:
在寄存器约束下,扩展 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);
根据https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html,语法看起来是正确的但是,我一定是忽略了一些东西,或者犯了一些我由于某种原因看不到的小错误。
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),它会给出预期的输出,并且程序集看起来更合理。
【问题讨论】:
-
你可能想看看early clobbers & modifier。特别是我觉得你需要
=&r作为你的 output0 操作数,因为该寄存器在你的汇编器模板的最后一条指令之前被修改。 GCC 会认为它也可以重用该寄存器作为输入。&将防止早期的clobber分配寄存器被用作输入寄存器 -
您可能还想考虑在输入操作数上使用
g约束而不是r。由于输出仅定义为寄存器,并且模板中的mov指令可以占用至少一个内存或立即值操作数,因此您可以让编译器有机会使用g执行其他优化。g约束记录为允许任何寄存器、内存或立即整数操作数,除了不是通用寄存器的寄存器 -
特别是如果您使用
g作为输入操作数约束,编译器应该能够意识到一些输入实际上是常量(立即)值,这应该允许一些代码减少。如果您使用 GCC 使用-O3的优化级别进行编译,您可以更好地看到这些优化 -
@MichaelPetch 好吧,如果您想完全枚举允许的操作数并为编译器提供最大的灵活性,您将使用
"=r,r,rm,rm", "=r,rm,r,rm" : "g,g,ri,ri", "g,ri,g,ri"。