这是 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 个参数的函数调用:一个整数 in%eax
和两个 XMM 参数 in %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
与进入和退出该区域的开销相比,一个额外的函数调用的开销可以忽略不计。您可以在内部使用 OpenMP pragma func
- 由于该parallel
区域的动态范围,它们将起作用。
考虑到他们承诺用 Clang 替换 GCC,Apple 可能会在未来提供一个固定的编译器,但他们可能不会。