Сбой компиляции с OpenMP в Mac OS X Lion (встроенные функции memcpy и SSE)

Я наткнулся на следующую проблему. Приведенный ниже фрагмент кода не связывает 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=0 в gcc список аргументов. Мне не удалось решить вторую проблему (sse intrinsic).

Может кто-нибудь помочь мне решить это? Вопросы:

  • самое главное: как избавиться от ошибки «___builtin_ia32_shufpd»?
  • что именно является причиной memcpy проблема, а что -D_FORTIFY_SOURCE=0 флаг в итоге делать?

8

Решение

Это ошибка в способе GCC, поддерживаемом Apple LLVM (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 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 и два аргумента XMM в %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 встроенный.

Это, очевидно, ошибка компилятора. Если вы действительно действительно должны использовать GCC от Apple для компиляции кода, то временное решение будет заключаться в том, чтобы переместить код, вызывающий проблемы, во внешнюю функцию, поскольку ошибка, по-видимому, влияет только на код, извлекаемый из 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 прагмы внутри func — они будут работать из-за динамического определения parallel область, край.

Возможно, в будущем Apple предоставит фиксированный компилятор, а может и нет, учитывая их приверженность замене GCC на Clang.

15

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]