8

我偶然发现了以下问题。下面的代码片段在 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));
  }
}

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

/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。现在,我四处搜索并找到了与 相关的第一个问题的解决方案memcpy,即添加-fno-builtin-D_FORTIFY_SOURCE=0gcc参数列表。我没有设法解决第二个问题(sse 内在)。

谁能帮我解决这个问题?问题:

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

1 回答 1

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);
}

这是编译器在所有转换后如何在内部查看代码的类似 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 可能会在未来提供一个固定的编译器,但他们可能不会。

于 2012-10-18T18:32:32.177 回答