2016-04-15 5 views
7

Angesichts der folgenden C-Funktion:Warum fügt GCC auf x86-64 ein NOP in eine Funktion ein?

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

GCC 5 und 6 auf x86-64 Kompilierung (plain gcc -c -g -o von objdump gefolgt), dies zu:

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 

Gibt es einen Grund für GCC die einfügen 90/nop um 1f oder ist das nur ein Nebeneffekt, der passieren kann, wenn keine Optimierungen eingeschaltet sind?

Hinweis: Diese Frage unterscheidet sich von den meisten anderen, weil sie nach nop in einem Funktionskörper fragt, nicht nach einem externen Padding.

Compiler-Versionen getestet: GCC Debian 5.3.1-14 (5.3.1) und Debian 6-20160313-1 (6.0.0)

+1

NOPs werden oft aus Zeitgründen verwendet. Normalerweise muss man sich keine Sorgen machen. –

+0

Warum sollten Sie sich mit * Timing * in obigem befassen? –

+0

Kann es mit Pipelining und Wartezuständen zu tun haben? –

Antwort

9

Das ist seltsam, ich Streu nop s nie bemerkt hatte in der asm-Ausgabe bei -O0 vor. (Wahrscheinlich, weil ich meine Zeit nicht damit verschwendet, unoptimierte Compiler-Ausgaben zu betrachten).

Normalerweise sind nop s Innenfunktionen zum Ausrichten von Verzweigungszielen, einschließlich Funktionseinstiegspunkten wie in the question Brian linked. (Siehe auch -falign-loopsin the gcc docs, die standardmäßig auf anderen Optimierungsstufen als -Os aktiviert ist).


In diesem Fall ist die nop Teil des Compilers Rauschens für eine bloße leere Funktion:

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 so können Sie die asm für andere Compiler-Versionen überprüfen und Optionen kompilieren.

(nicht technisch Lärm, aber -O0-fno-omit-frame-pointer ermöglicht und bei -O0 auch leere Funktionen einrichten und reißen einen Stapelrahmen nach unten.)


Natürlich, dass nop jeder nicht vorhanden ist Null-Optimierungsebene. Es gibt kein Debugging oder Leistungsvorteil zu diesem nop im Code in der Frage. (die Leistungsführungsglieder im Tag Wiki Siehe, esp. Agner Fog's microarchitecture guide darüber zu erfahren, was Code schnell auf aktuellen CPUs macht.)

Meine Vermutung ist, dass es rein ein Artefakt von gcc Interna. Diese nop ist dort als nop in der gcc -S asm-Ausgabe, nicht als .p2align Richtlinie. gcc selbst zählt keine Maschinenkodebytes, es verwendet an bestimmten Punkten nur Ausrichtungsdirektiven, um wichtige Verzweigungsziele auszurichten. Nur der Assembler weiß, wie groß ein nop tatsächlich ist, um die gegebene Ausrichtung zu erreichen.

Der Standard -O0 sagt gcc, dass Sie es schnell kompilieren wollen und nicht guten Code machen.Dies bedeutet, dass die asm-Ausgabe Ihnen mehr über gcc-Interna als andere -O Ebenen und sehr wenig darüber, wie Sie oder irgendetwas anderes optimieren.

Wenn Sie versuchen, asm zu lernen, ist es interessanter, zum Beispiel den Code unter -Og zu betrachten (für Debugging optimieren).

Wenn Sie versuchen, zu sehen, wie gut gcc oder clang beim Erstellen von Code funktionieren, sollten Sie sich -O3 -march=native (oder -O2 -mtune=intel oder die Einstellungen ansehen, mit denen Sie Ihr Projekt erstellen). Die Optimierungen bei -O3 zu puzzeln ist eine gute Möglichkeit, ein paar Tricks zu lernen. -fno-tree-vectorize ist praktisch, wenn Sie eine nicht-vektorisierte Version von etwas anderem als optimiert sehen wollen.

+0

Gute Antwort. Sehr interessant –

Verwandte Themen