Mac OS X Lion上的OpenMP编译失败(memcpy和SSE内在函数)

我偶然发现了以下问题。 下面的代码片段在Mac OS X上没有与我试过的任何Xcode链接(4.4,4.5)

#include  #include  #include  int main(int argc, char *argv[]) { char *temp; #pragma omp parallel { __m128d v_a, v_ar; memcpy(temp, argv[0], 10); v_ar = _mm_shuffle_pd(v_a, v_a, _MM_SHUFFLE2 (0,1)); } } 

代码仅作为示例提供,并且在运行时会出现段错误。 关键是它不能编译。 使用以下行完成编译

 /Applications/Xcode.app/Contents/Developer/usr/bin/gcc test.c -arch x86_64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk -mmacosx-version-min=10.7 -fopenmp Undefined symbols for architecture x86_64: "___builtin_ia32_shufpd", referenced from: _main.omp_fn.0 in ccJM7RAw.o "___builtin_object_size", referenced from: _main.omp_fn.0 in ccJM7RAw.o ld: symbol(s) not found for architecture x86_64 collect2: ld returned 1 exit status 

使用-fopenmp标志到gcc时,代码编译得很好。 现在,我用Google搜索并找到了与memcpy的第一个问题的解决方案,即将-fno-builtin-D_FORTIFY_SOURCE=0gcc参数列表中。 我没有设法解决第二个问题(sse内在)。

任何人都可以帮我解决这个问题吗? 问题:

  • 最重要的是:如何摆脱“___builtin_ia32_shufpd”错误?
  • 什么是memcpy问题的原因究竟是什么, -D_FORTIFY_SOURCE=0标志最终会做什么?

这是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 * (*) (void *, const  *, long unsigned int, long unsigned int) __builtin___memcpy_chk.21; long unsigned int (*) (const  *, int) __builtin_object_size.20; vector double (*) (vector double, vector double, int) __builtin_ia32_shufpd.23; long unsigned int (*) (const  *, 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代码trasnformer真的坚持通过函数指针调用所有内置函数。 这些指针指向:

 __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个参数的函数调用: %eax一个整数和%xmm0%xmm1两个XMM参数,结果在%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来编译代码,那么临时解决方案是将有问题的代码移动到外部函数,因为bug显然只影响从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区域的开销相比,一个额外函数调用的开销是可忽略的。 您可以在func使用OpenMP pragma – 它们将起作用,因为parallel区域的动态范围。

可能是Apple将来会提供一个固定的编译器,可能他们不会,因为他们承诺用Clang取代GCC。