Warum verwendet MIPS R0 als "Null", wenn Sie nur zwei Register XOR-verknüpfen könnten, um 0 zu erzeugen?

Ich glaube, ich suche nach einer Antwort auf eine Trivia-Frage. Ich versuche zu verstehen, warum die MIPS-Architektur einen expliziten "Null" -Wert in einem Register verwendet, wenn Sie dasselbe erreichen können, indem Sie einfach jedes Register mit sich selbst XOR-verknüpfen. Man könnte sagen, dass die Operation bereits für Sie erledigt ist; Ich kann mir jedoch keine Situation vorstellen, in der Sie viele "Null" -Werte verwenden würden. Ich habe Hennesseys Originalpapiere gelesen, und es wird tatsächlich ohne wirkliche Begründung eine Null zugewiesen.

Gibt es einen logischen Grund für eine fest codierte binäre Zuweisung von Null?

Update: In 8k einer ausführbaren Datei von xc32-gcc für den MIPS-Kern im PIC32MZ habe ich eine einzelne Instanz von "Null".

add     t3,t1,zero

Die eigentliche Antwort: Ich habe die Prämie an die Person vergeben, die die Informationen über MIPS und Bedingungscodes hatte. Die Antwort liegt tatsächlich in der MIPS-Architektur für Bedingungen. Obwohl ich dem zunächst keine Zeit widmen wollte, habe ich die Architektur für opensparc , RISC-V , MIPS-IV und OpenPOWER überprüft (dieses Dokument war intern) und hier sind die zusammenfassenden Ergebnisse. Das R0-Register, das aufgrund der Architektur der Pipeline zum Vergleich auf Verzweigungen erforderlich ist.

  • ganzzahliger Vergleich mit Null und Verzweigung (bgez,bgtz,blez,bltz)
  • Integer zwei Register vergleichen und verzweigen (beq,bne)
  • Integer Vergleich zweier Register und Trap (teq,tge,tlt,tne)
  • Integer-Vergleichsregister und Sofort und Trap (teqi,tgei,tlti,tnei)

Es kommt nur darauf an, wie die Hardware in der Umsetzung aussieht. Aus dem RISC-V-Handbuch gibt es auf Seite 68 ein nicht referenziertes Zitat:

Die bedingten Verzweigungen wurden entwickelt, um arithmetische Vergleichsoperationen zwischen zwei Registern (wie auch in PA-RISC und Xtensa ISA) einzuschließen, anstatt Bedingungscodes (x86, ARM, SPARC, PowerPC) zu verwenden oder nur ein Register mit Null zu vergleichen ( Alpha, MIPS) oder zwei Register nur für Gleichheit (MIPS). Dieses Design wurde durch die Beobachtung motiviert, dass ein kombinierter Vergleichs- und Verzweigungsbefehl in eine reguläre Pipeline passt, einen zusätzlichen Bedingungscodezustand oder die Verwendung eines temporären Registers vermeidet und die statische Codegröße und den dynamischen Befehlsabruf-Trac reduziert. Ein weiterer Punkt ist, dass Vergleiche mit Null eine nicht triviale Schaltungsverzögerung erfordern (insbesondere nach der Umstellung auf statische Logik in fortgeschrittenen Prozessen) und daher fast so teuer sind wie arithmetische Größenvergleiche. Ein weiterer Vorteil eines verschmolzenen Vergleichs- und Verzweigungsbefehls besteht darin, dass Verzweigungen früher im Front-End-Befehlsstrom beobachtet werden und somit früher vorhergesagt werden können. Ein Design mit Bedingungscodes hat vielleicht einen Vorteil, wenn mehrere Verzweigungen auf der Grundlage derselben Bedingungscodes genommen werden können, aber wir glauben, dass dieser Fall relativ selten ist.

Das RISC-V-Dokument trifft den Autor des zitierten Abschnitts nicht. Ich danke allen für ihre Zeit und Überlegung.

