如何编写一个内嵌gnu扩展程序集的短块来交换两个整数变量的值?
对于娱乐,我正在学习GNU扩展程序集,使用带有32位Linux目标的x86的AT & T语法。我刚刚花了最后三个小时编码两个可能的解决方案,以交换两个整数变量a
和b
的值,我的解决方案都不能完全解决我的问题。首先,让我们来看看我的TODO障碍在一些细节:如何编写一个内嵌gnu扩展程序集的短块来交换两个整数变量的值?
int main()
{
int a = 2, b = 1;
printf("a is %d, b is %d\n", a, b);
// TODO: swap a and b using extended assembly, and do not modify the program in any other way
printf("a is %d, b is %d\n", a, b);
}
阅读this HOWTO后,我写了下面的内联扩展汇编代码。这是我第一次尝试换整数:
asm volatile("movl %0, %%eax;"
"movl %1, %%ecx;"
"movl %%ecx, %0;"
: "=r" (a)
: "r" (b)
: "%eax", "%ecx");
asm volatile("movl %%eax, %0;"
: "=r" (b)
: "r" (a)
: "%eax", "%ecx");
我的理由是,设置A = B,我需要一个扩展组件调用从装配分离设置B = A。于是我编写了两个扩展的程序集调用,编译了我的代码,即gcc -m32 asmPractice.c,并运行了a.out。结果如下:
a为2,b为1
a为1,b为1
看怎么说也不能正常工作,然后我决定结合两个扩展汇编程序调用,并写了这一点:
asm volatile("movl %0, %%eax;"
"movl %1, %%ecx;"
"movl %%ecx, %0;"
"movl %%eax, %1;"
: "=r" (a)
: "r" (b));
重新编译和链接之后,我的代码仍无法正常交换两个值。你自己看。下面是我的结果:
a为2,b为1
a为1,b为1
以下是评论的一些解决方案:
解决方案#0 (最好的选择):https://gcc.gnu.org/wiki/DontUseInlineAsm
即使是零指令解决方案也会影响常量传播,以及任何其他优化,这些优化会让gcc知道有关值的任何信息。它还会强制编译器在此时同时在寄存器中同时存在两个变量。在考虑使用inline-asm而不是builtins/intrinsics时,请始终记住这些缺点。
解决方案#1:xchg
,其成本与大多数CPU上的指令3 mov
大致相同。
asm("xchg %0, %1;" : "+r" (a), "+r" (b));
解决方案#2:纯粹使用GNU C inline asm约束。
asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b));
见行动on the Godbolt compiler explorer所有三种解决方案,其中包括击败优化他们的例子:
int swap_constraints(int a, int b) {
asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b));
return a;
}
// Demonstrate the optimization-defeating behaviour:
int swap_constraints_constants(void) {
int a = 10, b = 20;
return swap_constraints(a, b) + 15;
}
swap_constraints_constants:
movl $10, %edx
movl $20, %eax
addl $15, %eax
ret
与用纯C互换:
swap_noasm_constants:
movl $35, %eax # the add is done at compile-time, and `a` is optimized away as unused.
ret
你不需要一个早期的clobber,因为'xchg'是一个单一的指令。 –
@PeterCordes我在评论中犯了一个错字。本来是为了%不是,因为我在谈论交换性质。他把我的错误传达给了答案。 –
@MichaelPetch我认为[交换](https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html)只适用于输入(又名“只读操作数”)。 –
既然你传递寄存器你可以只要做'xchg%0,%1'。使用移动只需要1个临时寄存器。将%0复制到该寄存器。然后将%1复制到%0,然后将临时寄存器复制到%1。临时将需要列在clobber列表中 –
另请参见XOR交换算法https://en.wikipedia.org/wiki/XOR_swap_algorithm –
您现有的行内汇编还存在问题,即a和b都是输入和输出。所以** both **应该使用读写约束''+ r“' –