Warum verwenden CPU-Architekturen ein Flags-Register (Vorteile?)

15

Einige CPUs haben ein Flags-Register (ARM, x86, ...), andere nicht (MIPS, ...). Was ist der Vorteil eines CMP-Befehls zum Aktualisieren des Flags-Registers, gefolgt von einem Verzweigungsbefehl, anstatt ein Nullregister und bedingte Verzweigungen zum Überprüfen auf Vorzeichen, Überlauf usw. zu verwenden?

Modellwelt
quelle

Antworten:

11

In modernen Mikroarchitekturen mit Registerumbenennung sind die Implementierungskosten für Flags oder Nicht-Flags ziemlich ähnlich. Der Hauptunterschied, den ich mir vorstellen kann, besteht darin, dass einige Flags die Eigenschaften eines Werts anzeigen (Ist der Wert negativ? Ist der Wert Null? Hat der Wert eine gerade oder ungerade Parität?), Während einige ein Ereignis darstellen, das während einer vorherigen Operation aufgetreten ist (Hatte der Befehl add einen Übertrag oder einen Überlauf?) Dies führte auf dem MIPS zu einer nicht idealen Situation, als Sie eine 64-Bit-Addition auf der 32-Bit-Architektur (oder eine 128-Bit-Addition auf dem MIPS) simulieren wollten 64-Bit-Architektur.) Auf den meisten Architekturen mit Carry-Flag gibt es eine spezielleadd-with-carryBefehl, der das Übertragsflag aus dem vorherigen Befehl add enthält. Dies macht das Simulieren von Arithmetik mit mehreren Genauigkeiten auf vielen Architekturen mit Flags-Registern relativ kostengünstig.

Auf der anderen Seite ist das Testen eines N-Bit-Registers auf Null oder Nicht-Null tatsächlich überraschend teuer. Um ein N-Bit-Register auf Null zu testen, müssen Sie eine N-Bit-NOR-Operation ausführen, für deren Berechnung Logikpegel erforderlich sind . Bei Architekturen mit Flags-Registern kann die zusätzliche Logik für die Null- / Nicht-Null-Berechnung am Ende der ALU-Stufe dazu führen, dass der Takt langsamer läuft (oder die ALU zu zwei Zyklusoperationen gezwungen wird). Aus diesem Grund, denke ich, einige Architekturen wie SPARC hatten zwei Versionen jeder arithmetischen Operation, eine, die Flags setzte, und eine, die keine hatte.Ö(LogN)

Aber MIPS speichert hier nichts. Sie haben das Problem einfach woanders hingelegt. Auf MIPS gibt es eine branch-on-equalAnweisung. Dies bedeutet, dass der Verzweigungsbefehl tatsächlich eine ALU-Stufe haben muss (einschließlich einer bitweisen xorOperation, gefolgt von einem nor, um auf das einzelne gleiche / ungleiche Bit zu reduzieren), bevor bestimmt wird, in welche Richtung die Verzweigung geht.

Die DEC Alpha-Architektur hat versucht, den Unterschied mit einem Trick aufzuteilen. DEC Alpha hatte keine Flaggenregister, aber auch keine branch-on-equalAnweisung. Stattdessen betrachten alle Verzweigungsbefehle den Zustand eines einzelnen Universalregisters. Es ist branch-on-zero, branch-on-not-zero, branch-on-less-than-zeroetc. Der Trick ist , dass Sie alle Universalregister ein zusätzliches 65. Bit , das Ihnen sagt , geben kann , ob die anderen 64 Bits alle Null ist oder nicht. Das macht es eher so, als hätte man ein Flagsregister: Alle Verzweigungsbefehle betrachten ein einziges Bit (das bereits berechnet wurde), um ihre Entscheidung zu treffen, aber jetzt müssen Sie wieder herausfinden, wie Sie dieses zusätzliche Null-Indikatorbit während einer normalen ALU berechnen Zyklus. (Und Sie können immer noch keine Multi-Präzisions-Arithmetik ausführen, indem Sie nur das Carry-Flag der vorherigen Operation betrachten.)

