c++ - 在 Mac OS X Lion 上使用 OpenMP 编译失败(memcpy 和 SSE 内在函数)

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

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

/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

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

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

  • 最重要的是:如何消除“___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 * (*<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 个参数的函数调用:%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 的常规调用所取代code> 被改用。这消除了对 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 区域的开销相比,一个额外函数调用的开销可以忽略不计。您可以在 func 中使用 OpenMP 编译指示 - 由于 parallel 区域的动态作用域,它们将起作用。

考虑到他们 promise 用 Clang 替换 GCC,Apple 将来可能会提供一个固定的编译器,也可能不会。

关于c++ - 在 Mac OS X Lion 上使用 OpenMP 编译失败(memcpy 和 SSE 内在函数),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13919995/

相关文章:

c - 声明中使用的 * 运算符 (int *p = &i) 和表达式 (*p = &i) 之间有什么区别

C程序在while循环中不保存到文件

macos - 在 Swift 中列出蓝牙设备范围内的设备

android - 无法从 Mac OS X 中的命令行启动 Android 模拟器

c++ - 根据执行时间清理对象池

c++ - 如何在此OpenMP摘要示例中删除依赖项

Visual Studio 可以用来运行带有 ARM 脚本的 C 代码吗?

macos - 在 Mac OS 上将 Node.js 升级到最新版本

c++ - 在 Makefile 中指定库路径

c++ - Brox 密集光流跟踪器错误 'EFilterIncompatibleSize'