Zwischencode-Erzeugung im Rahmen des Seminars "Übersetzung von künstlichen Sprachen" Sebastian Hanneken 2. Juni 2009 1 / 32
1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung von 3AC Deklarationen Ausdrücke Arrayzugriffe Boolesche Ausdrücke If-Else - Anweisung Switchanweisung 3 Java Bytecode 4 Zusammenfassung 2 / 32
Einleitung 1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung von 3AC Deklarationen Ausdrücke Arrayzugriffe Boolesche Ausdrücke If-Else - Anweisung Switchanweisung 3 Java Bytecode 4 Zusammenfassung 3 / 32
C++ Itanium C++ Itanium Einleitung Einordnung Einordnung in den Compile-Prozess Quell- Programm Lexer (Lexikalische Analyse) Parser (Syntaktische Anlayse) Statische Überprüfung Zwischencode Zwischencodeerzeugung Codeerzeugung front end back end Explizite Zwischendarstellung (= Zwischencode) am Ende des "front end" Eigenschaften des Zwischencodes: Bezeichner Typ Offset arr array(2, integer) 0 rechteck record 12 höhe float 20 Spiegelt das Quellprogramm exakt wieder Bezeichner Typ Offset Effiziente Erstellung aus dem Quellprogramm länge integer muss möglich 0 sein breite integer 4 Einfache Transformation in Maschinencode If - Schleife If-Else - Schleife While - Schleife 4 / 32
Einleitung Funktion von Zwischencode Reduktion der benötigten Beziehungen n n m m n n m m * * + + Quellsprache Quellsprache 1 1 HW-Plattform HW-Plattform 1 1Quellsprache Quellsprache 1 1 HW-Plattform HW-Plattform 1 1 Quellsprache Quellsprache 2 2 Quellsprache Quellsprache 3 3 HW-Plattform HW-Plattform 2 2Quellsprache Quellsprache 2 2 ZC HW-Plattform HW-Plattform 3 3Quellsprache Quellsprache 3 3 HW-Plattform HW-Plattform 2 2 ZC HW-Plattform HW-Plattform 3 3 Quellsprache Quellsprache 4 4 HW-Plattform HW-Plattform 4 4Quellsprache Quellsprache 4 4 HW-Plattform HW-Plattform 4 4 Um jede Quellsprache (n) in jeden Maschinencode (m) übersetzen zu können, benötigt man n m Compiler Zwischencode als Schaltwerk zwischen "front end" und "back end"! Reduktion der benötigten Compiler auf n + m 5 / 32
Einleitung 3-Adresscode (3AC) Bausteine des 3AC Linearisierte Form eines Syntaxbaumes Besteht aus den beiden Konzepten Adressen und Instruktionen Adressen: Bezeichner des Quellprogramms Konstanten Compiler generierte temporäre Bezeichner Nur ein arithmetischer/logischer Operator (op) bzw. relationaler Operator (relop) pro Instruktion! Welche Instruktionen stehen beim 3AC zur Verfügung? 6 / 32
Einleitung 3-Adresscode (3AC) Instruktionen des 3AC x = y op z x = op y x = y goto L if x goto L bzw. iffalse x goto L if x relop y goto L x = y[i] bzw. x[i] = y Zuweisungsinstruktionen (x, y und z sind Adressen) Zuweisung (op ist unär) Kopierinstruktionen Unbedingter Sprung. Instruktion mit der Marke L wird als Nächstes ausgeführt Bedingte Sprünge, die in Abhängigkeit von der Bedingung x zur Instruktion L springen Bedingte Sprünge (in Abhängigkeit der Bedingung x relop y) Indizierte Kopierinstruktionen 7 / 32
1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung von 3AC Deklarationen Ausdrücke Arrayzugriffe Boolesche Ausdrücke If-Else - Anweisung Switchanweisung 3 Java Bytecode 4 Zusammenfassung 8 / 32
Deklarationen 1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung von 3AC Deklarationen Ausdrücke Arrayzugriffe Boolesche Ausdrücke If-Else - Anweisung Switchanweisung 3 Java Bytecode 4 Zusammenfassung 9 / 32
Deklarationen Handhabung von Deklarationen Zuweisung des Speicherplatzes für Deklarationen erfolgt zur Laufzeit. ABER: Vorbereitung mithilfe relativer Adressen Speicherung des Bezeichners, des Typs und der relativen Adresse in der Symboltabelle Behandlung von: Basistypen! fixe Größe (z. B. Integer = 4) Arrays! Größe eines Elementes Anzahl Elemente Records (bilden komplexe Strukturen ab) Enthält Deklarationen Eigene Symboltabelle Deklarationen werden analysiert. Informationen werden für nachgelagerte Prozesse in der Symboltabelle gespeichert. 10 / 32
Deklarationen Deklarationen - Beispiel int[3] arr; record { int länge; int breite;} rechteck; float höhe; Symboltabellen: Bezeichner Typ Offset arr array(3, integer) 0 rechteck record 12 höhe float 20 Bezeichner Typ Offset länge integer 0 breite integer 4 Problematisch: Feste Größen der Basiswerte Lösung: Symbolische Typgrößen, welche im "back end" ersetzt werden 11 / 32
Ausdrücke 1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung von 3AC Deklarationen Ausdrücke Arrayzugriffe Boolesche Ausdrücke If-Else - Anweisung Switchanweisung 3 Java Bytecode 4 Zusammenfassung 12 / 32
Ausdrücke Arrayzugriffe Elemente des Arrays werden zeilenweise in aufeinander folgenden Speicherbereichen hinterlegt (A[0; 0] A[0; 1] A[n; m]) Adresse eines Elementes A[i 1 ][i 2 ] [i k ] wird berechnet durch base + i 1 w 1 + i 2 w 2 + + i k w k Übersetzung in indizierte Kopierinstruktionen x = y[i] y ist die Adresse des ersten Elements (vgl. relative Adresse) i ist die Position des Elements im Speicherbereich des Arrays 13 / 32
Ausdrücke Arrayzugriffe - Beispiel Array: 30 34 38 42 46 50 54 58 62 66 70 74 Zugriff auf. arr[i][j] w 1 = 16 w 2 = 4 3AC =) t 1 = i 16 t 2 = j 4 t 3 = t 1 + t 2 t 4 = arr[t 3 ] 14 / 32
Ausdrücke Übersetzung von booleschen Ausdrücken Anwendung von booleschen Operatoren (&& (UND), k (ODER),! (NICHT)) auf boolesche Variablen oder relationale Ausdrücke der Form "E 1 rel E 2 "! rel ist Vergleichsoperator (<; ; =;!=; ; >) Funktionen: Ermittlung eines expliziten Wertes Steuerung des Kontrollflusses In vielen Fällen muss nicht der gesamte boolesche Ausdruck ausgewertet werden (vgl. B 1 && B 2 ) Wie kann 3AC zur Steuerung des Kontrollflusses erzeugt werden? 15 / 32
Ausdrücke Boolesches UND - Umsetzung Boolescher Ausdruck (B) true false true false B.true B.false Nächste Instruktion wird in Abhängigkeit des Wahrheitsgehalts bestimmt (Sprungmarken B.true bzw. B.false) Zwischencode wird erzeugt durch B 1.code k label(b 1.true) k B 2.code 16 / 32
1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung von 3AC Deklarationen Ausdrücke Arrayzugriffe Boolesche Ausdrücke If-Else - Anweisung Switchanweisung 3 Java Bytecode 4 Zusammenfassung 17 / 32
If-Else - Anweisung if(länge > 5 && breite < 3) höhe = 6 else höhe = 3 If-Else While if länge > 5 goto L 1 goto L 3 L 1 : if breite < 3 goto L 2... goto L 3 L 2 : höhe = 6... goto L 4 L 3 : höhe = 3 L 4 : Reduktion von goto-instruktionen? 18 / 32
Reduktion von goto-instruktionen Instruktionen des 3AC werden sequenziell abgearbeitet Einsparung von Instruktionen möglich, dieses führt zu: Reduktion des benötigten Speicherplatzes Optimierung des Laufzeitverhaltens if länge > 5 goto L 1 goto L 3 L 1 : if breite < 3 goto L 2 goto L 3 L 2 : höhe = 6 goto L 4 L 3 : höhe = 3 L 4 : iffalse länge > 5 goto L 2 iffalse breite < 3 goto L 2 höhe = 6 goto L 1 L 2 : höhe = 3 L 1 : 19 / 32
Reduktion von goto-instruktionen - Umsetzung Umsetzung erfolgt in 2 Schritten 1. Sprungmarke fall einfügen! erzeugt keinen Sprung 2. Auswahl der Instruktion anhand dieser Sprungmarken if B.true 6= fall and B.false 6= fall then gen( if test goto B.true) k gen( goto B.false) else if B.true 6= fall then gen( if test goto B.true) else if B.false 6= fall then gen( iffalse test goto B.false) 20 / 32
Backpatching - Notwendigkeit Ziel: Übersetzung in einem Durchgang (Bsp.: B! B 1 && B 2 )! Sprünge müssen generiert werden, wenn deren Ziele noch unbekannt sind?... Zwischencode kann nicht in einem Durchgang erstellt werden Aber: Lösungsansatz durch Backpatching 21 / 32
Backpatching - Umsetzung Sprungmarken B.true und B.false werden durch synthetisierte Listen B.truelist bzw. B.falselist ersetzt Listen enthalten Indizes der goto-instruktionen bei denen B.true bzw. B.false eingetragen werden muss Erzeugung von unvollständigen Sprüngen Namensgebende Funktion backpatch(l 1, x) fügt x als Sprungziel der goto-instruktionen aus l 1 ein Marker-Nichtterminale speichern die Indizes der benötigten Instruktionen B! B 1 && MB 2 22 / 32
Backpatching - Beispiel B! B 1 && MB 2 länge > 5 && breite < 3 00 : if laenge > 5 goto _ 01 : goto _ 02 : if breite < 3 goto _ 03 : goto _ M = 02 B 1.truelist = {00} backpatch({00}, 02) =) 00 : if laenge > 5 goto 02 01 : goto _ 02 : if breite < 3 goto _ 03 : goto _ 23 / 32
Übersetzung einer Switchanweisung t 1 = länge switch (länge) { case 5: höhe = 4 case 7: höhe = 8 default: höhe = 6 } 3AC =) goto test L 1 : höhe = 4 goto next L 2 : höhe = 8 goto next L 3 : höhe = 6 goto next test : if t 1 = 5 goto L 1 if t 1 = 7 goto L 2 next : goto L 3 24 / 32
Switchanweisung - Variationen Überprüfungen der Fälle zu Beginn! Nachteil, dass Sprungziele noch nicht bekannt sind Ersetzung von if t = V i goto L i durch case t V i L i! Gesonderte Behandlung während der Codeerzeugung einfacher Optimierung, wenn Fälle in kleinem Intervall [min; max]! Dynamische Ermittlung der Sprungziele! Intervall muss komplett gefüllt sein min = 5 max = 7 L 1 L 2 25 / 32
Java Bytecode 1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung von 3AC Deklarationen Ausdrücke Arrayzugriffe Boolesche Ausdrücke If-Else - Anweisung Switchanweisung 3 Java Bytecode 4 Zusammenfassung 26 / 32
Java Bytecode Java Bytecode Compile-Zeit Quellcode Compiler Bytecodes Laufzeitumgebung (Java Runtime Enviroment) Hybrider Compiler! Compiler + Interpreter Bytecode hat die Form <index>: <opcode> [<operand1> [<operand2> ]] Jeder opcode wird durch 1 Byte repräsentiert (theoretisch 256 Befehle möglich) Bytecode ist stackorientiert (Operanden werden auf dem Stack erwartet - Ergebnisse werden hierhin zurückgeschrieben) 27 / 32
Java Bytecode Java Bytecode - Beispiel public static void bestimmehöhe(int länge, int breite)f int höhe; if (länge > 5 && breite < 3) höhe = 6; else höhe = 3; return höhe;g 0 : iload_0 1 : iconst_5 2 : if_icmple 16 5 : iload_1 6 : iconst_3 7 : if_icmpge 16 10 : bipush 6 12 : istore_2 13 : goto 18 16 : iconst_3 17 : istore_2 18 : iload_2 19 : ireturn 28 / 32
Java Bytecode Vergleich 3AC - Bytecode Bytecodes ist eine komprimierte Form des 3AC, bei der Operanden implizit auf dem Stack erwartet werden ABER: Umfang des Zwischencodes ist erheblich größer: Beispiel Switch: Befehle für die fallgerichtete Übersetzung (lookupswitch) bzw. die dynamische Berechnung (tableswitch) Abbildung von objektorientierten Features Fazit: Die Programmiersprache Java, inkl. des Bytecodes setzt die beschriebene Funktion zur Reduzierung der Anzahl der benötigten Compiler gut um. 29 / 32
Zusammenfassung 1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung von 3AC Deklarationen Ausdrücke Arrayzugriffe Boolesche Ausdrücke If-Else - Anweisung Switchanweisung 3 Java Bytecode 4 Zusammenfassung 30 / 32
Zusammenfassung Zusammenfassung Zwischencode ist eine wichtige Phase der Compilierung Ziel: Reduktion der Anzahl der benötigten Compiler Umsetzung wichtiger Konstrukte einer Programmiersprache möglich Java Bytecode ist ein Beispiel für eine gelungene Umsetzung der Anforderungen an einen Zwischencode Zwischencode eignet sich für Optimierungen und als Ausgangspunkt für die Codeerzeugung 31 / 32
Zusammenfassung Vielen Dank! Vielen Dank für die Aufmerksamkeit! Noch Fragen? 32 / 32