Sie möchten oft ein 0-wertiges Register in einer Operation als Quellwert verwenden. Es wäre ein gewisser Overhead, ein Register vor diesen Operationen auf Null zu setzen, daher profitieren Sie von Leistungsvorteilen, wenn Sie einfach eine bereitgestellte Null verwenden können, anstatt sie selbst zu erstellen, wenn eine benötigt wird. Beispiele umfassen das Hinzufügen eines Carry-Flags.
Auf der AVR-Architektur kümmert sich gcc darum, r1 beim Start auf Null zu initialisieren und berührt diesen Wert nie wieder, wobei r1 als Quelle verwendet wird, wenn eine unmittelbare 0 nicht verwendet werden kann. Hier wird das dedizierte Nullregister aus Leistungsgründen vom Compiler in Software „emuliert“. (Die meisten AVRs haben 32 Register, daher kostet es nicht viel, wenn man eines (eigentlich zwei) beiseite legt, im Verhältnis zu den möglichen Vorteilen bei Leistung und Codegröße.)
Ich weiß nichts über MIPS, aber es kann schneller sein, r0 in ein anderes Register zu verschieben, als dieses Register mit XOR zu verknüpfen, um es zu löschen.
Sie sind sich also nicht einig, dass die Null so häufig vorkommt, dass es eine Position in der Registerkartei wert wäre? Dann haben Sie wahrscheinlich Recht, denn es stimmt, dass dies umstritten ist und viele ISAs sich dafür entscheiden, kein Nullregister zu reservieren. Wie andere umstrittene Funktionen zu dieser Zeit wie Registerfenster, Verzweigungsschlitze, Befehlsvorhersage aus "alten Tagen" ... wenn Sie eine ISA entwerfen möchten, müssen Sie sie nicht verwenden, wenn Sie sich dagegen entscheiden.
@ user3528438 Die Verzweigung auf modernen GPUs wird aufgrund der Lockstep-Natur der einzelnen Verarbeitungseinheiten auf eine Weise durchgeführt, die der Anweisungsvorhersage sehr ähnlich ist.
Es kann interessant sein, eines der alten RISC-Papiere von Berkeley zu lesen, RISC I: A Reduced Instruction Set VLSI Computer . Es zeigt, wie die Verwendung eines festverdrahteten Nullregisters, R0, die Implementierung einer Reihe von VAX-Befehlen und Adressierungsmodi in einem einzigen RISC-Befehl ermöglicht.
Ich mag diese Antwort stackoverflow.com/a/32233978/1468850 . Höchstwahrscheinlich ist "Null" überhaupt kein Register , sondern nur ein Multiplex von Erdungskabeln.
@Anonymous Die Prämisse, dass es nützlich ist, wenn ich es nicht oft in der Montage sehe, ist verdächtig. Selbst wenn es nicht als Register implementiert ist, verschwenden Sie immer noch Decodierbits, die mit einem Register verwendet werden könnten.
@b degnan: Haben Sie versucht, in Ihrer Demontage nach Pseudoanweisungen zu suchen?
@Jarhmander Ich habe nach $zero gesucht, indem ich einfach das Flag -s verwendet habe, um die Assembly zu sichern. Ich denke aufgrund der Art des Codes, wo ich nur einmal mit "0" verglichen habe, tatsächlich selten von "Null".
Verwandte StackOverflow-Frage: Wie verbessert ein Nullregister die Leistung?

Antworten (4)

Das Nullregister auf RISC-CPUs ist aus zwei Gründen nützlich:

Es ist eine nützliche Konstante

Abhängig von Einschränkungen der ISA können Sie in einigen Befehlscodierungen kein Literal verwenden, aber Sie können sicher sein, dass Sie r0damit 0 erhalten.

Es kann verwendet werden, um andere Anweisungen zu synthetisieren

