Superskalare Prozessorarchitekturen

Programmdarstellung, Abhängigkeiten und parallele Ausführung

Die Programmierung eines Rechners kann man typischerweise in die Erstellung des Sourcecodes in einer Hochsprache, die Übersetzung in Assembler und Binärcode sowie die weiteren Vorgänge unterteilen. Der Linker-Lauf ist in diesem Zusammenhang nicht von Interesse, da er im Wesentlichen nur Adressen setzt. Das Bild zeigt eine kurze C-Routine aus einem Sortierprogramm sowie die Übersetzung in einen nicht optimierten Assembler-Code.

Diese Übersetzung zeigt sehr deutlich die Kontrollflussabhängigkeit dieser Architekturen: Der Prozessor erreicht jeden Befehl, indem er den vorhergehenden ausführt (PC Increment) oder indem ein Sprungbefehl auf die Codespeicherstelle zeigt (PC Update). Beide Kontrollflussabhängigkeiten muss die CPU bei der parallelen Ausführung von Instruktionen berücksichtigen.

Zunächst ist das Assembler-Programm im Bildteil (b) in drei Basisblöcke aufgeteilt:

Dabei gilt: Ein Basisblock ist die maximale Anzahl von Instruktionen (um eine gewählte Stelle) mit exakt einem Eintritts- und einem Austrittspunkt.

Ein Basisblock entspricht damit der um den gewählten Punkt maximalen Folge von Instruktionen ohne Verzweigungsbefehl (Ausnahme: letzter Befehl) und ohne Sprungmarke (Ausnahme: am ersten Befehl). Dieser Block wird immer vollständig durchlaufen, falls das Programm seinen Anfang erreicht. Somit kann die CPU einen Basisblock prinzipiell in paralleler Weise ausführen. Der Basisblock ist damit die Größe im Befehlsfluss, auf die sich Compiler und superskalare Architekturen konzentrieren.

Weitere Parallelität entsteht durch die Zusammenfassung mehrerer Basisblöcke, etwa durch die spekulative Ausführung von Branch-Befehlen. Trifft die Ausführungsannahme nicht zu, muss der Prozessor alle Ergebnisse und die InstruktionsPipeline entsprechend korrigieren. Zwischen der superskalaren Rechnerarchitektur und dem Compiler-Bau (insbesondere Codegenerator und Optimierer) besteht daher eine erhebliche Wechselwirkung. Kapitel 6 erläutert diese Abhängigkeiten näher. Innerhalb eines Basisblocks existieren jedoch auch Datenabhängigkeiten, die wie im Fall der RISC-CPU weiter zu untersuchen sind. Diese Datenabhängigkeiten bestehen zwischen verschiedenen Instruktionen, falls diese auf die gleichen Speicherstellen beziehungsweise Register lesend oder schreibend zugreifen.