2011-01-11 10 views
2

Ich habe gerade ein einfaches C-Programm mit einer if-Anweisung ausprobiert und analysiert seine Montage. Sein Verhalten ist jedoch sehr unterschiedlich, wenn das -O2-Flag zur Kompilierung verwendet wird.Verständnis Assembly: -O2 wenn Verzweigung

Der C-Code für das gleiche ist: -

#include<stdio.h> 

int main(int argc, char **argv) { 
    int a; 

    if(a<0) { 
     printf("A is less than 0\n"); 
    } 
} 

und die entsprechende Baugruppe ist: -

main: 
    push %ebp 
    mov %ebp, %esp 
    sub %esp, 8 
    and %esp, -16 
    sub %esp, 16 
    test %eax, %eax 
    js .L4 
    leave 
    ret 
    .p2align 4,,15 
.L4: 
    sub %esp, 12 
    push OFFSET FLAT:.LC0 
    call puts 
    add %esp, 16 
    leave 
    ret 
    .size main, .-main 
    .section .note.GNU-stack,"",@progbits 
    .ident "GCC: (GNU) 3.4.6" 

ich gelesen, dass die test Anweisung im Grunde führt nur die logische UND-Verknüpfung der zwei Operanden . Ich lese auch, dass die js Anweisung einen Sprung ausführt, wenn in der vorherigen Anweisung ein Zeichenwechsel erfolgt. Also, test ing eax mit eax würde geben 0 oder 1 und der Sprung würde davon abhängen.

Ich verstehe nicht, wie es hier für die Verzweigung verwendet wird. Könnte jemand erklären, wie das funktioniert?

+1

Ein guter Compiler sollte das ganze Programm auf nichts optimieren, da es undefiniertes Verhalten hat (Verwendung einer nicht initialisierten Variablen). Oder, wenn "a" initialisiert wurde, sollte es die Bedingung basierend auf dem Wert von "a" optimieren. –

Antwort

11

JS springt nicht auf, wenn es zu einer Änderung des Vorzeichens ist, springt er, wenn das Vorzeichen-Flag ist 1.

Das Vorzeichen-Bit ist an, wenn das Ergebnis der letzten Operation negativ war (negative Zahlen in Zweierkomplement haben das höchstwertige Bit in 1).

Wenn also die UND-Operation zwischen zwei negativen ganzen Zahlen (-1 & -1) war, wird das letzte Bit 1 (Zeichenflag) sein, also wird der Sprung gemacht. Wenn die Zahlen positiv sind, ist das letzte Bit 0, der Sprung wird nicht ausgeführt.

3

Nun, ich weiß nicht, was Sie erwarten, aber Ihr Programm hat undefiniertes Verhalten seit a ist nicht initialisiert. Die Assembler-Ausgabe könnte also buchstäblich alles Mögliche sein.

+3

Wird das ganze Programm undefiniert, nur weil 'a's Wert nicht definiert ist? Ich nahm an, dass es alles, was auf dem Stapel war, benutzen würde. – nmichaels

+1

Die Ausgabe des Programms könnte das printf sein oder es könnte nichts sein, aber warum sollte die Ausgabe des Compilers irgendetwas sein? –

+1

In der Theorie könnte die Ausgabe alles sein, aber kein echter Welt-Compiler wird Unsinn ausgeben. –

2

Wenn eax negativ ist, zeigen die Flags an, dass nach der test Anweisung der Sprung genommen wird. Das schiebt den PC zu .L4, der das Drucken durchführt. Sonst gehen wir.

2

test eax, eax gesetzt Null-Flag, wenn EAX = 0

js Anweisung wird im Grunde den Zeichen-Flag überprüfen (a < 0)

+0

'test' setzt * alle * arithmetischen Flags, nicht nur die Null-Flags. –

2

Sieht so aus, als wenn Sie -O2 angeben, der Compiler setzt Ihr "int a" in ein Register für die Geschwindigkeitsoptimierung.

Da Sie 'int a' niemals initialisieren, zeigt die Assembly nichts an, das an eax geschrieben wurde, und stattdessen hat sie den Wert, der ihr zuletzt zugewiesen wurde.

Die anderen Antworten erklären, wie der Test als Verzweigungsmechanismus funktioniert.

3

Die Intel Handbücher sind dafür gut. Dies ist, was er dokumentiert, für die Testanleitung:

alt text

SF ist das Zeichen-Flag, die eine, die durch die JS Opcode getestet wird. Es wird hier auf das höchstwertige Bit von eax gesetzt, das Vorzeichenbit. Der Sprung wird also genommen, wenn eax eine negative Zahl enthält.