2016-04-15 44 views
7

Data la seguente funzione C:Perché GCC su x86-64 inserisce un NOP all'interno di una funzione?

void go(char *data) { 
    char name[64]; 
    strcpy(name, data); 
} 

GCC 5 e 6 su x86-64 compilazione (plain gcc -c -g -o seguita da objdump) a questo:

0000000000000000 <go>: 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: 48 83 ec 50    sub $0x50,%rsp 
    8: 48 89 7d b8    mov %rdi,-0x48(%rbp) 
    c: 48 8b 55 b8    mov -0x48(%rbp),%rdx 
    10: 48 8d 45 c0    lea -0x40(%rbp),%rax 
    14: 48 89 d6    mov %rdx,%rsi 
    17: 48 89 c7    mov %rax,%rdi 
    1a: e8 00 00 00 00   callq 1f <go+0x1f> 
    1f: 90      nop 
    20: c9      leaveq 
    21: c3      retq 

v'è alcuna ragione per GCC inserire la 90/nop a 1f o è solo un effetto collaterale che potrebbe verificarsi quando non vengono attivate le ottimizzazioni?

Nota: questa domanda è diversa dalla maggior parte degli altri perché richiede nop all'interno di un corpo di funzione, non un riempimento esterno.

versioni Compiler testati: GCC Debian 5.3.1-14 (5.3.1) e Debian 6-20160313-1 (6.0.0)

+1

I NOP ​​vengono spesso utilizzati per motivi di temporizzazione. Normalmente non è nulla di cui preoccuparsi. –

+0

Perché dovresti preoccuparti di * timing * in quanto sopra? –

+0

Potrebbe avere a che fare con il pipelining e gli stati di attesa? –

risposta

9

Che strano, non avevo mai notato randagi nop s nell'output ASM a -O0 prima. (Probabilmente perché non sprecare il mio tempo a guardare l'output del compilatore non ottimizzato).

Solitamente le funzioni interne di nop s sono per allineare i target di diramazione, inclusi i punti di ingresso delle funzioni come in the question Brian linked. (Vedere anche -falign-loopsin the gcc docs, che è attivo per impostazione predefinita a livelli di ottimizzazione diversi da -Os).


In questo caso, il nop fa parte del rumore compilatore per una funzione vuota nuda:

void go(void) { 
    //char name[64]; 
    //strcpy(name, data); 
} 
    push rbp 
    mov  rbp, rsp 
    nop      # only present for gcc5, not gcc 4.9.3 
    pop  rbp 
    ret 

See that code in the Godbolt Compiler Explorer modo da poter controllare l'ASM per altre versioni del compilatore e compilare le opzioni.

(non tecnicamente rumore, ma -O0 consente -fno-omit-frame-pointer, ea -O0 funzioni anche vuote istituito e abbattere un frame dello stack.)


Naturalmente, che nop non è presente in qualsiasi non- livello di ottimizzazione zero. Non c'è alcun vantaggio di debug o prestazioni per quello nop nel codice nella domanda. (vedere i collegamenti di guida prestazioni nel tag wiki, esp. Agner Fog's microarchitecture guide per conoscere ciò che rende il codice veloce sulle CPU attuali.)

La mia ipotesi è che è puramente un artefatto di interni gcc. Questo nop è presente come nop nell'output gcc -S asm, non come una direttiva .p2align. gcc non conta i byte del codice macchina, usa solo le direttive di allineamento in alcuni punti per allineare gli obiettivi importanti del ramo. Solo l'assemblatore sa quanto è veramente necessario un nop per raggiungere l'allineamento dato.

Il valore predefinito -O0 indica a gcc che si desidera che compili velocemente e non codice buono.Ciò significa che l'output asm ti dice di più su gcc internals rispetto ad altri livelli -O e molto poco su come ottimizzare o qualcos'altro.

Se stai cercando di imparare ASM, è più interessante guardare il codice a -Og, ad esempio (ottimizza per il debug).

Se stai cercando di vedere quanto gcc o clang fai al codice, devi guardare -O3 -march=native (o -O2 -mtune=intel o qualsiasi altra configurazione con cui crei il tuo progetto). Sconcertare le ottimizzazioni fatte a -O3 è un buon modo per imparare alcuni trucchi asm, però. -fno-tree-vectorize è utile se si desidera vedere una versione non vettoriale di qualcosa di completamente ottimizzato diverso da quello.

+0

Buona risposta. Molto interessante –