内联gcc程序集和局部变量(double)

时间:2013-07-08 05:15:50

标签: c++ gcc assembly inline-assembly att

我正在尝试使用gcc / g ++的内联 asm 指令(我不得不说我以前在 MSVC 上使用了英特尔语法,那是 breeze )。 我正在玩 double 值,以下 my_func2 似乎在执行后崩溃:

  #include <iostream>

  void my_func(const double *in, double *out) {
    asm("mov %0, %%r8" : : "r"(in));
    asm("movupd (%%r8), %%xmm0" :);
    asm("movupd (%%r8), %%xmm1" :);
    asm("addpd %%xmm1, %%xmm0" :);
    asm("movupd %%xmm0, (%0)" : : "r"(out) : "%r8", "%xmm0", "%xmm1");
  }

  double my_func2(const double *in) {
    double  ret = 0.0;

    asm("mov %0, %%r8" : : "r"(in));
    asm("movupd (%%r8), %%xmm0" :);
    asm("movupd (%%r8), %%xmm1" :);
    asm("addpd %%xmm1, %%xmm0" :);
    asm("movupd %%xmm0, %0" : "=m"(ret) : : "memory", "%r8", "%xmm0", "%xmm1");

    return ret;
 }

  int main(int argc, char *argv[]) {
    const double    a = 1.0;
    double      b = 0.0;
    my_func(&a, &b);
    std::cout << "b:" << b << std::endl;
    b = my_func2(&a);
    std::cout << "b:" << b << std::endl;
  }

我得到的错误是具体的(当我使用 gdb 运行时):

Program received signal SIGBUS, Bus error.
0x00000000004008e1 in main (argc=<error reading variable: Cannot access memory at address 0x400fffffffffffec>, 
    argv=<error reading variable: Cannot access memory at address 0x400fffffffffffe0>) at asm_test.cpp:28
28      b = my_func2(&a);

我做错了什么?在 my_func2 的最后一行,我已经指定内存也被破坏了,我不明白...... 我在哪里可以找到一个很好的指南如何使用臭名昭着的AT&amp; T语法?
我在Ubuntu上编译:{{1​​}}, g ++ 版本 g ++(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3 Linux scv 3.2.0 -48-generic#74-Ubuntu SMP Thu Jun 6 19:43:26 UTC 2013 x86_64 x86_64 x86_64 GNU / Linux

我找到http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.htmlhttp://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html,你会推荐更多吗?

谢谢,
EMA

2 个答案:

答案 0 :(得分:2)

这里的错误是使用movupd时必须小心。使用此指令,实际上可以在输出中复制128位内存。

第一个函数偶然可以复制这些值,但第二个函数在ret变量中只有 64位空间。正如预期的那样,这会破坏堆栈,产生不确定的行为? 将movupd替换为movlpd(或movhpd),可以使用魅力

我还在破坏正确的寄存器吗?

使用g++ -O3 -o asm_test asm_test.cpp

编译时,以下代码就好了
  void my_func(const double *in, double *out) {
    asm ("mov %0, %%r8" : : "r"(in));
    asm ("movhpd (%%r8), %%xmm0" :);
    asm ("movhpd (%%r8), %%xmm1" :);
    asm ("addpd %%xmm1, %%xmm0" :);
    asm ("movhpd %%xmm0, (%0)" : : "r"(out) : "memory", "%r8", "%xmm0", "%xmm1");
  }

  double my_func2(const double *in) {
    double  ret;

    asm("mov %0, %%r8" : : "r"(in));
    asm("movlpd (%%r8), %%xmm0" :);
    asm("movlpd (%%r8), %%xmm1" :);
    asm("addpd %%xmm1, %%xmm0" :);
    asm("movlpd %%xmm0, %0" : "=m"(ret) : : "memory", "%r8", "%xmm0", "%xmm1");

    return ret;
  }

答案 1 :(得分:0)

如果您有单独 的<{em> 行,那么gcc内联汇编并不特别喜欢它。您可以更好地编写以上内容,例如:

asm()

因为这可以让编译器选择放置内容的地方(mem或reg)。对于您的来源,这会将以下两个块内联到#include <xmmintrin.h> // for __m128d static void my_func(const double *in, double *out) { asm("movupd %1, %%xmm0\n" "movupd %1, %%xmm1\n" "addpd %%xmm1, %%xmm0\n" "movupd %%xmm0, %0" : "=rm"(*(__m128d*)out) : "rm"(*(__m128d*)in) : "%xmm0", "%xmm1"); } static double my_func2(const double *in) { double ret; asm("movupd %1, %%xmm0\n" "movupd %1, %%xmm1\n" "addpd %%xmm1, %%xmm0\n" "movlpd %%xmm0, %0" : "=xm"(ret) : "rm"(*(__m128d*)in) : "%xmm0", "%xmm1"); return ret; }

  1c:   66 0f 10 44 24 10       movupd 0x10(%rsp),%xmm0
  22:   66 0f 10 4c 24 10       movupd 0x10(%rsp),%xmm1
  28:   66 0f 58 c1             addpd  %xmm1,%xmm0
  2c:   66 0f 11 44 24 20       movupd %xmm0,0x20(%rsp)
[ ... ]
  63:   66 0f 10 44 24 10       movupd 0x10(%rsp),%xmm0
  69:   66 0f 10 4c 24 10       movupd 0x10(%rsp),%xmm1
  6f:   66 0f 58 c1             addpd  %xmm1,%xmm0
  73:   66 0f 13 44 24 08       movlpd %xmm0,0x8(%rsp)

这不是最佳的,但是......如果你把它改为:

main()

将它留给编译器放置变量的位置。编译器检测到它可以完全没有进行加载/存储......因为它被简单地内联为:

  18:   66 0f 28 c1             movapd %xmm1,%xmm0
  1c:   66 0f 58 c1             addpd  %xmm1,%xmm0
因为编译器认识到它在寄存器中得到所有变量/希望寄存器中的所有返回。

虽然使用装配完全不需要它;使用一个不错的编译器(你的gcc会做)普通的C / C ++版本,

static  void my_func(const double *in, double *out) {
    asm volatile("movapd %1, %0\n"
                 "addpd %1, %0"
                 : "=xm"((__m128d*)out)
                 : "x"(*(__m128d*)in));
}

最有可能变成效率不高的代码。