これは、Apple の LLVM ベースの GCC ( llvm-gcc
) が OpenMP 領域を変換し、その内部のビルトインへの呼び出しを処理する方法のバグです。この問題は、中間ツリー ダンプを調べることで診断できます (-fdump-tree-all
引数を に渡すことで取得できますgcc
)。OpenMP を有効にしないと、次の最終的なコード表現が生成されます ( からtest.c.016t.fap
)。
main (argc, argv)
{
D.6544 = __builtin_object_size (temp, 0);
D.6545 = __builtin_object_size (temp, 0);
D.6547 = __builtin___memcpy_chk (temp, D.6546, 10, D.6545);
D.6550 = __builtin_ia32_shufpd (v_a, v_a, 1);
}
これは、すべての変換後にコンパイラがコードを内部的にどのように認識するかを C に似た表現で表したものです。これは、アセンブリ命令に変換されるものです。(ビルトインを参照する行のみがここに表示されます)
OpenMP を有効にすると、並列領域が独自の関数に抽出されますmain.omp_fn.0
。
main.omp_fn.0 (.omp_data_i)
{
void * (*<T4f6>) (void *, const <unnamed type> *, long unsigned int, long unsigned int) __builtin___memcpy_chk.21;
long unsigned int (*<T4f5>) (const <unnamed type> *, int) __builtin_object_size.20;
vector double (*<T6b5>) (vector double, vector double, int) __builtin_ia32_shufpd.23;
long unsigned int (*<T4f5>) (const <unnamed type> *, int) __builtin_object_size.19;
__builtin_object_size.19 = __builtin_object_size;
D.6587 = __builtin_object_size.19 (D.6603, 0);
__builtin_ia32_shufpd.23 = __builtin_ia32_shufpd;
D.6593 = __builtin_ia32_shufpd.23 (v_a, v_a, 1);
__builtin_object_size.20 = __builtin_object_size;
D.6588 = __builtin_object_size.20 (D.6605, 0);
__builtin___memcpy_chk.21 = __builtin___memcpy_chk;
D.6590 = __builtin___memcpy_chk.21 (D.6609, D.6589, 10, D.6588);
}
ここでも、ビルトインを参照するコードのみを残しました。明らかなこと (ただし、その理由はすぐにはわかりません) は、OpenMP コード トランスフォーマーが、関数ポインターを介してすべての組み込み関数を呼び出すことを実際に主張していることです。これらのポインター割り当て:
__builtin_object_size.19 = __builtin_object_size;
__builtin_ia32_shufpd.23 = __builtin_ia32_shufpd;
__builtin_object_size.20 = __builtin_object_size;
__builtin___memcpy_chk.21 = __builtin___memcpy_chk;
実際にはシンボルではなく、コンパイラによって特別に扱われる名前であるシンボルへの外部参照を生成します。次に、リンカーはそれらを解決しようとし__builtin_*
ますが、コードがリンクされているオブジェクト ファイルのいずれかで名前を見つけることができません。-S
これは、に渡すことで取得できるアセンブリ コードでも確認できますgcc
。
LBB2_1:
movapd -48(%rbp), %xmm0
movl $1, %eax
movaps %xmm0, -80(%rbp)
movaps -80(%rbp), %xmm1
movl %eax, %edi
callq ___builtin_ia32_shufpd
movapd %xmm0, -32(%rbp)
これは基本的に、3 つの引数を取る関数呼び出しです。1 つの整数が に%eax
、2 つの XMM 引数が%xmm0
および%xmm1
にあり、結果が返され%xmm0
ます (SysV AMD64 ABI 関数呼び出し規約に従って)。対照的に、それなしで生成されたコード-fopenmp
は、それが起こるはずの組み込みの命令レベルの展開です。
LBB1_3:
movapd -64(%rbp), %xmm0
shufpd $1, %xmm0, %xmm0
movapd %xmm0, -80(%rbp)
パスする-D_FORTIFY_SOURCE=0
とmemcpy
、「強化された」チェック バージョンに置き換えられず、通常の呼び出しmemcpy
が代わりに使用されます。これにより、への参照が削除さobject_size
れますが、組み込み__memcpy_chk
への呼び出しを削除することはできません。ia32_shufpd
これは明らかにコンパイラのバグです。コードをコンパイルするために本当に Apple の GCC を使用する必要がある場合、一時的な解決策は、問題のあるコードを外部関数に移動することです。これは、バグがparallel
領域から抽出されたコードにのみ影響を与えるように見えるためです。
void func(char *temp, char *argv0)
{
__m128d v_a, v_ar;
memcpy(temp, argv0, 10);
v_ar = _mm_shuffle_pd(v_a, v_a, _MM_SHUFFLE2 (0,1));
}
int main(int argc, char *argv[])
{
char *temp;
#pragma omp parallel
{
func(temp, argv[0]);
}
}
parallel
1 つの追加関数呼び出しのオーバーヘッドは、領域に出入りするオーバーヘッドと比較して無視できます。内部で OpenMP プラグマを使用できます。領域func
の動的スコープにより機能しますparallel
。
GCCをClangに置き換えるという彼らのコミットメントを考えると、Appleは将来的に修正されたコンパイラを提供するかもしれません.