Universität Karlsruhe (TH) Institut für Innovatives Rechnen und Programmstrukturen (IPD) Übersetzerbau WS 2004/05 Dozent: Prof. Dr.rer.nat. G. Goos Übungsleiter: Rubino Geiß Übungsblatt 7 http://www.info.uni-karlsruhe.de/ [email protected] [email protected] Ausgabe: 25.01.2005 Besprechung: 03.02.2005 Aufgabe 1: Auswertung Boolescher Ausdrücke Boolesche Ausdrücke können oft mit bedingten Sprüngen ausgewertet werden. Dabei muss jeweils die Adresse spezifiziert werden, an der die Berechnung nach dem Sprung fortgesetzt wird. Gegeben sei folgende Syntax Boolescher Ausdrücke: conditional clause boolean expr boolean expr boolean op ::= ::= ::= ::= if boolean expr then stmt list else stmt list end boolean expr boolean op boolean expr not boolean expr and | or Sprungmarken sollen mit Referenzen auf Knoten des Syntaxbaumes spezifiziert werden, für die der Assembler später Adressen einsetzen muss. Die Funktion new label liefert jeweils eine neue Sprungmarke. Geben Sie eine attributierte Grammatik an, die die Auswertung Boolescher Ausdrücke spezifiziert. Aufgabe 2: Codegenerierung für Signalprozessoren Betrachten wir eine Standardfilteroperation: yn := c0 · xn + c1 · xn−1 + c2 · xn−2 + c3 · xn−3 Der Einfachheit wegen ignorieren wir Lade- und Speicheroperationen. Der Berechnungsbaum ist im Folgenden abgebildet: * * * + * + REG * + + REG Angenommen Multiplikation und Addition können gleichzeitig ausgeführt werden und benötigen einen Takt. Mit wievielen Takten kann die Berechnung durchgeführt werden? Aufgabe 3: Codeselektion Der IA-32 Prozessor kennt u.a. die folgenden Befehle (vereinfacht): Übersetzerbau WS 2004/05 Befehl lea dst, (src) mov dst, src add op2, op1 lea op3, (op1,op2) inc op test op1, op2 cmp op1, op2 jne label jmp label label: #int const label S. 2 Bedeutung Berechne Adresse von src und speichere in dst. Kopiere src nach dst. op2 := op1 + op2 op3 := op1 + op2 op := op + 1 Berechnet op1 AND op2 und setzt setzt Conditioncode (CC) Flagbits. Vergleicht op1 mit op2 und setzt die CC Flagbits. Wenn das CC Flagbit zero nicht gesetzt ist, springe zu label, sonst führe nächste Anweisung aus. Wenn das CC Flagbit less gesetzt ist, springe zu label, sonst führe nächste Anweisung aus. Springe zu label Setzt die Codemarke label. jl label eax . . . edx, esi, edi ri (ri ) identifier Übungsblatt 7 Die Operanden können sein: Register, ebp und esp werden für andere Zwecke benötigt. Register eax . . . edi direkt, d.h. der Inhalt des Registers ri wird benutzt. Register indirekt, d.h. der Inhalt des Speichers unter der Adresse Inhalt von ri . Speicher direkt, d.h. identifier steht für die Adresse, an der die Variable identifier gespeichert ist. Integerkonstante mit dem Wert int const. Codemarke. Gegeben sei die folgende 3-Adreßzwischensprache: 1 2 3 4 5 6 7 8 ti ti ti ti ti ti ti ti : : : : : : : : adr identifier load tj const int const plus tj tk less tj tk if tj tk goto tj store tj tk ti := Speicheradresse der Variablen identifier ti := Memory [tj ] ti := int const ti := tj + tk ti := tj < tk Wenn tj <> 0 springe Anweisung tk sonst führe ti+1 aus Springe zur Anweisung tj Memory [tj ] := tk 3.1 on the fly“-Registerzuteilung ” Die Register werden on the fly“ zugeteilt, d.h. wird ein Wert berechnet, muß ein freies“ Register beschafft ” ” werden, das diesen Wert aufnimmt. Nachdem der Wert eines Registers in einer anderen Instruktion benutzt wurde, wird das Register wieder freigegeben. Falls nicht mehr ausreichend viele Register zur Verfügung stehen, wird die Fehlermeldung Expression too complex ausgegeben und das Programm abgebrochen. Die Verwaltung der freien/belegten Register wird durch die folgenden Prozeduren erledigt: PROCEDURE new reg() : INTEGER liefert die Nummer eines freien Registers, PROCEDURE release reg(reg : INTEGER) gibt das Register reg wieder frei. Implementieren Sie die Prozeduren. 3.2 Codeselektion mit AG’s Entwerfen Sie eine Attributgrammatik, die fr diese Zwischensprache IA32-Code erzeugt, und Register zuteilt. Die ti haben die Attribute reg, code und label1 . Der Aufruf der Registerzuteilungsprozeduren kann man sich 1 PROCEDURE new label():tLabel liefert bei jedem Aufruf eine neue Labelkennung. Übersetzerbau WS 2004/05 Übungsblatt 7 S. 3 als Berechnungsvorschriften für ein zusätzliches Attribut vorstellen, das in jedem Knoten berechnet werden muß (vgl. Testen von Bedingungen). 3.3 Codeerzeugung am Beispiel Erzeugen Sie Zwischen- und IA32-Code für die folgenden Quellsprachanweisungen: i := 1, i := i + 1, i := j + k und IF i < j THEN min := i ELSE min := j END. 3.4 Optimierung Geben Sie besseren IA32-Code an. 3.5 Baum Überdeckungen Wie könnte dieser Code erzeugt werden? Beschreiben Sie Ihr Verfahren. (Hinweis: Betrachten Sie eine andere Darstellungsform für die Liste der 3-Adreßanweisungen, z.B.: Jede Anweisung ist ein Knoten in einem Baum, die ti sind Kanten zwischen diesen Knoten; oder: Jede Anweisung ist ein Termkonstruktor, die ti repräsentieren entsprechende Subterme. Anstatt nun für jede Anweisung einzeln Code zu erzeugen (Makroexpansion) könnte nun Code für einen komplexeren Teilbaum / Subterm erzeugt werden, vgl. Patternmatching auf Bäumen / Termen.) 3.6 delayed branch“ ” Ältere RISC-Prozessoren, deren Instruktionen in einer Pipeline ausgeführt werden, kennen sogenannte delayed branch Instruktionen. Bei ihnen wird die ihnen folgende Instruktion ausgeführt, bevor der eigentliche Sprung erfolgt. Z.B. wird bei der Instruktionsfolge: jmp lab; mov r1, (r2); vor dem Sprung nach lab noch die mov Instruktion ausgeführt. Man sagt, daß die mov Instruktion im delay slot des verzögerten Sprunges steht. Angenommen, der IA-32 hätte delayed branch Instruktionen und einen Befehl nop, der nichts macht. 1. Was ist bei der Codeerzeugung zu beachten? 2. Wann kann eine Instruktion, die ursprünglich vor dem normalen“ Sprung ausgeführt werden soll, in das ” delay slot eines verzögerten Sprunges verschoben werden? 3. Kann auch eine Instruktion nach vorne verschoben werden, die ursprünglich nach dem normalen“ Sprung ” ausgeführt werden sollte? 4. Es gibt allerdings nicht nur verzögerte Sprünge sondern, z.B. auch verzögerte Speicherladebefehle, deren Ergebnis erst in der übernächsten Instruktion verwendbar ist. (Der Grund liegt in dem im Vergleich zum Prozessor langsameren Speicher.) Was ist hierbei zu beachten? 5. Neuere Architekturen verwenden spekulative Auswertung, bei denen ein Zweig als bevorzugt angenommen wird. Was muß hierbei beachtet werden? Aufgabe 4: Auswertung von Ausdrücken / lokale Registerzuteilung Gegeben seien die folgenden Maschinenbefehle: Befehl Bedeutung ld offset, ri ri := Memory [offset] st offset, ri Memory [offset] := ri add ri , rj ri := ri + rj ri seien Register 4.1 Codeerzeugung Erzeugen Sie für folgende Ausdrücke Code: Übersetzerbau WS 2004/05 Übungsblatt 7 S. 4 • (a1 + (a2 + (a3 + (a4 + a5)))) und • ((((a1 + a2) + a3) + a4) + a5) sowie • ((((a1 + a2) + a3) + a4) + (a5 + (a6 + (a7 + a8)))). Verwenden Sie zuerst das Verfahren: zuerst linken, dann rechten Ausdruck auswerten, dann das Verfahren zuerst rechten, dann linken Ausdruck auswerten. Wieviele Register benötigen Sie? 4.2 Optimale Registerzuteilung Gibt es ein besseres Verfahren? (Hinweis: Die Auswertungsreihenfolge wird abhängig vom Ausdruck bestimmt.) Wann kann dieses Verfahren angewandt werden, welche Probleme können entstehen? (Siehe [Waite/Goos, 10.2.1]) 4.3 Lösung als AG Geben Sie für Ihr Verfahren eine Attributgrammatik an, die die Anzahl der benötigten Register und die Auswertungsreihenfolge der Operanden bestimmt. Betrachten Sie nullstellige, unäre und binäre Operatoren: Expr ::= id | const. Expr ::= op Expr. Expr ::= Expr op Expr. 4.4 INMOS Der Transputer [INMOS, The transputer instruction set - a compiler writer’s guide, Prentice Hall 1988] hat anstatt eines Registersatzes, bei dem auf jedes Register direkt zugegriffen werden kann, einen Auswertungskeller der Tiefe drei. Jedes Kellerelement hat einen Namen: Name A B C Position im Keller top bottom Jeder Prozedur ist eine Prozedurschachtel zugeordnet, in der Variablen gespeichert werden. Diese werden relativ zum WSP (Workspace Pointer) adressiert. Der Transputer kennt u.a. die folgenden Befehle: Befehl LDL offset STL offset LDC int const ADD SUB REV Bedeutung C := B; B := A; A := Memory [WSP + offset]; Memory [WSP + offset] := A; A := B; B := C; C := undefiniert; C := B; B := A; A := int const; A := B + A; B := C; C := undefiniert; A := B - A; B := C; C := undefiniert; A,B := B,A; C := C; (vertauscht die Werte von A und B) Entwerfen Sie ein Schema zur Codeerzeugung, das Spillcode einfügt. Spillcode sind Instruktionen, die eingefügt werden, um Ausdrücke, für deren Berechnung eigentlich mehr als die verfügbaren Register benötigt werden, dennoch auswerten zu können. Zum Beispiel: STL/LDL um Zwischenergebnisse abzuspeichern und wieder in Register zu laden. Die Kostenfunktion, die der Anzahl der Instruktionen entspricht, gilt es zu minimieren. Betrachten Sie die Grammatik aus Aufgabe 2.3. Die Konvention sei, daß das Ergebnis (d.h. der Wert von Expr0 ) in A erwartet wird. Der Operator op erwartet seine Operanden in den Registern wie folgt: Regel Expr0 ::= op Expr1 . Expr0 ::= Expr1 Op Expr2 . Konvention Der Wert von Expr1 in A. Der Wert von Expr1 in B;Expr2 in A.