129 Stimmen

`testl` eax gegen eax?

Ich versuche zu verstehen, etwas Assembly.

Der Assembly-Code lautet wie folgt, ich interessiere mich für die testl Zeile:

000319df  8b4508        movl   0x08(%ebp), %eax  
000319e2  8b4004        movl   0x04(%eax), %eax  
000319e5  85c0          testl  %eax, %eax  
000319e7  7407          je     0x000319f0  

Ich versuche zu verstehen, was der Punkt des testl zwischen %eax und %eax ist? Ich denke, die Details dieses Codes sind nicht wichtig, ich versuche nur zu verstehen, ob der Test mit sich selbst nicht immer wahr wäre?

102voto

Die Bedeutung von test besteht darin, die Argumente miteinander zu VERUNDEN und das Ergebnis auf Null zu überprüfen. Daher überprüft dieser Code, ob EAX null ist oder nicht. je springt, wenn null.

Übrigens generiert dies eine kleinere Anweisung als cmp eax, 0, was der Grund dafür ist, dass Compiler dies im Allgemeinen auf diese Weise tun werden.

97voto

Chris Jester-Young Punkte 212385

Es wird getestet, ob eax 0 ist, oder darüber oder darunter liegt. In diesem Fall wird der Sprung genommen, wenn eax 0 ist.

3 Stimmen

Ich habe eine Änderung vorgenommen, um diese beliebte Antwort in eine bessere kanonische Antwort auf "Was ist dieses TEST-Ding überhaupt und wie unterscheidet es sich von CMP" umzuwandeln, was irgendwie impliziert ist. Sehen Sie weiter unten meine eigene Antwort für Kommentare zur semantischen Bedeutung der synonymen JE und JZ. Bitte überprüfen Sie meine Bearbeitung, da sie ziemlich umfangreich ist, und es ist immer noch Ihre Antwort.

1 Stimmen

@PeterCordes Ich schätze die Absicht, aber ich werde deine Bearbeitung rückgängig machen. 1. Deine "Stimme" ist sehr unterschiedlich von meiner und im Moment liest es sich viel mehr wie deine Antwort als wie meine. 2. Problematischer ist die kühne Behauptung, dass die Flags genau gleich herauskommen zwischen test und cmp. Ja, ich verstehe, dass das deine Überzeugung aufgrund deiner Kommentare zu Cody ist. Jedoch es in meinen Beitrag zu setzen, ist eine andere Sache; es ist keine Behauptung, zu der ich stehen möchte, einfach weil ich es nicht weiß, ob es in allen Fällen identisch ist.

0 Stimmen

@PeterCordes Ich verstehe den Wunsch nach einer "kanonischen" Antwort, aber ich denke, deine Antwort muss auf ihren eigenen Verdiensten schwimmen oder untergehen, anstatt sich an einer akzeptierten Antwort anzuhängen. Das musste ich auch für einige meiner Antworten auf sehr beliebte Fragen tun, wie z.B. die Gleitkommazahlen-Einführung, die zum Zeitpunkt meiner Antwort bereits andere Antworten mit 500+ Upvotes hatte.

35voto

Nils Pipenbrinck Punkte 80152

Die Testanweisung führt eine logische UND-Operation zwischen den Operanden durch, schreibt das Ergebnis jedoch nicht in ein Register zurück. Es werden nur die Flags aktualisiert.

In Ihrem Beispiel setzt die Test eax, eax-Anweisung das Null-Flag, wenn eax null ist, das Vorzeichen-Flag, wenn das höchste Bit gesetzt ist, und einige andere Flags ebenfalls.

Die Jump if Equal (je)-Anweisung springt, wenn das Null-Flag gesetzt ist.

Sie können den Code in einen übersichtlicheren Code übersetzen, so:

cmp eax, 0
je irgendwo

Dies hat die gleiche Funktionalität, erfordert jedoch einige Bytes mehr Code-Speicherplatz. Das ist der Grund, warum der Compiler ein Test anstelle eines Vergleichs ausgegeben hat.

3 Stimmen

Eigentlich könnte cmp dort nicht funktionieren. Das heißt, es funktioniert für den spezifischen Fall, aber cmp beeinflusst die Flags anders als test, da es sich um ein internes Sub statt und handelt. Etwas, woran man denken sollte.

4 Stimmen

Für einen Test gegen Null ist es absolut gültig.

3 Stimmen

Aber du weißt nicht, was sonst noch auf die Fahnen schaut. Die Auswirkungen auf Fahnen sind sehr unterschiedlich, daher kann dies ein Problem sein und ist es sehr häufig.

31voto

Peter Cordes Punkte 279904

