Ich habe in meinem Grundstudium einen Kurs über Compiler besucht, in dem wir einen Compiler geschrieben haben, der Quellprogramme in einer Spielzeug-Java-ähnlichen Sprache zu einer Spielzeug-Assemblersprache kompiliert (für die wir einen Dolmetscher hatten). Im Projekt haben wir einige Annahmen über den Zielcomputer getroffen, die eng mit "echten" nativen ausführbaren Dateien zusammenhängen, darunter:
- ein Laufzeitstapel, der von einem dedizierten Stapelzeigerregister ("SP") verfolgt wird
- Ein Heap für die dynamische Objektzuweisung, der von einem dedizierten Heap-Zeigerregister ("HP") verfolgt wird
- ein dediziertes Programmzählerregister ("PC")
- Der Zielcomputer verfügt über 16 Register
- Operationen an Daten (im Gegensatz zu z. B. Sprüngen) sind Register-zu-Register-Operationen
Als wir zur Einheit kamen, um die Registerzuordnung als Optimierung zu verwenden, fragte ich mich: Was ist die theoretische Mindestanzahl von Registern für eine solche Maschine? Sie können an unseren Annahmen erkennen, dass wir in unserem Compiler fünf Register (SP, HP, PC plus zwei zur Verwendung als Speicher für binäre Operationen) verwendet haben. Während Optimierungen wie die Registerzuweisung sicherlich mehr Register verwenden können, gibt es eine Möglichkeit, mit weniger auszukommen, während Strukturen wie der Stapel und der Heap beibehalten werden? Ich nehme an, dass wir bei der Registeradressierung (Register-zu-Register-Operationen) mindestens zwei Register benötigen, aber brauchen wir mehr als zwei?
quelle
Antworten:
Wenn Sie den direkten Speicherzugriff über die Speicheradresse zulassen, benötigen Sie keine "Register", da Sie stattdessen Speicherorte verwenden können. Zum Beispiel kann der Speicher an Position 0 der Programmzähler sein, an Position 1 haben wir den Stapelzeiger usw. Aber das ist Betrug.
Um zu verhindern, dass wir betrügen, nehmen wir an, dass es keinen direkten Speicherzugriff gibt, da wir feste Speicherorte als Register verwenden könnten. Dann können wir mit zwei Registern davonkommen, einem Programmzähler und einem Stapelzeiger, wie im Wikipedia-Artikel über Stapelmaschinen erläutert . Auf den Stapel kann nur über den Stapelzeiger zugegriffen werden, und auf das Programm kann nur über den Programmzähler zugegriffen werden.
Eine andere Möglichkeit ist die Verwendung von Gegenmaschinen. Eine Maschine mit zwei Zählern ist Turing vollständig, dh sie kann alles berechnen, was Turing kann. Dies wiederum ist in dem Wikipedia - Artikel über schön erklärt Zähler Maschinen .
quelle
Die PIC-Architektur, die in den 1970er Jahren von General Instruments eingeführt wurde und heute noch verwendet wird, hatte die folgenden Register:
Ein typischer Befehl liest ein Register, führt eine Berechnung mit dem Wert read und W durch und speichert das Ergebnis der Berechnung entweder in W oder in dem gelesenen Register. Eine der verfügbaren Berechnungen ergibt "den gelesenen Wert, wobei W ignoriert wird"; Ein anderes ist "nimm W, ignoriere den gelesenen Wert". Die Bitmuster, die "XX lesen, dann W nehmen, den gelesenen Wert ignorieren und das Ergebnis in W speichern" entsprechen würden, werden für NOP sowie eine Vielzahl von speziellen Anweisungen verwendet.
Um Adressberechnungen zu ermöglichen, sucht die Ausführungseinheit des Prozessors nach Anweisungen, die eine Adresse von 00 codieren, und ersetzt die Adresse durch den Inhalt des Dateiauswahlregisters.
Obwohl das Einspeisen aller Werte durch das W-Register ein Engpass sein kann, hat die PIC-Architektur einen größeren Arbeitssatz als andere Architekturen, die das Befehlswort gleicher Länge verwenden. Auf dem PIC16C54 (noch heute hergestellt und den PICs der 1970er Jahre sehr ähnlich) sind die Anweisungen 12 Bit lang. Bei vielen anderen 16Cxx- oder 16Fxx-Teilen sind Befehle 14 Bit lang und können direkt auf einen 128-Byte-Adressraum zugreifen. Wenn der Arbeitssatz eines Programms gut zum Arbeitssatz des Befehlssatzes passt, würde eine Anweisung wie "total + = value", wobei "total" und "value" vom Typ sind
unsigned char
, kompiliert zu:Auf so etwas wie dem ARM wäre der Code eher so, selbst wenn ein Register mit der Basisadresse seiner Variablen vorinstalliert ist:
In vielen Fällen kann ein Compiler das Laden und Speichern bei jeder Operation vermeiden, aber bei so etwas wie dem PIC können die Vorteile des größeren Arbeitssatzes manchmal die Einschränkungen überwiegen, die es mit sich bringt, ständig W durchlaufen zu müssen.
quelle