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

Inline gcc assembly and local variables (double)

本文关键字:局部变量 gcc 程序集 内联      更新时间:2023-10-16

我正在尝试使用gcc/g++的内联asm指令(我不得不说,我以前在MSVC上使用过英特尔语法,这很简单)。我在玩值,下面的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在main中(argc=<读取变量时出错:无法访问地址0x400fffffffffec>处的内存;,argv=<读取变量时出错:无法访问地址0x400fffffffffe0>处的内存)在asm_test.cpp:2828 b=my_func2(/a);

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

我发现http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html和http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html,有什么更多你会推荐吗?

谢谢,
Ema

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

碰巧第一个函数也可以复制这些值,但第二个函数在ret变量中只有的64位空间。正如预期的那样,这会腐蚀堆栈,导致未定义的行为
movlpd(或movhpd)代替movupd,事物就会发挥魅力。

我还在敲打正确的寄存器吗?

以下代码在使用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;
  }

gcc内联程序集并不特别喜欢如果有单独的asm()语句,而这些实际上并不独立。你最好把上面的代码写成:

#include <xmmintrin.h> // for __m128d
static  void my_func(const double *in, double *out) {
    asm("movupd %1, %%xmm0n"
        "movupd %1, %%xmm1n"
        "addpd %%xmm1, %%xmm0n"
        "movupd %%xmm0, %0"
        : "=rm"(*(__m128d*)out)
        : "rm"(*(__m128d*)in)
        : "%xmm0", "%xmm1");
}
static double my_func2(const double *in) {
    double ret;
    asm("movupd %1, %%xmm0n"
        "movupd %1, %%xmm1n"
        "addpd %%xmm1, %%xmm0n"
        "movlpd %%xmm0, %0"
        : "=xm"(ret)
        : "rm"(*(__m128d*)in)
        : "%xmm0", "%xmm1");
    return ret;
}

因为这让编译器可以选择把东西放在哪里(mem或reg)。对于您的源代码,这将以下两个块内联到main():中

1c:66 0f 10 44 24 10 movupd 0x10(%rsp),%xmm022:66 0 f 10 4c 24 10 movupd 0x10(%rsp),%xmm128:66 0f 58 c1 addpd%xmm1,%xmm02c:66 0f 11 44 24 20 movupd%xmm0,0x20(%rsp)[…]63:66 0 f 10 44 24 10 movupd 0x10(%rsp),%xmm069:66 0 f 10 4c 24 10 movupd 0x10(%rsp),%xmm16f:66 0f 58 c1 addpd%xmm1,%xmm073:66 0f 13 44 24 08 movlpd%xmm0,0x8(%rsp)

不过,这不是最佳的(_N)。。。如果您将其更改为:

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

把变量放在哪里就交给编译器了。编译器检测到它可以完全不进行加载/存储。。。因为它被内联为:

18:66 0f 28 c1 movpd%xmm1,%xmm01c: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];
}

很可能会变成同样高效的代码。