如何编写一个嵌入式gnu扩展程序集的短块来交换两个整数变量的值? [英] How to write a short block of inline gnu extended assembly to swap the values of two integer variables?

查看:113
本文介绍了如何编写一个嵌入式gnu扩展程序集的短块来交换两个整数变量的值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了娱乐,我正在学习使用AT& T语法的gnu扩展程序集,用于带有32位Linux目标的x86。我刚刚花了最后三个小时编码两个可能的解决方案,以交换两个整数变量 a b

  int main()
{
int a = 2,b = 1;
printf(a是%d,b是%d \ n,a,b);
// TODO:使用扩展程序集交换a和b,并且不要以任何其他方式修改程序
printf(a是%d,b是%d \ n,a,b );
}

阅读这个HOWTO ,我写了以下内联扩展汇编代码。这是我第一次尝试交换整数:

  asm volatile(movl%0,%% eax;
movl%1,%% ecx;
movl %% ecx,%0;
:= r(a)
:r(b)
:%eax,%ecx);
$ b asm volatile(movl %% eax,%0;
:= r(b)
:r(a)
: %eax,%ecx);

我的推理是设置a = b,我需要一个扩展的程序集调用,组装来设置b = a。于是我编写了两个扩展的程序集调用,编译了我的代码,即gcc -m32 asmPractice.c,并运行了a.out。结果如下:


是2,b是1

a是1,b是1 b

然后我决定合并两个扩展的汇编程序调用,并且写道:

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $' %ecx;
movl %% ecx,%0;
movl %% eax,%1;
:= r(a)
: r(b));

重新编译和链接后,我的代码仍然无法正确交换两个值。你自己看。这是我的结果:


是2,b是1

a是1,b是1



以下是一些解决方案:



解决方案#0(最佳选项): https:// gcc.gnu.org/wiki/DontUseInlineAsm

即使零指令解决方案也能够消除常量传播,以及任何其他优化,包括gcc知道任何有关值的任何优化。它还会强制编译器在此时同时在寄存器中同时存在两个变量。解决方案#1: xchg 在考虑使用inline-asm而不是builtins / intrinsics时,始终要考虑到这些缺点。 >,其成本与大多数CPU上的3 mov 指令大致相同。

  asm(xchg%0,%1;:+ r(a),+ r(b)); 

解决方案#2:纯粹使用GNU C inline约束。
$ (a),0(b))b $ b

  asm(:= r ; 

查看所有三种解决方案在Godbolt编译器浏览器上,包括它们的例子击败优化:

  int swap_constraints(int a,int b){
asm(:= r (a),= r(b):1(a),0(b));
返回a;
}

//演示优化失败行为:
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

vs。用纯C交换:

  swap_noasm_constants:
movl $ 35,%eax#add在编译时完成,并且`a`被优化为未使用。
ret


For entertainment, I am learning gnu extended assembly using AT&T syntax for x86 with a 32bit Linux target. I have just spent the last three hours coding two possible solutions to my challenge of swapping the values of two integer variables a and b, and neither of my solutions completely solved my problem. First, let's look at my TODO obstacle in some more detail:

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);
}

After reading this HOWTO, I wrote the following inline extended assembler code. Here is my first attempt at swapping the integers:

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");

My reasoning was that to set a = b, I needed an extended assembly call that was separated from the assembly to set b = a. So I wrote the two extended assembly calls, compiled my code, i.e., gcc -m32 asmPractice.c, and ran a.out. The results were as follows:

a is 2, b is 1

a is 1, b is 1

Seeing how that did not work properly, I then decided to combine the two extended assembler calls, and wrote this:

asm volatile("movl %0, %%eax;"
    "movl %1, %%ecx;"
    "movl %%ecx, %0;"
    "movl %%eax, %1;"
  : "=r" (a)
  : "r" (b));

After recompiling and linking, my code still does not correctly swap both values. See for yourself. Here are my results:

a is 2, b is 1

a is 1, b is 1

解决方案

Here are some solutions from the comments:

Solution #0 (best option): https://gcc.gnu.org/wiki/DontUseInlineAsm
Even the zero-instruction solution defeats constant-propagation, and any other optimization that involves gcc knowing anything about the value. It also forces the compiler to have both variables in registers at the same time at that point. Always keep these downsides in mind when considering using inline-asm instead of builtins / intrinsics.

Solution #1: xchg, which costs about the same as 3 mov instructions on most CPUs.

asm("xchg %0, %1;" : "+r" (a), "+r" (b));

Solution #2: purely using GNU C inline asm constraints.

asm("" : "=r" (a), "=r" (b) : "1" (a), "0" (b));

See all three solutions in action on the Godbolt compiler explorer, including examples of them defeating optimization:

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

vs. with a pure C swap:

swap_noasm_constants:
    movl    $35, %eax    # the add is done at compile-time, and `a` is optimized away as unused.
    ret

这篇关于如何编写一个嵌入式gnu扩展程序集的短块来交换两个整数变量的值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