Wandering Logic
quelle
2
Die Nicht-CC-Einstellungsoperationen waren ( soweit ich weiß ) eine Compileroptimierung , die es dem Compiler ermöglichte, CC-Einstellungsbefehle frühzeitig einzuplanen, ohne dass der Wert durch letztere Befehle beeinträchtigt wird. Der PowerPC750 platzierte die Bedingungsregister (8 4-Bit-Register) näher am Front-End, so dass eine genommene Verzweigung, die den Verzweigungszielbefehlscache trifft und die Bedingung früh genug verfügbar hat , eine genommene Verzweigung ohne Strafe auflösen kann . (Das CRISP von AT & T nutzte auch die frühzeitige Auflösung von Zweigniederlassungen.) Die geringe Menge und Spezialisierung von CCs macht dies praktischer.
Paul A. Clayton
Ein Detail: Es werden nicht alle Merkerberechnungen gleich gemacht. Stellen Sie sich vor, Ihre CPU verfügt über die traditionellen NZVC-Flags. Wenn alle ALU-Anweisungen die Flags aktualisieren dürfen, müssen Sie die Flag-Generierung nach dem Addierer / Subtrahierer und einigen Multiplexern platzieren. Das Negative Flag ist einfach, es ist nur das MSB, während das Zero Flag teuer ist und von jedem Bit abhängt. Wenn Sie nun Flags auf Compare- (und Bittest-) Anweisungen beschränken, können die Zero-Flags mit parallelen XORs auf den Quelloperanden berechnet werden, ohne auf das Ergebnis der Subtraktion zu warten. Die Berechnung des Z-Flags nach einer Addition ist nahezu unbrauchbar.
TEMLIB
7

1 Aus ISA-Sicht

  1. Testanweisungen, die nur die Flags setzen, sind nur eine Möglichkeit, den Registerdruck in Architekturen mit Registermangel zu verringern. Wenn Sie genug Register haben, ändern Sie einfach eines davon und ignorieren Sie das Ergebnis. Der Trick, ein Register 0 mit dem Eingabewert 0 zu haben, ist nur ein Codierungstrick, der praktisch ist, wenn Sie genug Register haben, um eines davon auf 0 zu setzen, anstatt die Anzahl der Befehle zu erhöhen. Es ist dann bequem, es auch als Ziel zu verwenden (es reduziert die Anzahl falscher Abhängigkeiten).

  2. Nochmal codieren. Wenn Sie die Bedingung in Sprüngen codieren, haben Sie Sprünge mit 3 Operanden (die beiden zu vergleichenden und das Sprungziel), von denen zwei unmittelbare Werte sein sollen, einer so groß wie möglich (Sprünge haben oft ein eigenes Codierungsformat, damit das Ziel so viele Bits wie möglich verwenden kann). Oder du lässt Möglichkeiten fallen.

  3. Die Verwendung von Flags bietet Ihnen mehr Möglichkeiten, diese zu setzen. Es sind nicht nur die Vergleichsoperationen, die die Flags setzen können, sondern was auch immer Sie wollen. (Mit der Einschränkung, dass je mehr Operationen Sie haben, die Flags setzen, desto sorgfältiger müssen Sie sicherstellen, dass die letzte Operation, die die Flags setzt, die gewünschte ist). Wenn Sie Flags haben, können Sie die Anzahl der Bedingungen (häufig 16) multiplizieren mit der Anzahl der Anweisungen, mit denen die Flags gesetzt werden können. Wenn Sie keine Flags verwenden, erhalten Sie ungefähr so ​​viele bedingte Sprünge wie Sie Dinge zu testen haben oder es gibt Dinge, die Sie nicht so einfach testen können (zum Beispiel Übertragen oder Überlaufen).

2 Aus Sicht des Implementierers

  1. Das Testen von Flags ist einfach und schnell erledigt. Je komplexer Ihr Test ist, desto mehr Auswirkungen hat er auf die Zykluszeit (oder auf die Pipeline-Struktur, wenn Sie eine Pipeline-Verbindung herstellen). Dies gilt insbesondere für einfachere Implementierungen. Wenn Sie mit allen Tricks des Buches zu einem High-End-Prozessor gelangen, ist der Effekt ziemlich gering.

  2. Flags zu haben bedeutet, dass viele Anweisungen mehrere Ergebnisse haben (das natürliche Ergebnis und jedes der modifizierten Flags). Und bei einem POV mit Mikroarchitektur sind mehrere Ergebnisse schlecht (Sie müssen die Zuordnung nachverfolgen). Wenn Sie nur einen Satz von Flags haben, die Abhängigkeiten einführen (nicht erforderlich, wenn das Flag dann nicht verwendet wird), müssen Sie auf die eine oder andere Weise damit umgehen. Dies gilt insbesondere für einfachere Implementierungen. Wenn Sie mit allen Tricks des Buches zu einem High-End-Prozessor gelangen, werden die zusätzlichen Schwierigkeiten vom Rest des Prozessors in den Schatten gestellt.

Ein Programmierer
quelle
2

