In der x86-64-Tour der Intel-Handbücher habe ich gelesen
Die vielleicht überraschendste Tatsache ist, dass ein Befehl wie beispielsweise
MOV EAX, EBX
automatisch die oberen 32RAX
Registerbits auf Null setzt .
In der Intel-Dokumentation (3.4.1.1 Allzweckregister im 64-Bit-Modus in der manuellen Basisarchitektur), die an derselben Quelle zitiert wird, heißt es:
- 64-Bit-Operanden erzeugen ein 64-Bit-Ergebnis im Ziel-Allzweckregister.
- 32-Bit-Operanden erzeugen ein 32-Bit-Ergebnis, das im Ziel-Allzweckregister auf ein 64-Bit-Ergebnis erweitert wird.
- 8-Bit- und 16-Bit-Operanden erzeugen ein 8-Bit- oder 16-Bit-Ergebnis. Die oberen 56 Bits bzw. 48 Bits des Ziel-Allzweckregisters werden durch die Operation nicht modifiziert. Wenn das Ergebnis einer 8-Bit- oder 16-Bit-Operation für die 64-Bit-Adressberechnung vorgesehen ist, erweitern Sie das Register explizit auf die vollen 64-Bit-Zeichen.
In der x86-32- und x86-64-Assembly werden 16-Bit-Anweisungen wie z
mov ax, bx
Zeigen Sie nicht diese Art von "seltsamem" Verhalten, dass das obere Wort von eax auf Null gesetzt ist.
Also: Was ist der Grund, warum dieses Verhalten eingeführt wurde? Auf den ersten Blick scheint es unlogisch (aber der Grund könnte sein, dass ich an die Macken der x86-32-Assembly gewöhnt bin).
r32
Zieloperanden setzen den High 32 auf Null, anstatt zusammenzuführen. Zum Beispiel wird ersetzen einige Montiererpmovmskb r64, xmm
mitpmovmskb r32, xmm
einer REX zu speichern, da die 64 - Bit - Zielversion identisch verhält. Obwohl im Abschnitt "Operation" des Handbuchs alle 6 Kombinationen von 32/64-Bit-Dest und 64/128 / 256b-Quelle separat aufgeführt sind, dupliziert die implizite Null-Erweiterung des r32-Formulars die explizite Null-Erweiterung des r64-Formulars. Ich bin gespannt auf die HW-Implementierung ...xor eax,eax
oderxor r8d,r8d
ist der beste Weg, um RAX oder R8 auf Null zu setzen (das Speichern eines REX-Präfixes für RAX und 64-Bit-XOR wird in Silvermont nicht einmal speziell behandelt). Verwandte: Wie genau funktionieren Teilregister auf Haswell / Skylake? Das Schreiben von AL scheint eine falsche Abhängigkeit von RAX zu haben, und AH ist inkonsistentAntworten:
Ich bin nicht AMD oder spreche für sie, aber ich hätte es genauso gemacht. Da das Nullstellen der oberen Hälfte keine Abhängigkeit vom vorherigen Wert erzeugt, müsste die CPU warten. Der Mechanismus zur Umbenennung von Registern würde im Wesentlichen zunichte gemacht, wenn dies nicht auf diese Weise geschehen würde.
Auf diese Weise können Sie schnellen Code mit 32-Bit-Werten im 64-Bit-Modus schreiben, ohne die Abhängigkeiten ständig explizit aufheben zu müssen. Ohne dieses Verhalten müsste jeder einzelne 32-Bit-Befehl im 64-Bit-Modus auf etwas warten, das zuvor passiert ist, obwohl dieser hohe Teil fast nie verwendet würde. (Das Erstellen von
int
64-Bit würde den Cache-Platzbedarf und die Speicherbandbreite verschwenden. X86-64 unterstützt am effizientesten 32- und 64-Bit-Operandengrößen. )Das Verhalten für 8- und 16-Bit-Operandengrößen ist seltsam. Der Abhängigkeitswahnsinn ist einer der Gründe, warum 16-Bit-Befehle jetzt vermieden werden. x86-64 erbte dies von 8086 für 8-Bit und 386 für 16-Bit und entschied, dass 8- und 16-Bit-Register im 64-Bit-Modus genauso funktionieren wie im 32-Bit-Modus.
Siehe auch Warum verwendet GCC keine Teilregister? Für praktische Details, wie Schreibvorgänge in 8- und 16-Bit-Teilregister (und nachfolgende Lesevorgänge des vollständigen Registers) von echten CPUs behandelt werden.
quelle
Es spart einfach Platz in den Anweisungen und im Befehlssatz. Sie können kleine Sofortwerte mithilfe vorhandener (32-Bit-) Anweisungen in ein 64-Bit-Register verschieben.
Außerdem müssen Sie keine 8-Byte-Werte codieren
MOV RAX, 42
, wennMOV EAX, 42
diese wiederverwendet werden können.Diese Optimierung ist für 8- und 16-Bit-Operationen nicht so wichtig (weil sie kleiner sind), und eine Änderung der Regeln dort würde auch alten Code beschädigen.
quelle
XOR EAX, EAX
daXOR RAX, RAX
ein REX-Präfix erforderlich wäre.[rsi + edx]
ist nicht erlaubt). Ein weiterer wichtiger Grund ist natürlich die Vermeidung falscher Abhängigkeiten / Teilregisterstillstände (die andere Antwort).Ohne eine Ausdehnung von Null auf 64 Bit würde dies bedeuten, dass ein Befehl, aus dem gelesen wird
rax
, zwei Abhängigkeiten für seinenrax
Operanden hat (den Befehl, in den geschrieben wird,eax
und den Befehl, in denrax
davor geschrieben wird). Dies bedeutet, dass 1) der ROB Einträge für haben müsste mehrere Abhängigkeiten für einen einzelnen Operanden, was bedeutet, dass der ROB mehr Logik und Transistoren benötigt und mehr Platz beansprucht, und die Ausführung langsamer auf eine unnötige zweite Abhängigkeit wartet, deren Ausführung möglicherweise ewig dauert; oder alternativ 2), was meiner Meinung nach mit den 16-Bit-Befehlen geschieht, bleibt die Zuweisungsstufe wahrscheinlich stehen (dh wenn die RAT eine aktive Zuordnung für einenax
Schreibvorgang hat und eineax
Lesevorgang angezeigt wird, bleibt sie stehen, bis derax
Schreibvorgang beendet wird).Der einzige Vorteil der Erweiterung nicht Null ist die Sicherstellung der Bits höherer Ordnung von
rax
enthalten sind. Wenn sie beispielsweise ursprünglich 0xffffffffffffffff enthalten, wäre das Ergebnis 0xffffffff00000007, aber es gibt kaum einen Grund für die ISA, diese Garantie auf eine solche Kosten zu übernehmen Es ist wahrscheinlicher, dass der Vorteil einer Null-Erweiterung tatsächlich mehr erforderlich ist, sodass die zusätzliche Codezeile gespart wirdmov rax, 0
. Durch die Gewährleistung wird es immer verlängert werden null bis 64 Bits können die Compiler mit diesem Axiom daran arbeiten , während inmov rdx, rax
,rax
nur für seine einzige Abhängigkeit warten, dh es ist die Ausführung schneller beginnen kann und sich zurückziehen, Ausführungseinheiten frei. Darüber hinaus ermöglicht es auch effizientere Null-Idiome wiexor eax, eax
Null,rax
ohne dass ein REX-Byte erforderlich ist.quelle
cmovbe
ist 2 Uops, abercmovb
ist 1). Aber keine CPU, die eine Umbenennung von Teilregistern durchführt, macht es so, wie Sie es vorschlagen. Stattdessen fügen sie ein Zusammenführungs-UOP ein, wenn eine Teilregistrierung getrennt von der Vollregistrierung umbenannt wird (dh "schmutzig" ist). Siehe Warum verwendet GCC keine Teilregister? und wie genau funktionieren Teilregister auf Haswell / Skylake? Das Schreiben von AL scheint eine falsche Abhängigkeit von RAX zu haben, und AH ist inkonsistentThis gives a delay of 5 - 6 clocks. The reason is that a temporary register has been assigned to AL to make it independent of AH. The execution unit has to wait until the write to AL has retired before it is possible to combine the value from AL with the value of the rest of EAX
Ich kann kein Beispiel für das 'Zusammenführen von UOP' finden, das verwendet werden würde, um dies zu lösen, genau wie bei einem partiellen Flaggenstillstandmov al, [mem]
wie eine mikrofusionierte Last + ALU- Zusammenführen, nur Umbenennen von AH, und ein AH-Zusammenführen von UOP wird immer noch alleine ausgegeben. Die Mechanismen zum Zusammenführen von Teilflags in diesen CPUs variieren, z. B. bleibt Core2 / Nehalem im Gegensatz zu Teilregistern nur für Teilflags stehen.