2016-06-22 65 views
6

Dato il seguente codiceGCC 5.1 svolgimento del ciclo

#include <stdio.h> 

int main(int argc, char **argv) 
{ 
    int k = 0; 
    for(k = 0; k < 20; ++k) 
    { 
    printf("%d\n", k) ; 
    } 
} 

Uso GCC 5.1 o successiva con

-x c -std=c99 -O3 -funroll-all-loops --param max-completely-peeled-insns=1000 --param max-completely-peel-times=10000 

non parzialmente svolgimento del ciclo, si srotola il loop dieci volte e poi fa un salto condizionato.

.LC0: 
     .string "%d\n" 
main: 
     pushq %rbx 
     xorl %ebx, %ebx 
.L2: 
     movl %ebx, %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 1(%rbx), %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 2(%rbx), %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 3(%rbx), %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 4(%rbx), %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 5(%rbx), %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 6(%rbx), %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 7(%rbx), %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 8(%rbx), %esi 
     movl $.LC0, %edi 
     xorl %eax, %eax 
     call printf 
     leal 9(%rbx), %esi 
     xorl %eax, %eax 
     movl $.LC0, %edi 
     addl $10, %ebx 
     call printf 
     cmpl $20, %ebx 
     jne  .L2 
     xorl %eax, %eax 
     popq %rbx 
     ret 

ma utilizzando le vecchie versioni di GCC 4.9.2, come crea il desiderato assemlby

.LC0: 
    .string "%d\n" 
main: 
    subq $8, %rsp 
    xorl %edx, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $1, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $2, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $3, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $4, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $5, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $6, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $7, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $8, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $9, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $10, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $11, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $12, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $13, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $14, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $15, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $16, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $17, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $18, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    movl $19, %edx 
    movl $.LC0, %esi 
    movl $1, %edi 
    xorl %eax, %eax 
    call __printf_chk 
    xorl %eax, %eax 
    addq $8, %rsp 
    ret 

E c'è un modo per forzare le versioni successive di GCC per produrre lo stesso risultato?

Utilizzando https://godbolt.org/g/D1AR6i per produrre l'assemblaggio

EDIT: Nessuna domanda duplicato, dal momento che il problema di srotolare completamente loop con le versioni successive di GCC non è stato ancora risolto. Passando --param max-completely-peeled-insns=1000 --param max-completely-peel-times=10000 ha non effetti sulla generato assembly utilizzando GCC> = 5.1

+1

La cosa interessante è che se si modifica la condizione for, ad esempio, 'k <9' lo srotolamento non viene eseguito affatto ... – LPs

+0

@LPS eccetto per iterazione molto piccola come 2 o 3 – Garf365

+0

@LPs utilizzando Lo svincolo di GCC 4.9.2 funziona anche per meno di 9 iterazioni https://godbolt.org/g/ZPlCP6 – surrz

risposta

4

Le bandiere ed i parametri in uso non garantisce che i loop saranno completamente srotolati. I GCC documentation afferma quanto segue per quanto riguarda la bandiera -funroll-all-loops che si sta utilizzando:

accende ciclo completo peeling (cioè rimozione completa del loop con una piccolo numero costante di iterazioni)

Se il compilatore decide che il numero di iterazioni per un dato pezzo di codice non è "una piccola costante" (cioè il numero è troppo alto), può solo eseguire un peeling o uno srotolamento parziale come è stato fatto qui. Inoltre, le opzioni param utilizzate sono solo valori massimi , ma non forzare lo srotolamento completo per cicli più piccoli del valore impostato. In altre parole, se un ciclo ha più iterazioni del massimo che hai impostato, allora il ciclo non sarà completamente srotolato; ma l'inverso non è vero.

Molti fattori vengono presi in considerazione durante le ottimizzazioni. Qui il collo di bottiglia nel codice è la chiamata alla funzione printf e il compilatore probabilmente ne terrà conto durante i calcoli dei costi o giudicherà che il sovraccarico delle dimensioni dell'istruzione per lo srotolamento è troppo importante. Come stai comunque dicendo di srotolare i loop, sembra che la soluzione migliore sia trasformare il ciclo iniziale con 10 arresti e un salto.

Se si sostituisce printf con qualcos'altro, il compilatore può ottimizzare in modo diverso. Per esempio provare a sostituire con il seguente:

volatile int temp = k; 

Il ciclo con questo nuovo frammento di codice sarà completamente srotolato sulle versioni più recenti di GCC (e quelli più anziani pure). Si noti che la parola chiave volatile è solo un trucco usato in modo che il compilatore non ottimizzi completamente il ciclo.

Per riassumere, per quanto ne so, non c'è modo di forzare le versioni successive di GCC a produrre lo stesso output.


Come nota a margine, dal livello di ottimizzazione -O2 in poi e senza flag di compilazione aggiuntivi, le versioni recenti di Clang srotolano completamente il ciclo.

Problemi correlati