2

gcc/g++のインラインasmディレクティブを使用しようとしています (以前にMSVCで Intel 構文を使用したことがあり、それは簡単でした)。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で実行している場合):

プログラム受信信号 SIGBUS、バスエラー。
0x00000000004008e1 in main (argc=<エラー読み取り変数: アドレス 0x400ffffffffffffec のメモリにアクセスできません>,
    argv=<変数の読み取りエラー: アドレス 0x400fffffffffffe0 のメモリにアクセスできません>) at asm_test.cpp:28
28 b = my_func2(&a);

私は何を間違っていますか?my_func2の最後の行で、メモリも破壊されると指定しましたが、わかりません... 悪名高い AT&T 構文の使用方法に関する適切なガイドはどこにありますか? で
コンパイルします: g++バージョンg++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 on Ubuntu 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/リナックスg++ -g -o asm_test asm_test.cpp

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

実際には独立していない別々の行の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) を選択できるようになるためです。ソースの場合、これにより次の 2 つのブロックが にインライン化されmain()ます。

  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)

これは最適ではありませんが、次のように変更した場合:

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 movapd %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 に答える