Test ist wie and, außer dass es nur FLAGS schreibt und beide Eingänge unverändert lässt. Mit zwei verschiedenen Eingängen ist es nützlich, um zu testen, ob einige Bits alle Null sind oder ob mindestens eines gesetzt ist. (z.B. test al, 3 setzt ZF, wenn EAX ein Vielfaches von 4 ist (und somit beide seiner niedrigen 2 Bits auf Null gesetzt hat).


test eax,eax setzt alle Flags genau so, wie es cmp eax, 0 würde:

Außer bei AF (Hilfs-Carry-Flag, verwendet von ASCII/BCD-Anweisungen). TEST lässt es undefiniert, aber CMP setzt es "entsprechend dem Ergebnis". Da Subtrahieren von Null keinen Carry vom 4. zum 5. Bit erzeugen kann, sollte CMP AF immer löschen.


TEST ist kleiner (keine Immediate) und manchmal schneller (kann auf mehr CPUs in mehr Fällen in ein Vergleichen-und-Sprung-UOP fusionieren als CMP). Dies macht test zum bevorzugten Ausdruck für den Vergleich eines Registers mit null. Es ist eine Peephole-Optimierung für cmp reg,0, die unabhängig von der semantischen Bedeutung verwendet werden kann.

Der einzige häufige Grund für die Verwendung von CMP mit einem Immediate-Wert von 0 ist, wenn Sie gegen eine Speicheroperanden vergleichen möchten. Zum Beispiel cmpb $0, (%esi) um nach einem Terminierungsnullbyte am Ende eines impliziten C-Style-Strings zu suchen.


AVX512F fügt kortestw k1, k2 hinzu und AVX512DQ/BW (Skylake-X aber nicht KNL) fügen ktestb/w/d/q k1, k2 hinzu, die auf AVX512-Maskenregister (k0..k7) arbeiten, aber immer noch reguläre FLAGS wie test setzen, genauso wie Ganzzahl-OR oder AND Anweisungen. (Ähnlich wie SSE4 ptest oder SSE ucomiss: Eingaben im SIMD-Bereich und Ergebnis in Ganzzahl-FLAGS.)

kortestw k1,k1 ist die idiomatische Möglichkeit, basierend auf einem AVX512-Vergleichsergebnis zu verzweigen / cmovcc / setcc, wodurch SSE/AVX2 (v)pmovmskb/ps/pd + test oder cmp ersetzt werden.


Die Verwendung von jz vs. je kann verwirrend sein.

jz und je sind buchstäblich dieselbe Anweisung, d.h. dieselbe Opcodes im Maschinencode. Sie tun dasselbe, haben aber unterschiedliche semantische Bedeutung für Menschen. Disassembler (und typischerweise Assemblierungscode von Compilern) werden immer nur eine verwenden, sodass die semantische Unterscheidung verloren geht.

cmp und sub setzen ZF, wenn ihre beiden Eingaben gleich sind (d.h. das Subtraktionsergebnis ist 0). je (springen, wenn gleich) ist das semantisch relevante Synonym.

test %eax,%eax / and %eax,%eax setzt erneut ZF, wenn das Ergebnis Null ist, aber es gibt keinen "Gleichheits"-Test. ZF nach test sagt Ihnen nicht, ob die beiden Operanden gleich waren. Also ist jz (springen, wenn null) das semantisch relevante Synonym.

0 Stimmen

Ich würde in Betracht ziehen, grundlegende Informationen über test als bitweise and-Operation hinzuzufügen, die für Personen, die sich gerade mit der Assemblersprache beschäftigen (und zu faul/unwissend sind, alle 60 Sekunden in der Anweisungsreferenz nachzuschlagen ;) :) ), möglicherweise nicht offensichtlich sind.

1 Stimmen

@Ped7g: Nun gut, ich denke, es kann nicht schaden, alles in diese Antwort zu packen, anstatt diesen Teil den anderen Antworten zu überlassen. Habe AVX512 kortest* und ktest* hinzugefügt, während ich dabei war.

0 Stimmen

BTW, das ist im Grunde dasselbe wie meine Antwort auf eine andere Version der gleichen Frage, aber ich habe dort mehr über Leistung gesagt, z. B. möglicherweise Register-Lese-Unterbrechungen auf alten P6-Familien-CPUs wie Nehalem zu vermeiden, indem das Register mit demselben Wert neu geschrieben wird.

5voto

DarenW Punkte 16149

Dieser Code-Ausschnitt stammt aus einer Unterfunktion, die einen Zeiger auf etwas erhalten hat, wahrscheinlich eine Struktur oder ein Objekt. Die zweite Zeile dereferenziert diesen Zeiger und ruft einen Wert von diesem Ding ab - möglicherweise selbst ein Zeiger oder vielleicht nur eine int, die als sein zweites Element (Offset +4) gespeichert ist. Die dritte und vierte Zeile prüfen diesen Wert auf Null (NULL, wenn es ein Zeiger ist) und überspringen die folgenden wenigen Operationen (nicht gezeigt), wenn er null ist.

Der Test auf Null wird manchmal als Vergleich mit einem unmittelbaren Literalnullwert codiert, aber der Compiler (oder der Mensch?), der dies geschrieben hat, dachte möglicherweise, dass ein Testl op schneller laufen würde - unter Berücksichtigung aller modernen CPU-Dinge wie Pipelineing und Registernamen. Es stammt aus demselben Trickbeutel, der die Idee des Löschen eines Registers mit XOR EAX,EAX (das ich auf dem Nummernschild eines Autos in Colorado gesehen habe!) anstelle der offensichtlichen, aber vielleicht langsameren MOV EAX, #0 (ich verwende eine ältere Notation).

In Assembler, wie Perl, TMTOWTDI.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X