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.
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
@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
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. –