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

ang*_*nor 8 c c++ macos openmp fortify-source

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

#include <stdlib.h>
#include <string.h>
#include <emmintrin.h>

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));
  }
}
Run Code Online (Sandbox Code Playgroud)

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

/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
Run Code Online (Sandbox Code Playgroud)

使用-fopenmp标志时,代码编译得很好gcc.现在,我用Google搜索了一圈,发现,与相连的第一个问题的解决方案memcpy,这是增加-fno-builtin,还是-D_FORTIFY_SOURCE=0gcc参数列表.我没有设法解决第二个问题(sse内在).

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

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

Hri*_*iev 15

这是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);
}
Run Code Online (Sandbox Code Playgroud)

这是一个类似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);
}
Run Code Online (Sandbox Code Playgroud)

我再次只留下了引用内置函数的代码.显而易见的是(但我之所以没有立即明白的原因)是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;
Run Code Online (Sandbox Code Playgroud)

生成对符号的外部引用,这些符号不是真正的符号,而是由编译器进行特殊处理的名称.然后,链接器尝试解析它们,但无法__builtin_*在代码链接的任何目标文件中找到任何名称.这也可以在汇编代码中观察到,可以通过传递-Sgcc:

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)
Run Code Online (Sandbox Code Playgroud)

这基本上是一个函数调用,它接受3个参数:一个整数%eax和两个XMM参数,%xmm0%xmm1返回结果%xmm0(根据SysV AMD64 ABI函数调用约定).相比之下,生成的代码-fopenmp是内在的指令级扩展,因为它应该发生:

LBB1_3:
    movapd  -64(%rbp), %xmm0
    shufpd  $1, %xmm0, %xmm0
    movapd  %xmm0, -80(%rbp)
Run Code Online (Sandbox Code Playgroud)

传递时会发生的情况-D_FORTIFY_SOURCE=0memcpy"强化"检查版本没有替换,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]);
  }
}
Run Code Online (Sandbox Code Playgroud)

与进入和退出该parallel区域的开销相比,一个额外函数调用的开销是可忽略的.您可以在里面使用OpenMP编译指示func- 由于区域的动态范围,它们将起作用parallel.

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