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=0
到gcc
参数列表中。 我没有设法解决第二个问题(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。