Das ist vielleicht der wichtigste Punkt. Als ISA-Designer können Sie ein Allzweckregister gegen ein Nullregister austauschen, um andere nützliche Anweisungen zu synthetisieren. Das Synthetisieren von Anweisungen ist gut, da Sie durch weniger tatsächliche Anweisungen weniger Bits zum Codieren einer Operation in einem Opcode benötigen, wodurch Speicherplatz im Anweisungscodierungsraum freigegeben wird. Sie können diesen Platz verwenden, um zB größere Adress-Offsets und/oder Literale zu haben.

Die Semantik des Nullregisters ist wie /dev/zeroauf *nix-Systemen: Alles, was darauf geschrieben wird, wird verworfen, und Sie lesen immer 0 zurück.

Sehen wir uns einige Beispiele an, wie wir mit Hilfe des Nullregisters Pseudobefehle erstellen können r0:

; ### Hypothetical CPU ###

; Assembler with syntax:
; op rd, rm, rn 
; => rd: destination, rm: 1st operand, rn: 2nd operand
; literal as #lit

; On an CPU architecture with a status register (which contains arithmetic status
; flags), `sub` can be used, with r0 as destination to discard result.
cmp rn, rm     ; => sub r0, rn, rm

; `add` instruction can be used as a `mov` instruction:
mov rd, rm     ; => add rd, rm, r0
mov rd, #lit   ; => add rd, r0, #lit

; Negate:
neg rd, rm     ; => sub rd, r0, rm

; On CPU without status flags,
nop            ; => add r0, r0, r0

; RISC-V's `jal` instruction -- Jump and Link: Jump to PC-relative instruction,
; save return address into rd; we can synthesize a `jmp` instruction out of it.
jmp dest       ; => jal r0, dest

