2016-01-14 11 views
13

Ho un codice C++ che richiede template e che voglio garantire che il compilatore ottimizzi il più possibile a causa della grande quantità di informazioni che ha al momento della compilazione. Per valutare le sue prestazioni, ho deciso di dare un'occhiata allo smontaggio del file oggetto che genera. Di seguito è riportato un frammento di quello che ho ottenuto da objdump -dC:Per che cosa sono queste istruzioni callq apparentemente inutili nei miei file oggetto x86?

0000000000000000 <bar<foo, 0u>::get(bool)>: 
    0: 41 57     push %r15 
    2: 49 89 f7    mov %rsi,%r15 
    5: 41 56     push %r14 
    7: 41 55     push %r13 
    9: 41 54     push %r12 
    b: 55      push %rbp 
    c: 53      push %rbx 
    d: 48 81 ec 68 02 00 00 sub $0x268,%rsp 
    14: 48 89 7c 24 10   mov %rdi,0x10(%rsp) 
    19: 48 89 f7    mov %rsi,%rdi 
    1c: 89 54 24 1c    mov %edx,0x1c(%rsp) 
    20: e8 00 00 00 00   callq 25 <bar<foo, 0u>::get(bool)+0x25> 
    25: 84 c0     test %al,%al 
    27: 0f 85 eb 00 00 00  jne 118 <bar<foo, 0u>::get(bool)+0x118> 
    2d: 48 c7 44 24 08 00 00 movq $0x0,0x8(%rsp) 
    34: 00 00 
    36: 4c 89 ff    mov %r15,%rdi 
    39: 4d 8d b7 30 01 00 00 lea 0x130(%r15),%r14 
    40: e8 00 00 00 00   callq 45 <bar<foo, 0u>::get(bool)+0x45> 
    45: 84 c0     test %al,%al 
    47: 88 44 24 1b    mov %al,0x1b(%rsp) 
    4b: 0f 85 ef 00 00 00  jne 140 <bar<foo, 0u>::get(bool)+0x140> 
    51: 80 7c 24 1c 00   cmpb $0x0,0x1c(%rsp) 
    56: 0f 85 24 03 00 00  jne 380 <bar<foo, 0u>::get(bool)+0x380> 
    5c: 48 8b 44 24 10   mov 0x10(%rsp),%rax 
    61: c6 00 00    movb $0x0,(%rax) 
    64: 80 7c 24 1b 00   cmpb $0x0,0x1b(%rsp) 
    69: 75 25     jne 90 <bar<foo, 0u>::get(bool)+0x90> 
    6b: 48 8b 74 24 10   mov 0x10(%rsp),%rsi 
    70: 4c 89 ff    mov %r15,%rdi 
    73: e8 00 00 00 00   callq 78 <bar<foo, 0u>::get(bool)+0x78> 
    78: 48 8b 44 24 10   mov 0x10(%rsp),%rax 
    7d: 48 81 c4 68 02 00 00 add $0x268,%rsp 
    84: 5b      pop %rbx 
    85: 5d      pop %rbp 
    86: 41 5c     pop %r12 
    88: 41 5d     pop %r13 
    8a: 41 5e     pop %r14 
    8c: 41 5f     pop %r15 
    8e: c3      retq 
    8f: 90      nop 
    90: 4c 89 f7    mov %r14,%rdi 
    93: e8 00 00 00 00   callq 98 <bar<foo, 0u>::get(bool)+0x98> 
    98: 83 f8 04    cmp $0x4,%eax 
    9b: 74 f3     je  90 <bar<foo, 0u>::get(bool)+0x90> 
    9d: 85 c0     test %eax,%eax 
    9f: 0f 85 e4 08 00 00  jne 989 <bar<foo, 0u>::get(bool)+0x989> 
    a5: 49 83 87 b0 01 00 00 addq $0x1,0x1b0(%r15) 
    ac: 01 
    ad: 49 8d 9f 58 01 00 00 lea 0x158(%r15),%rbx 
    b4: 48 89 df    mov %rbx,%rdi 
    b7: e8 00 00 00 00   callq bc <bar<foo, 0u>::get(bool)+0xbc> 
    bc: 49 8d bf 80 01 00 00 lea 0x180(%r15),%rdi 
    c3: e8 00 00 00 00   callq c8 <bar<foo, 0u>::get(bool)+0xc8> 
    c8: 48 89 df    mov %rbx,%rdi 
    cb: e8 00 00 00 00   callq d0 <bar<foo, 0u>::get(bool)+0xd0> 
    d0: 4c 89 f7    mov %r14,%rdi 
    d3: e8 00 00 00 00   callq d8 <bar<foo, 0u>::get(bool)+0xd8> 
    d8: 83 f8 04    cmp $0x4,%eax 

Lo smontaggio di questa particolare funzione continua, ma una cosa che ho notato è il numero relativamente elevato di call istruzioni come questo:

20: e8 00 00 00 00   callq 25 <bar<foo, 0u>::get(bool)+0x25> 

Queste istruzioni, sempre con l'opcode e8 00 00 00 00, si verificano frequentemente attraverso il codice generato, e da quello che posso dire, non sono altro che no-ops; tutti sembrano semplicemente passare alla prossima istruzione. Questo pone la domanda, quindi, c'è una buona ragione per cui tutte queste istruzioni sono generate?

Sono preoccupato per l'impronta di cache delle istruzioni del codice generato, quindi sprecare 5 byte molte volte in una funzione sembra controproducente. Sembra un po 'pesante per un nop, a meno che il compilatore non stia cercando di conservare un qualche tipo di allineamento di memoria o qualcosa del genere. Non sarei sorpreso se questo fosse il caso.

Ho compilato il mio codice utilizzando g ++ 4.8.5 utilizzando -O3 -fomit-frame-pointer. Per quello che vale, ho visto la generazione di codice simile usando clang 3.7.

+0

@PascalCuoq: Grazie; Immagino che probabilmente avrebbe dovuto essere ovvio. Se vuoi convertire una risposta, mi sembra abbastanza buona. –

+0

Ok, convertito e leggermente modificato. –

risposta

21

L'indirizzo di destinazione 00 00 00 00 (relativo) in e8 00 00 00 00 deve essere compilato dal linker. Ciò non significa che la chiamata cada attraverso. Significa solo che stai smontando un file oggetto che non è ancora stato collegato.

Inoltre, una chiamata all'istruzione successiva, se quello era il risultato finale dopo la fase di collegamento, non sarebbe un no-op, perché cambia lo stack (un certo suggerimento che questo non è ciò che sta accadendo in il tuo caso).

+11

Usa 'objdump -dr' per vedere dove puntano realmente. – Jester

+1

@PascalCuoq: hai esattamente ragione. Ho ottenuto risultati molto più realistici quando ho disassemblato la libreria condivisa in cui il file oggetto era collegato. Tutti i 'callq's corrispondevano ai salti condizionali relativi all'interno della funzione. –

+0

@Jester: Grazie, anche questo funziona bene. –

Problemi correlati