2012-10-17 10 views
8

Mi sono imbattuto nel seguente problema. Il frammento di codice qui sotto non collega su Mac OS X con qualsiasi Xcode ho cercato (4,4, 4,5)La compilazione non riesce con OpenMP su Mac OS X Lion (memcpy e SSE intrinseche)

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

Il codice è solo fornito come un esempio e potrebbe segfault quando lo si esegue. Il punto è che non si compila. La compilazione è fatto utilizzando la seguente riga

/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 

Il codice compila bene quando non utilizzando il flag -fopenmp a gcc. Ora, ho cercato su Google e trovato una soluzione per il primo problema relativo a memcpy, che sta aggiungendo -fno-builtin o -D_FORTIFY_SOURCE=0 all'elenco di argomenti gcc. Non sono riuscito a risolvere il secondo problema (sse intrinseco).

Qualcuno può aiutarmi a risolvere questo? Le domande:

  • cosa più importante: come sbarazzarsi dell'errore "___builtin_ia32_shufpd"?
  • qual è esattamente il motivo del problema memcpy e cosa fa infine il flag -D_FORTIFY_SOURCE=0?
+0

compila per me (OSX10.8.2, Xcode 4.5, macports gcc 4.7.1) quando si utilizza -fopenmp -O1 (o superiore, ma non -O0: questo dà errore del linker con mancante ___ gxx_personality_v0'). Tuttavia il codice produce segfault quando viene eseguito. Quando si compila senza -fopenmp, il codice compila per qualsiasi -O, ma di nuovo segfaults (tranne per -O0: errore del bus). – Walter

+0

@Walter Grazie. segfault non è un problema, il codice è solo un esempio - ovviamente è sbagliato. Stai utilizzando gcc 4.7.1, quindi non i compilatori Xcode, giusto? Potresti compilare con la linea di comando che ho dato? Il cambiamento del livello di ottimizzazione non è stato d'aiuto qui. – angainor

+1

Questo è un bug nel compilatore 'llvm-gcc' con cui Xcode viene fornito. È un compilatore LLVM con frontend GCC. La fase OpenMP sta generando alcuni built-in che il backend non è in grado di riconoscere. Poiché Xcode si sta costantemente spostando verso la sostituzione completa di GCC con 'clang', il bug probabilmente non verrà mai corretto. Basta installare il vero GCC dal sorgente o tramite un altro metodo e utilizzarlo per compilare i codici OpenMP. –

risposta

15

Si tratta di un bug nel modo in cui Apple LLVM-GCC sostenuta (llvm-gcc) trasforma regioni OpenMP e gestisce le chiamate ai built-in al loro interno. Il problema può essere diagnosticato esaminando i dump dell'albero intermedio (ottenibile passando l'argomento -fdump-tree-all su gcc). Senza OpenMP attivata la seguente rappresentazione codice finale viene generata (dalla 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); 
} 

Questa è una rappresentazione C-like di come il compilatore vede il codice internamente dopo tutte le trasformazioni. Questo è ciò che viene poi trasformato in istruzioni di assemblaggio. (solo le righe che fanno riferimento alle built-in sono mostrati qui)

Con OpenMP abilitata la regione parallelo viene estratta in propria funzione, 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); 
} 

nuovo mi hanno lasciato solo il codice che si riferisce ai builtins. Ciò che è evidente (ma la ragione per cui non mi è immediatamente chiaro) è che il trasnformer del codice OpenMP in realtà insiste su sulla chiamata di tutti i built-in tramite i puntatori di funzione. Questi asignments puntatore:

__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; 

generano riferimenti esterni ai simboli che non sono in realtà simboli, ma piuttosto i nomi che ricevono un trattamento speciale da parte del compilatore. Quindi il linker tenta di risolverli ma non riesce a trovare nessuno dei nomi __builtin_* in uno qualsiasi dei file oggetto a cui è collegato il codice.Questo è anche osservabile nel codice assembly che si può ottenere facendo passare -S a 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) 

questo è fondamentalmente una chiamata di funzione che prende 3 parametri: un numero intero %eax e due argomenti XMM in %xmm0 e %xmm1, con il risultato viene restituito in %xmm0 (come da convenzione di chiamata della funzione ABI SysV AMD64). Al contrario, il codice generato, senza -fopenmp è un'espansione a livello di istruzioni del intrinseco in quanto si suppone accada:

LBB1_3: 
    movapd -64(%rbp), %xmm0 
    shufpd $1, %xmm0, %xmm0 
    movapd %xmm0, -80(%rbp) 

Cosa succede quando si passa -D_FORTIFY_SOURCE=0 è che memcpy non è sostituito dal "fortificata" controllo della versione viene invece utilizzata una normale chiamata allo memcpy. Ciò elimina i riferimenti a object_size e __memcpy_chk ma non è possibile rimuovere la chiamata al ia32_shufpd integrato.

Questo è ovviamente un bug del compilatore. Se davvero davvero davvero necessario utilizzare GCC di Apple per compilare il codice, quindi una soluzione intermedia sarebbe quella di spostare il codice incriminato a una funzione esterna come il bug colpisce apparentemente solo codice che viene estratto da parallel regioni:

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

Il sovraccarico di una chiamata di funzione aggiuntiva è trascurabile rispetto al sovraccarico di ingresso e uscita dalla regione parallel. È possibile utilizzare i prmap di OpenMP all'interno di func: funzioneranno a causa dell'ambito dinamico della regione parallel.

Potrebbe essere che Apple fornirà un compilatore fisso in futuro, potrebbe non farlo, visto il loro impegno a sostituire GCC con Clang.

+0

Grazie per avermi spiegato a fondo che questo non funzionerà;) Ho rinunciato a 'Xcode' e ho installato macports gcc 4.7, come menzionato in precedenza da Walter. Ha funzionato senza problemi.L'unica cosa fastidiosa è che per poter utilizzare i Macport è comunque necessario installare Xcode ** e ** strumenti da riga di comando, e si ottiene un nuovo set di compilatori in cima. solo wow. – angainor