; You can even load from an absolute (direct) address, for a usually small range
; of addresses by using a literal offset as an address.
ld rd, addr    ; => ld rd, [r0, #addr]

Der Fall von MIPS

Ich habe mir den MIPS-Befehlssatz genauer angeschaut. Es gibt eine Handvoll Pseudoanweisungen, die $zero; Sie werden hauptsächlich für Zweige verwendet. Hier sind einige Beispiele von dem, was ich gefunden habe:

move $rt, $rs          => add $rt, $rs, $zero

not $rt, $rs           => nor $rt, $rs, $zero

b Label                => beq $zero, $zero, Label ; a small relative branch

bgt $rs, $rt, Label    => slt $at, $rt, $rs
                          bne $at, $zero, Label

blt $rs, $rt, Label    => slt $at, $rs, $rt
                          bne $at, $zero, Label

bge $rs, $rt, Label    => slt $at, $rs, $rt
                          beq $at, $zero, Label

ble $rs, $rt, Label    => slt $at, $rt, $rs
                          beq $at, $zero, Label

Warum Sie in Ihrer Disassemblierung nur eine Instanz des $zeroRegisters gefunden haben, ist vielleicht Ihr Disassembler schlau genug, um bekannte Befehlssequenzen in ihre äquivalente Pseudo-Anweisung umzuwandeln.

Ist das Nullregister wirklich nützlich?

Nun, anscheinend findet ARM ein Nullregister so nützlich, dass es in ihrem (etwas) neuen ARMv8-A-Kern, der AArch64 implementiert, jetzt ein Nullregister im 64-Bit-Modus gibt; vorher gab es kein nullregister. (Das Register ist jedoch etwas speziell, in einigen Codierungskontexten ist es ein Nullregister, in anderen bezeichnet es stattdessen den Stapelzeiger. )

Ich glaube nicht, dass MIPS Flags verwendet, oder? Das Nullregister fügt die Möglichkeit hinzu, auf bestimmte Adressen ohne Rücksicht auf den Inhalt von CPU-Registern bedingungslos zu lesen/schreiben, und hilft, eine Operation im Stil von "mov-unmittelbar" zu erleichtern, aber andere movs könnten durch logisches Oder-Verknüpfen der Quelle mit sich selbst durchgeführt werden .
Tatsächlich gibt es kein Register, das arithmetische Flags enthält, stattdessen gibt es drei Anweisungen, die dabei helfen, gemeinsame bedingte Verzweigungen ( slt, slti, sltu) zu emulieren.
Wenn ich mir den MIPS-Befehlssatz ansehe und da nach meinem Verständnis jede Anweisung zum Zeitpunkt der Ausführung der vorherigen Anweisung abgerufen wird, frage ich mich, ob es schwierig gewesen wäre, einen Opcode zu haben, der nichts direkt bewirkt, sondern das sagt Wenn ein Direktmodusbefehl ausgeführt wird und der nächste abgerufene Befehl dieses Bitmuster hat, werden die oberen 16 Bits des Operanden aus dem vorab abgerufenen Befehl genommen? Das würde 32-Bit-Immediate-Mode-Operationen mit einem Zwei-Wort-Zwei-Zyklus-Befehl verarbeiten, anstatt zwei Wörter und zwei Zyklen ausgeben zu müssen ...
... Laden eines Operanden und dann ein dritter Zyklus, um ihn tatsächlich zu verwenden.

Die meisten ARM/POWER/SPARC-Implementierungen haben ein verstecktes RAZ-Register

Sie könnten denken, dass ARM32, SPARC usw. kein 0-Register haben, aber tatsächlich haben sie es! Auf der Ebene der Mikroarchitektur fügen die meisten CPU-Entwickler ein 0-Register hinzu, das für die Software möglicherweise unsichtbar ist (das Nullregister von ARM ist unsichtbar), und verwenden dieses Nullregister, um die Befehlsdecodierung zu optimieren.

Stellen Sie sich ein typisches modernes ARM32-Design vor, das über ein unsichtbares Softwareregister verfügt, z. B. R16, das mit 0 verbunden ist. Betrachten Sie die ARM32-Last, viele Fälle von ARM32-Ladeanweisungen fallen in eine dieser Formen (Ignorieren Sie die Prä-Post-Indizierung für eine Weile, um die Diskussion einfach zu halten )...

LDR ra, [rb] // NOTE:The ! is optional and represents address writeback.
LDR ra, [rb, rc](!)
LDR ra, [rb, #k](!)

Innerhalb des Prozessors wird dies zu einem General dekodiert

ldr.uop ra, rb, rx, rc, #c // Internal decoded instruction format.

vor Eintritt in die Ausgabephase, in der die Register gelesen werden. Beachten Sie, dass rx das Register darstellt, um die aktualisierte Adresse zurückzuschreiben. Hier sind einige Dekodierungsbeispiele:

LDR R0, [R1]      ==> ldr.uop R0, R1, R16, R16, #0 // Writeback to NULL. 
LDR R0, [R1, R2]! ==> ldr.uop R0, R1, R1, R2,   #0 // Writeback to R1.
LDR R0, [R1, #2]  ==> ldr.uop R0, R1, R16, R16, #2 // Writeback to NULL.

Auf Schaltungsebene sind alle drei Lasten tatsächlich die gleichen internen Befehle, und eine einfache Möglichkeit, diese Art von Orthogonalität zu erreichen, besteht darin, ein Grundregister R16 zu erstellen. Da R16 immer geerdet ist, decodieren diese Befehle natürlich korrekt ohne zusätzliche Logik. Das Abbilden einer Klasse von Anweisungen auf ein einziges internes Format hilft bei superskalaren Implementierungen sehr, da es die logische Komplexität reduziert.

Ein weiterer Grund ist eine optimierte Methode zum Wegwerfen von Schreibvorgängen. Anweisungen können deaktiviert werden, indem einfach das Zielregister und die Flags auf R16 gesetzt werden. Es besteht keine Notwendigkeit, ein anderes Steuersignal zu erzeugen, um das Zurückschreiben usw. zu deaktivieren.

Die meisten Prozessorimplementierungen, unabhängig von der Architektur, enden früh in der Pipeline mit einem RAZ-Registermodell. Die MIPS-Pipeline beginnt im Wesentlichen an einem Punkt, der in anderen Architekturen ein paar Stufen entfernt wäre.

MIPS hat die richtige Wahl getroffen

Daher ist ein Null-Lese-Register in jeder modernen Prozessorimplementierung fast obligatorisch, und MIPS, das es für Software sichtbar macht, ist definitiv ein Pluspunkt, wenn man bedenkt, wie es die interne Dekodierungslogik rationalisiert. Designer von MIPS-Prozessoren müssen kein zusätzliches RAZ-Register hinzufügen, da $0 bereits auf Masse liegt. Da RAZ dem Assembler zur Verfügung steht, stehen MIPS viele Pseudobefehle zur Verfügung, und man kann sich das so vorstellen, als ob ein Teil der Decodierungslogik an den Assembler selbst weitergegeben wird, anstatt dedizierte Formate für jeden Befehlstyp zu erstellen, um das RAZ-Register vor der Software zu verbergen wie bei anderen Architekturen. Das RAZ-Register ist eine gute Idee und deshalb hat ARMv8 es kopiert.

Wenn ARM32 ein $0-Register hätte, wäre die Dekodierungslogik einfacher geworden und die Architektur wäre in Bezug auf Geschwindigkeit, Fläche und Leistung viel besser gewesen. Beispielsweise würden von den drei oben vorgestellten LDR-Versionen nur 2 Formate benötigt. Ebenso besteht keine Notwendigkeit, Dekodierlogik für die MOV- und MVN-Befehle zu reservieren. Außerdem würden CMP/CMN/TST/TEQ überflüssig werden. Es wäre auch nicht notwendig, zwischen kurzer (MUL) und langer Multiplikation (UMULL/SMULL) zu unterscheiden, da eine kurze Multiplikation als lange Multiplikation betrachtet werden könnte, wenn das obere Register auf $0 gesetzt ist usw.

Da MIPS ursprünglich von einem kleinen Team entworfen wurde, war die Einfachheit des Designs wichtig und daher wurde $0 explizit im Sinne von RISC gewählt. ARM32 behält viele traditionelle CISC-Funktionen auf Architekturebene bei.

Nicht alle ARM32-CPUs funktionieren so, wie Sie es beschreiben. Einige haben eine geringere Leistung für komplexere Ladebefehle und/oder für das Zurückschreiben in das Register. Sie können also nicht alle auf die gleiche Weise decodieren.

Haftungsausschluss: Ich kenne den MIPS-Assembler nicht wirklich, aber das 0-Wert-Register ist nicht einzigartig für diese Architektur, und ich denke, es wird auf die gleiche Weise verwendet wie in anderen mir bekannten RISC-Architekturen.

Das XOR-Verknüpfen eines Registers zum Erhalten von 0 kostet Sie eine Anweisung, während die Verwendung eines vordefinierten 0-Wert-Registers dies nicht tut.

Anweisungen werden beispielsweise mov RX, RYoft als implementiert add RX, RY, R0. Ohne ein 0-Wert-Register müssten Sie xor RZ, RZjedes Mal, wenn Sie verwenden möchten mov.

Ein weiteres Beispiel ist die cmpAnweisung und ihre Varianten (wie "vergleichen und springen", "vergleichen und verschieben" usw.), cmp RX, R0die zum Testen auf negative Zahlen verwendet werden.

Würde es irgendwelche Probleme bei der Implementierung MOV Rx,Ryals geben AND Rx,Ry,Ry?
@supercat Sie können nicht codieren mov RX, Immoder mov RX, mem[RY]wenn Ihr Befehlssatz nur einen einzigen unmittelbaren Wert und einen einzigen Speicherzugriff pro Befehl unterstützt.
Ich bin nicht mit den Adressierungsmodi des MIPS vertraut. Ich weiß, dass der ARM über die Modi [Rx+Ry << scale] und [Rx+disp] verfügt, und obwohl die Verwendung des letzteren für einige absolute Adressen in einigen Fällen nützlich sein könnte, ist dies im Allgemeinen nicht unbedingt erforderlich. Ein gerader [Rx]-Modus könnte über [Rx+disp] unter Verwendung von Nullverschiebung emuliert werden. Was verwendet das MIPS?
movist ein schlechtes Beispiel; Sie könnten es mit einer sofortigen 0 anstelle eines Nullregisters implementieren. zB ori dst, src, 0. Aber ja, Sie bräuchten einen Opcode für mov-immediate, um sich zu registrieren, wenn Sie kein addiu $dst, $zero, 1234, wie luiaber für die unteren 16 Bits anstelle der oberen 16 hätten. Und Sie könnten noroder nicht verwenden sub, um einen Operanden not / neg zu erstellen .
@supercat: falls du dich immer noch wunderst: klassisches MIPS hat nur einen einzigen Adressierungsmodus: register + disp16. Modernes MIPS fügte andere Opcodes für 2-Register-Adressierungsmodi für FP-Laden/Speichern hinzu, wodurch die Array-Indizierung beschleunigt wurde. (Aber immer noch nicht für das Laden/Speichern von Ganzzahlen, möglicherweise weil dies mehr Leseports in der Ganzzahlregisterdatei für 2 Adressregister + ein Datenregister für einen Speicher erfordern könnte. Siehe Verwenden eines Registers als Offset )
@PeterCordes: Wenn es genügend Integer-Ports für add r1,r2,r3gibt, warum nicht mov r1,[r2+r3]? Würden nicht beide Operationen zwei Lese- und eine Schreiboperation erfordern?
@supercat: Ja, dieses Argument hält nur Wasser für Geschäfte, in denen alle 3 Register Eingänge sind. Vielleicht ist es nicht der wahre Grund. Das war nicht der einzige Grund, den BeeOnRope in seiner Antwort vorschlug.
@PeterCordes: Aus irgendeinem Grund hatte ich nicht an Geschäfte gedacht. Ich kann mir vorstellen, dass es als umständlich angesehen wurde, zwei Indexregister für Ladevorgänge zuzulassen, ohne dies auch für Speicher zu tun. Übrigens, obwohl orthogonale Registersätze beliebt sind, mag ich irgendwie die Adress-/Datenaufteilung des 68000. Wenn die Adress- und Datenregisterdateien getrennt wären, würde ein Speicher, der von jedem ein Register nimmt, kein Problem darstellen, und Speicher von Datenregistern könnten ein Inkrementieren und Zurückschreiben der Adresse ohne Portkollisionen aufnehmen.
@supercat, ja, das war meine Überlegung: Eine Ladung ohne einen gleichwertigen Laden wäre manchmal nützlich, aber schwerer zu rechtfertigen und würde für viele Leute "falsch aussehen". Aber bezüglich: 68000 können Sie direkt in/aus Adressregistern laden/speichern. Ich denke, move A0, (0, A1, Xn.SIZE*SCALE)es ist gültig (wobei Xn ein A- oder D-Register ist). nxp.com/files-static/archives/doc/ref_manual/M68000PRM.pdf . Der Quelloperand kann An oder Dn sein, da es zwei getrennte Modi für Register-Direct gibt, einen für D-Regs (Modus=000) und einen für A-Regs (Modus=001). MOVE < ea > , < ea >hat Moduscodes für beide Operationen
Aber ich denke, m68k movekönnte mikrocodiert werden müssen, weil ich denke, dass es Mem-to-Mem-Bewegungen unterstützt, einschließlich der beiden Operanden, die speicherindirekte Adressierungsmodi sind. Das sind also potenziell 2 Ladevorgänge für src und 1 Ladevorgang + 1 Speichervorgang für dst und mehrere Adressberechnungen.
@PeterCordes: Der 68000 macht viele Dinge, die in einem RISC-Design fehl am Platz wären, aber eine geteilte Registerdatei zu haben, scheint eine nützliche Idee zu sein, wenn sie Teil der Befehlsphilosophie wäre (im Gegensatz zum Daumenmodus des ARM wobei die Register 8-12 nur marginalen Nutzen haben).
@supercat: ok richtig, ich bin da vom Weg abgekommen. Ja, interessante Idee. Aber vermutlich möchten Sie in der Lage sein, ein Adressregister zu speichern, ohne dass ALU es zuerst mit einer anderen Anweisung in ein Datenregister kopiert. (Unabhängig davon, dass m68k dies unterstützt.)
@PeterCordes: Wenn die Adress- und Datendateien jeweils zwei Ausgabeports haben, könnten indizierte Speicher aus jeder Datei untergebracht werden, vorausgesetzt, die Adresse wurde durch Kombinieren eines Registers aus jeder Datei gebildet (was das übliche Muster wäre).
@supercat: Ok, das funktioniert, ja, Sie könnten diese Einschränkung in einem ISA-Design sicherlich argumentieren / rechtfertigen. Und es spart Anweisungen für den Fall, dass Sie andernfalls ein A mit einem D-Register hinzufügen müssten, um die Adressberechnung manuell durchzuführen (möglicherweise mit 2 Anweisungen zum Verschieben und Hinzufügen), oder Ihre Registerzuordnung wiederholen.

Es ist billig, ein paar Kabel am Ende Ihrer Registerbank zu erden (billiger, als daraus ein vollwertiges Register zu machen).

Das Ausführen des eigentlichen xor erfordert ein wenig Kraft und Zeit, um die Gates umzuschalten und es dann im Register zu speichern. Warum diese Kosten zahlen, wenn ein vorhandener 0-Wert leicht verfügbar sein kann.

Moderne CPUs haben auch ein (verstecktes) 0-Wert-Register, das sie als Ergebnis einer xor eax eaxAnweisung durch Registerumbenennung verwenden können.

Die wirklichen Kosten R0liegen nicht in der Erdung einiger Drähte, sondern in der Tatsache, dass Sie in jeder Anweisung, die sich mit Registern befasst, einen Code dafür reservieren müssen.
Der xor ist ein Ablenkungsmanöver. xor-zeroing ist nur auf x86 gut, wo CPUs das Idiom erkennen und eine Abhängigkeit von den Eingaben vermeiden. Wie Sie betonen, führt die Sandybridge-Familie nicht einmal eine uop dafür durch, sondern behandelt sie nur in der Registrierungsumbenennungsphase. ( Was ist der beste Weg, um ein Register in der x86-Assembly auf Null zu setzen: xor, mov oder and? ). Aber auf MIPS hätte das XORing eines Registers eine falsche Abhängigkeit; Ordnungsregeln für Speicherabhängigkeiten (HW-Äquivalent von C++ std::memory_order_consume) erfordern XOR, um die Abhängigkeit weiterzugeben.
Wenn Sie kein Nullregister hätten, würden Sie einen Opcode einfügen, um einen Direktwert in ein Register zu verschieben. Wie lui, aber nicht um 16 nach links verschoben. Sie können also immer noch mit einer Anweisung eine kleine Zahl in ein Register schreiben. Nur Null mit einer falschen Abhängigkeit zuzulassen, wäre verrückt. (Normales MIPS erstellt Werte ungleich Null mit addiu $dst, $zero, 1234oder ori, sodass Ihr "Stromkosten" -Argument zusammenbricht. Wenn Sie vermeiden wollten, dass eine ALU gestartet wird, würden Sie einen Opcode für mov-immediate zum Registrieren hinzufügen, anstatt Software ADD oder OR zu haben ein Direktwert mit Null.)