2

我正在尝试使用 gcc/g++ 的内联asm指令(我不得不说我之前一直在MSVC上使用 Intel 语法,这很容易)。我正在玩值,以下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运行时):

程序收到信号 SIGBUS,总线错误。
0x00000000004008e1 in main (argc=<error reading variable: Cannot access memory at address 0x400ffffffffffffec>,
    argv=<错误读取变量:无法访问地址 0x400ffffffffffffe0>) at asm_test.cpp:28
28 b = my_func2(&a);

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

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

谢谢,
艾玛

4

2 回答 2

1

这里的错误是使用时必须小心movupd。使用此指令,您实际上复制了 128 位内存,进出

碰巧第一个函数也可以复制这些值,但第二个函数在变量中只有64 位空间。ret正如预期的那样,这会破坏堆栈,导致未定义的行为?用(or )
代替,事情就会变得有魅力movupdmovlpdmovhpd

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

以下代码在编译时工作得很好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;
  }
于 2013-07-08T05:53:43.740 回答
1

如果您有单独asm()的语句行实际上不是独立的,则 gcc 内联汇编不是特别喜欢它。你最好把上面的代码写成:

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

因为这让编译器可以选择放置东西的位置(mem 或 reg)。对于您的来源,这会将以下两个块内联到main()

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

这是 _not 最佳的,但是...如果您将其更改为:

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

你把它留给编译器把变量放在哪里。编译器检测到它可以完全不进行加载/存储......因为这被简单地内联为:

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

尽管完全没有必要使用汇编来做到这一点;使用一个体面的编译器(你的 gcc 会做)普通的 C/C++ 版本,

static void my_func(const double *in, double *out) {
    out[0] = in[0] + in[0];
    out[1] = in[1] + in[1];
}

很可能会变成效率不低的代码。

于 2013-07-08T20:10:56.480 回答