2017-08-28 163 views
2

对于娱乐,我正在学习GNU扩展程序集,使用带有32位Linux目标的x86的AT & T语法。我刚刚花了最后三个小时编码两个可能的解决方案,以交换两个整数变量ab的值,我的解决方案都不能完全解决我的问题。首先,让我们来看看我的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

+1

既然你传递寄存器你可以只要做'xchg%0,%1'。使用移动只需要1个临时寄存器。将%0复制到该寄存器。然后将%1复制到%0,然后将临时寄存器复制到%1。临时将需要列在clobber列表中 –

+0

另请参见XOR交换算法https://en.wikipedia.org/wiki/XOR_swap_algorithm –

+1

您现有的行内汇编还存在问题,即a和b都是输入和输出。所以** both **应该使用读写约束''+ r“' –

回答

2

以下是评论的一些解决方案:

解决方案#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 
+0

你不需要一个早期的clobber,因为'xchg'是一个单一的指令。 –

+1

@PeterCordes我在评论中犯了一个错字。本来是为了%不是,因为我在谈论交换性质。他把我的错误传达给了答案。 –

+0

@MichaelPetch我认为[交换](https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html)只适用于输入(又名“只读操作数”)。 –

相关问题