Auf einer 32-Bit-Maschine muss ein "Add-with-Carry" -Befehl, der als Teil einer Additionssequenz mit Mehrfachgenauigkeit verwendet wird, Operanden im Wert von 65 Bit akzeptieren und eine 33-Bit-Summe berechnen. Die Quellregisterspezifikationen geben an, woher 64 Operandenbits kommen sollen, und die Zielregisterspezifikation gibt an, wohin die unteren 32 Bits des Ergebnisses gehen sollen, aber was mit dem Operanden "add one extra" oder dem oberen Bit zu tun ist des ergebnisses? Als Teil des Befehls angeben zu dürfen, woher der zusätzliche Operand kommen soll und wohin das zusätzliche Ergebnisbit gehen soll, wäre mäßig nützlich, aber es wäre im Allgemeinen nicht so nützlich, ein zusätzliches Feld im Opcode zu rechtfertigen. Einen festen "Ort" für die Handhabung des Übertrags-Flags zu haben, kann aus Sicht der Befehlsplanung etwas umständlich sein, aber es ist

Wenn man versuchen würde, einen Befehlssatz zu entwerfen, der Arithmetik mit Mehrfachgenauigkeit ermöglicht, aber jeder Befehl auf zwei 32-Bit-Operanden und einen 32-Bit-Zieloperanden beschränkt ist, könnte man ein 64-Bit-Add in vier Befehlen implementieren: set r5 auf 1, wenn r0 + r2 andernfalls den Wert 0 haben würde; berechne r4 = r1 + r3; berechne r5 = r4 + r5; berechne r4 = r0 + r2 ", aber darüber hinaus würde es drei Anweisungen für jedes zusätzliche Wort erfordern. Durch die Verfügbarkeit eines Carry-Flags als zusätzliche Quelle und Ziel werden die Kosten auf einen Befehl pro Wort reduziert.

Man beachte übrigens, dass eine Befehlsbitsteuerung, ob der Befehl das Flagregister aktualisiert, eine Ausführung außerhalb der Reihenfolge erleichtern kann, da Befehle, die die Flagbits verwenden oder modifizieren, ihre Reihenfolge relativ zueinander beibehalten müssen, Befehle, die dies jedoch nicht tun frei angeordnet werden. Angesichts der Reihenfolge:

ldr  r0,[r1]
add  r0,r0,r2
eors r4,r5,r6

Eine Ausführungseinheit könnte ziemlich leicht erkennen, dass der dritte Befehl ausgeführt werden könnte, ohne darauf warten zu müssen, dass Daten gelesen werden [r1], aber wenn der zweite Befehl ausgeführt worden wäre adds r0,r0,r2, wäre dies nur möglich, wenn die Ausführungseinheit sicherstellen könnte, dass zu dem Zeitpunkt etwas versucht wird, dies zu verwenden Bei den Flags würde das Null-Flag den in der dritten Anweisung festgelegten Wert enthalten, während das Übertrags-Flag den Wert in der zweiten Anweisung enthält.

Superkatze
quelle
1
"Befehlsbitsteuerung, ob der Befehl das Flagregister aktualisiert": Verfügbar zum Beispiel in PowerPC, SPARC.
TEMLIB
MIPS verwendet "r5 = r1 + r2; setze r6, wenn r6 kleiner als r1 ist; r7 = r3 + r4; r5 = R5 + R6;". Einige SIMD-Erweiterungen könnten Vergleiche verwenden, die alle Bits auf Null oder Eins setzen (dh Null oder -1 Zweierkomplement-Ganzzahl), um den Übertrag zu finden und die Subtraktion, um den Übertrag anzuwenden.
Paul A. Clayton
@ PaulA.Clayton: Ich denke du meintest "wenn r5 kleiner als r1 ist". Wie würde MIPS mit längerer Mathematik umgehen? Würde es drei, mehr als drei oder weniger als drei Anweisungen pro Wort erfordern?
Supercat
@supercat Ja, das hätte "r6 setzen sollen, wenn r5 kleiner als r1 ist"!
Paul A. Clayton
@ PaulA.Clayton: Wie würde man beispielsweise zwei 64-Wort-Zahlen (2048-Bit) zu einem 32-Bit-MIPS hinzufügen? Gibt es eine effiziente Möglichkeit, die Übertragungen in und aus der mittleren Phase zu handhaben?
Supercat
0

Einfache Antwort ... schnelle, kostengünstige Speicheroperation, die bis auf die Anweisung selbst absolut keine interne Busbenutzung erfordert. Es kann als Stack-Bool ohne Stack oder Prozessbit ohne Speicher verwendet werden.

SkipBerne
quelle
1
Diese Antwort ist ziemlich detailliert. Lange Antworten sind nicht unbedingt erforderlich, aber etwas Ausgereifteres wäre eine deutliche Verbesserung.
David Richerby
Das Setzen eines Flags oder das Vergleichen eines Flag-Werts ist eine einzelne Anweisung ohne weitere Informationen in Form von Argumenten, die im Assemblycode enthalten wären. Flags sind auch das Ergebnis eines Prozessorvorgangs oder -tests und können effizient zum Verzweigen verwendet werden. Sie sind das tatsächliche Bit, das umgeschaltet oder gesetzt wird, wenn zwei Werte in Registern verglichen werden.
SkipBerne