2

ネオンを使ってiPhone4にドット積関数を実装しようとしています。このチュートリアルに基づいて:http : //www.delmarnorth.com/microwave/requirements/neon-test-tutorial.pdfXCode4.5で以下を試しました。nruns = 1に設定すると、機能します。つまり、ネオンから標準のC++バージョンと同じ答えが得られます。ただし、nruns> 1を設定すると、何かが破損してゴミが出てきます(たとえば、dotProduct()を最初に呼び出した後に配列の要素を出力すると、それらが破損します)。私はネオンの経験がないことを告白しなければなりませんが、必要なのはこのドット積を実行できることだけです!誰か考えがありますか?

   float dotProduct ( float *a, float *b, int n) {
        float sum=0.0f;
        __asm__ volatile (
                          "vmov.f32 q8, #0.0               \n\t"
                          "vmov.f32 q9, #0.0               \n\t"
                          "1:                             \n\t"
                          "subs %3, %3, #8                \n\t"
                          "vld1.f32 {d0,d1,d2,d3}, [%1]!      \n\t"
                          "vld1.f32 {d4,d5,d6,d7}, [%2]!      \n\t"
                          "vmla.f32 q8, q0, q2              \n\t"
                          "vmla.f32 q9, q1, q3              \n\t"
                          "bgt 1b                         \n\t"
                          "vadd.f32 q8, q8, q9              \n\t"
                          "vpadd.f32 d0, d16, d17           \n\t"
                          "vadd.f32 %0, s0, s1              \n\t"
                          : "=w"(sum)
                          : "+r"(a), "+r"(b), "+r"(n)
                          : "q0", "q1", "q2", "q3", "q8", "q9");
        return sum;
    }



    void test_dotProduct_neon()
    {
        int n=16, i, k;
        int nruns = 2;
        float dp;
        float *a = new float[n];
        float *b = new float[n];
        for (i=0; i < n; i++) {
           a[i] = (float) i; 
           b[i] = (float) (2*i);
        }
        for (i=0; i<nruns; i++) {
           dp=0.0f;
           for( k=0; k < n; k++) {
               dp += a[k] * b[k];
            }
        }
        printf(" C Result:   %f\n", dp );
        for (i=0; i<nruns; i++) {
            dp = dotProduct( a, b, n);
        }
        printf(" Neon Result:   %f\n", dp );
    }
4

1 に答える 1

3

NEON コードは、ロード命令 (vld1) がアドレス レジスタをインクリメントするため、ポインター "a" と "b" を変更します (これは、アドレスの後の '!' が行うことです)。

おそらく、コンパイラはこれらの値が変更される可能性があることを認識していないため、コードは偽のポインター値で 2 回目に動作します。

これらのインラインアセンブリ入力を「+」でマークします。これは「入力/出力オペランド」を意味しますが、それを機能させるには、2番目の入力オペランドではなく、出力オペランドセクションにリストする必要があると思います。あなたのコードは読むべきです

: "=w"(sum), "+r"(a), "+r"(b), "+r"(n)
: /* No inputs */
: "q0", "q1", "q2", "q3", "q8", "q9"

また、入力レジスター、出力レジスター、破壊されたレジスターのセットは互いに素ではありません。レジスターを破壊済みとしてマークしても、コンパイラーがそれを入力レジスター AFAIR として使用することを妨げません。ここで、入力は ARM レジスタであり、破壊されたレジスタは NEON レジスタであるため、出力と破壊されたレジスタにも同じことが当てはまらない場合は安全です。http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.htmlを確認することをお勧めします。

編集:あなたのコードは、sumたまたま壊れたレジスタの1つに住んでいても、最後にしか書いていないので、正しいようです。上記の警告を書いたとき、私はそれを見逃していました。

編集:インライン アセンブリは、ポインターab(-Constraint を使用した) メモリ領域としてではなく、m汎用レジスタのプレーンな値として渡します。したがって、コンパイラは、これらのメモリ位置から実際に読み取っていることを認識せず、その結果、アセンブリ ブロックを他のストアを越えてそれらの位置に誤って移動する可能性があります。現在、メモリ領域は可変サイズでmあるため、サイズが静的に決定されると想定しているため、-Constraints を簡単に使用することはできません。"memory"代わりに、アセンブリ ブロックが任意のメモリ位置との間で読み取りと書き込みを行うことをコンパイラに伝える clobber リストに追加できます。そして、あなたがそれをしている間に、「ccこれは、アセンブリ ブロックが条件コード レジスタ (テスト命令の結果を含むレジスタ) を変更するためです。入出力宣言は次のようになります。

: "=w"(sum), "+r"(a), "+r"(b), "+r"(n)
: /* No inputs */
: "q0", "q1", "q2", "q3", "q8", "q9", "cc", "memory"

インライン アセンブリ ブロックを記述するときは、コンパイラがそのようなブロックの内容をまったく見ていないことを常に覚えておいてください。これは、ブロックの動作に関する唯一の情報源であり、データの依存関係は、宣言された入力オペランドと出力オペランド、および宣言されたクロバー リストです。また、コンパイラ、最適化時にその情報を積極的に使用します。特に、以上でコンパイルする場合はそう-O2です。したがって、これらの宣言を省略すると、コンパイラが誤ったコードを生成する可能性があります。

于 2012-09-27T13:01:32.623 に答える