4

LLVM の JIT を使用してコードを動的に生成することにより、現在取り組んでいるプログラムを高速化したいと考えています。アルゴリズムはベクトルを操作できます。LLVM で SIMD ベクトル拡張を使用してこれを実行したいと思います (一部の操作が高速になるだけでなく、実際にコード生成が簡単になります)。

これを適度に移植可能な方法で動作させる可能性はありますか?

C 側では、gcc、clang、またはおそらく icc でコンパイルします。私のベクトルは単純なものになりfloat x 4ますdouble x 4。この世界における非プラットフォーム固有のベクトル操作の事実上の標準は、gcc ベクトル拡張のようです。

typedef double Vector4 __attribute__ ((vector_size (sizeof(double)*4)));

生成されたコードを調べると、clang はdouble x 4ベクトルをレジスターに渡しますが、gcc はそれをスタックに入れたいと考えています --- これは悪いことです。float x 4(どちらもベクトルをレジスターに渡します。)

私の理解では、2 つのシステムは ABI 互換であるはずですが、明らかにベクトルはカウントされません。私は実際にこれを行うことができますか?

私のサンプルプログラムは::

typedef double real;
typedef real Vector4 __attribute__ ((vector_size (sizeof(real)*4)));

Vector4 scale(Vector4 a)
{
    Vector4 s = {2, 2, 2, 2};
    return a*s;
}

これは LLVM でコンパイルすると次のようになります。

scale:
    movapd  .LCPI0_0(%rip), %xmm2
    mulpd   %xmm2, %xmm0
    mulpd   %xmm2, %xmm1
    ret

...しかし、gccはこの恐怖を生み出します:

scale:
    subq    $64, %rsp
    movq    %rdi, %rax
    movsd   .LC0(%rip), %xmm0
    movapd  72(%rsp), %xmm1
    movsd   %xmm0, -56(%rsp)
    movsd   %xmm0, -48(%rsp)
    movsd   %xmm0, -72(%rsp)
    movsd   %xmm0, -64(%rsp)
    mulpd   -56(%rsp), %xmm1
    movapd  88(%rsp), %xmm0
    mulpd   -72(%rsp), %xmm0
    movapd  %xmm1, -104(%rsp)
    movq    -104(%rsp), %rdx
    movapd  %xmm1, -24(%rsp)
    movapd  %xmm0, -8(%rsp)
    movq    %rdx, (%rdi)
    movq    -16(%rsp), %rdx
    movq    %rdx, 8(%rdi)
    movq    -8(%rsp), %rdx
    movq    %rdx, 16(%rdi)
    movq    (%rsp), %rdx
    movq    %rdx, 24(%rdi)
    addq    $64, %rsp
    ret

に再定義realすると、float両方のコンパイラから次のようになります (同じコードが生成されます)。

scale:
    mulps   .LCPI0_0(%rip), %xmm0
    ret

これらはすべて でコンパイルされました$CC -O3 -S -msse test.c

更新:単純な解決策は、LLVM を使用してトランポリンを作成し、構造体からベクトルに、またはその逆に変換することであることに突然気づきました。このようにして、相互運用性の問題は値渡し構造に縮小され、ABI によって突き止められますベクトルは LLVM ランドにのみ存在します。これは、LLVM 内で SIMD を使用することしかできないことを意味しますが、それを受け入れることができます。

ただし、上記の答えを知りたいです。ベクトルは素晴らしいので、もっと活用したいと思っています。

更新の更新: C が構造体を値で渡す方法は非常識であることがわかりました...えっと、非常識です! Astruct { double x, y, z; }はポインターによって渡されます。aは%xmm レジスタのペアstruct { float x, y, z }として渡されます: 最初のレジスタにパックされ、2 番目のレジスタにパックされます...xyz

シンプルで痛くない!

4

0 に答える 0