2012-10-17 76 views
8

我偶然發現了以下問題。下面的代碼片段無法在Mac OS X與在Mac OS X Lion上使用OpenMP編譯失敗(memcpy和SSE內部函數)

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

的代碼只是作爲示例提供的,當你運行它會出現段錯誤任何的Xcode我試過(4.4,4.5)鏈接。關鍵是它不能編譯。編譯使用下面一行時,使用-fopenmp標誌gcc

/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 

代碼編譯就好做了。現在,我搜索了一下,並找到了與memcpy相關的第一個問題的解決方案,它將​​或-D_FORTIFY_SOURCE=0添加到gcc參數列表中。我沒有設法解決第二個問題(sse intrinsic)。

任何人都可以幫助我解決這個問題嗎?問題:

  • 最重要的是:如何擺脫 「___builtin_ia32_shufpd」 的錯誤?
  • 究竟是memcpy問題的原因是什麼,-D_FORTIFY_SOURCE=0標誌最終會做什麼?
+0

當使用-fopenmp -O1(或更高,但不是-O0:這會導致鏈接器錯誤,缺少___ gxx_personality_v0)時,適合我編譯(OSX10.8.2,Xcode 4.5,macports gcc 4.7.1)。但是,代碼在運行時會產生段錯誤。在沒有-fopenmp的情況下編譯時,代碼針對任何-O進行編譯,但是又是段錯誤(除了-O0:總線錯誤)。 – Walter

+0

@Walter謝謝。 segfault不是問題,代碼只是一個例子 - 當然這是錯誤的。你使用的是gcc 4.7.1,所以不是Xcode編譯器,對不對?你能用我給的命令行進行編譯嗎?更改優化級別對此沒有幫助。 – angainor

+1

這是Xcode附帶的'llvm-gcc'編譯器中的一個錯誤。它是一個帶有GCC前端的LLVM編譯器。 OpenMP階段正在生成一些後端無法識別的內置內存。由於Xcode正在穩步地用'clang'完全替代GCC,這個bug可能永遠不會被修復。只需從源代碼或其他方法安裝真正的GCC,並使用它來編譯OpenMP代碼。 –

回答

15

這是蘋果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代碼trasnformer真的堅持通過函數指針調用所有內置函數。這些指針asignments:

__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_*名稱。這也是在彙編代碼,人們可以通過使-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) 

這基本上是一個函數調用需要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=0memcpy不會被「強化」檢查的版本替換髮生並且使用memcpy的定期呼叫代替。這消除了對object_size__memcpy_chk的引用,但不能刪除對內置的ia32_shufpd的調用。

這顯然是一個編譯器錯誤。如果你真的真的真的必須使用蘋果的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 pragmas - 由於parallel區域的動態範圍限定,它們可以工作。

可能蘋果將來會提供一個固定的編譯器,他們可能不會,因爲他們承諾用Clang代替GCC。

+0

謝謝你徹底向我解釋這是行不通的;)我放棄了'Xcode'並安裝了macport gcc 4.7,正如沃爾特先前提到的。它工作沒有問題。唯一令人討厭的是,爲了使用macports,你無論如何都需要安裝Xcode **和**命令行工具,並且你會得到一組新的編譯器。只是哇。 – angainor