40
Vorlesung Compilertechnik Sommersemester 2008 Zwischencodeerzeugung M. Schölzel

Vorlesung Compilertechnik Sommersemester 2008

  • Upload
    oke

  • View
    22

  • Download
    0

Embed Size (px)

DESCRIPTION

Vorlesung Compilertechnik Sommersemester 2008. Zwischencodeerzeugung M. Schölzel. Aufgabe der Zwischencodeerzeugung. Bereitstellung einer Schnittstelle zum Backend des Compilers. Generierung einer oder mehrerer Zwischenrepräsentation für das Quellprogramm, so dass: - PowerPoint PPT Presentation

Citation preview

Page 1: Vorlesung Compilertechnik Sommersemester 2008

Vorlesung CompilertechnikSommersemester 2008

Zwischencodeerzeugung

M. Schölzel

Page 2: Vorlesung Compilertechnik Sommersemester 2008

2

Aufgabe der Zwischencodeerzeugung

Bereitstellung einer Schnittstelle zum Backend des Compilers.

Generierung einer oder mehrerer Zwischenrepräsentation für das Quellprogramm, so dass:

alle erforderlichen Informationen erhalten bleiben, die benötigten Informationen auf eine geeignete Weise für

den jeweiligen Zweck dargestellt sind, keine oder wenig zielcodespezifische Informationen enthalten

sind, sich die Darstellung der Informationen gut in Zielcode/die

nächste Zwischencoderepräsentation übersetzen lässt. Art der Zwischenrepräsentation hängt stark vom

jeweiligen Zweck ab und kann damit stark variieren.

Page 3: Vorlesung Compilertechnik Sommersemester 2008

3

Beispiele für oft genutzte Arten der Zwischencodedarstellung

C (erster C++ Compiler, Bison, Flex): Darstellung mehrerer Module, jedes davon besteht aus Funktionen.

Syntaxbaum: In der Regel Darstellung eines Moduls mit mehreren Funktionen.

Aufrufgraph: Darstellung von Funktionen und der Aufrufbeziehungen.

Steuerflussgraph: Repräsentation des Steuerflusses innerhalb einer Funktion.

CDFG: Repräsentation einer Funktion und der Datenabhängigkeiten.

DAGs: Repräsentation von Datenabhängigkeiten ohne Steuerfluss.

3-Adress-Code: Repräsentation mehrerer Funktionen. SSA-Code: Repräsentation mehrere Funktionen. …

Page 4: Vorlesung Compilertechnik Sommersemester 2008

4

Einbettung der Zwischencodeerzeugung in den Compiler

Da der Syntaxbaum schon eine Zwischencodeart darstellt, ist die Erzeugung des Syntaxbaums bereits Teil der Zwischencodeerzeugung.

Dieser kann in mehreren Schritten vereinfacht werden.

Die Darstellung des Zwischencodes nähert sich dabei immer mehr dem gewünschten Zielcode an.

Parser

Syntaxbaum 3-Adress-Code

t2 := t1 + t0t3 := at4 := t2 * t3 …

Zielcode-erzeugung

AbstraktionsebeneHoch Niedrig

Zwischencodeerzeugung

Page 5: Vorlesung Compilertechnik Sommersemester 2008

5

3-Adress-Code

Folge von 3-Adress-Code-Anweisungen. Anweisungsarten:

Binäranweisungen: x := y z Unäranweisungen: x := y Kopieranweisungen: x:=y, x:=k, @x:=y, x:=@y, x:=&y Sprunglabel: Label: Funktionslabel: Function Label: Unbedingte Sprünge: goto index Bedingte Sprünge: if x then Label Funktionsaufrufe: x := call FLabel(y1,…,yn) Funktionsbeendigung: return x, return Castoperationen: x := (Type) y

Dabei sind: k Konstante, x, y, yi und z Variablen, wobei Variablen unterschieden werden in:

Programmvariablen (lokal, global) Temporäre Variablen (immer lokal)

Page 6: Vorlesung Compilertechnik Sommersemester 2008

6

Beispiel: 3-Adress-Code

int f(int n){ int fak = 1; while(n > 0) { fak = fak * n; n = n – 1; } return fak;}

Function f: t0 := 1 fak = t0while_0_cond: t1 := n t2 := 0 t3 := t1 > t2 t4 := not t3 if t4 then while_0_end t5 := fak t6 := n t7 := t5 * t6 fak := t7 t8 := n t9 := 1 t10 := t8 – t9 n := t10 goto while_0_condwhile_0_end: t11 := fak return t11

Page 7: Vorlesung Compilertechnik Sommersemester 2008

7

Speichermodell

Temporäre Variablen repräsentieren Werte, die bevorzugt in Prozessorregistern gehalten werden.

Programmvariablen repräsentieren Werte, die im Hauptspeicher gehalten werden und damit auch eine Speicheradresse besitzen.

Es wird unterschieden zwischen Programmvariablen mit:

einer absoluten Adresse (global), einer relativen Adresse (lokal).

Parametern einer Funktion und lokalen Variablen einer Funktion, die nicht Parameter sind.

Absolute und relative Adressen (möglicherweise als virtuelle Adresse) werden bereits während der Zwischencodeerzeugung festgelegt und zu jeder Variablen gespeichert.

Page 8: Vorlesung Compilertechnik Sommersemester 2008

8

Modell für die Zwischencodeerzeugung

Die LR(1)-Grammatik der Quellsprache wird für die Zwischencodeerzeugung zu einer attributierten Grammatiken erweitert:

S-attributierte Grammatik zum Aufbau des Syntaxbaums: Jedes Attributvorkommen in einer Regel A0 A1…An speichert die

Wurzel des Syntaxbaums, der während der Analyse zu der Ableitung Ai * gehört.

Der LR-Parser kann diese Attribute während der Analyse direkt auswerten und berechnet somit den Syntaxbaum.

S- oder L-attributierte Grammatik zur Erzeugung von 3-Adress-Code:

Jeder Knoten erhält ein Attribut, das ein Zwischencodefragment speichern kann.

Durch geeignete semantische Funktionen werden diesen Attributen Werte zugewiesen.

Konsequenz: An Syntaxbaumknoten, an denen dieselbe Grammatik-regel angewendet wurde, wird auch dieselbe Aktion ausgeführt.

Weitere Annahme: Hierarchisch organisierte Symboltabellen sind bereits erzeugt.

Page 9: Vorlesung Compilertechnik Sommersemester 2008

9

Prinzipien bei der Übersetzung eines Syntaxbaums in 3-Adress-Code

Anweisungen ändern Speicherzustände oder den Steuerfluss: Übersetzung in entsprechende Zwischencodebefehle.

Übersetzung von Ausdrücken aus der Quellsprache: Bedeutung ist ein Wert. Erzeugung von Zwischencode mit derselben Bedeutung. Berechnung des Wertes in eine temporäre Variable.

Ausdrucksanweisungen ändern Speicherzustände und haben einen Wert als Bedeutung.

Syntaxbaumknoten, die zu einem Ausdruck gehören können, speichern neben dem Zwischencodefragment auch die temporäre Variable, in die der Zwischencode den Wert berechnet.

Attribute für Syntaxbaumknoten, die zu einem Ausdruck gehören können:

(ir,t): ir Speichert den 3-Adress-Code und t die Zwischencodevariable, in die durch ir der Wert des Ausdrucks berechnet wird.

iVal, fVal sind synthetisiertes Attribute an INTLIT bzw. FLOATLIT, deren Wert durch den Scanner gesetzt wird.

Bereits vorhanden: t speichert den Typ des Ausdrucks.

Page 10: Vorlesung Compilertechnik Sommersemester 2008

10

Hilfsfunktionen, für die Übersetzung in 3-Adress-Code

getNextTemp(): liefert einen neuen, noch nicht benutzten Namen für eine temporäre Variable zurück.

getNextWhile(): liefert eine neue, noch nicht benutzten Nummer für eine while-Schleife zurück.

getNextIf(): liefert eine neue, noch nicht benutzten Nummer für eine if-Anweisunge zurück.

uniqueName(v): Liefert für die Programmvariable v ihren eindeutigen Namen durch Suchen in den hierarchisch organisierten Symboltabellen. Eindeutiger Name entsteht z.B. durch Erweiterung des Variablennamens mit der lexikografischen Nummer der Symboltabelle.

nextGlobalAdress: Nächste freie globale Adresse (relativ zu einer virtuellen Basis). Kann bereits beim Aufbau der globalen Symboltabelle festgelegt werden.

nextLocalAdress: Nächste freie lokale Adresse (relativ zu einer virtuellen Basisadresse). Kann lokal für jede Symboltabelle zu einer Funktion festgelegt werden.

Page 11: Vorlesung Compilertechnik Sommersemester 2008

11

Übersetzung von Zuweisungen und Ausdrücken

ir[Expr,6],0 := ("t:=a", t), wobei t := getNextTemp() und a = uniqueName(id1)

ir[Expr,8],0 := ("t:=iVal1", t), wobei t := getNextTemp() ir[Expr,9],0 := ("t:=fVal1", t), wobei t := getNextTemp() ir[Expr,7],0 := (ir "t:=(type)t'", t), wobei t := getNextTemp(),

ir4 = (ir,t'), type = t2 ir[Expr,5],0 := ir2 ir[Expr,1],0 := (irl irr "t:=tl + tr", t), wobei t := getNextTemp(),

ir1 = (irl,tl) und ir3 = (irr,tr) ir[Expr,2],0 := (irl irr "t:=tl - tr", t), wobei t := getNextTemp(),

ir1 = (irl,tl) und ir3 = (irr,tr) ir[Expr,3],0 := (irl irr "t:=tl * tr", t), wobei t := getNextTemp(),

ir1 = (irl,tl) und ir3 = (irr,tr) ir[Expr,4],0 := (irl irr "t:=tl / tr", t), wobei t := getNextTemp(),

ir1 = (irl,tl) und ir3 = (irr,tr) id[LVal,1],0 := id1 ir[Assign,1],0 := ir " t:=t' ", wobei t = uniqueName(id1),

ir3 = (ir,t')

Page 12: Vorlesung Compilertechnik Sommersemester 2008

12

Beispiel

Expr

Expr

Expr

Expr

ExprIDENT

INTLIT IDENT

Assign

id=a

iVal=2 id=b

ir=(t0:=a$0,t0)

ir=(t1:=2,t1)

ir=( t1:=2 t2:=b$01 t3:=t1*t2,t3)

ir=(t2:=b$01,t2)

ir=( t0:=a$0t1:=2 t2:=b$01 t3:=t1*t2t4:=t0+t3,t4)

IDENTid=c

LValid=c$01

ir= t0:=a$0t1:=2 t2:=b$01 t3:=t1*t2t4:=t0+t3c$01:=t4

{ int a; … { int b,c; … c := a+2*b; … } …}

Quelltextfragment:

Symtab0

Syntaxbaum für den Ausdruck c := a+2*b:

Name UniquName

Typ Scope Adresse

a a$0 int lokal 0

Name UniquName

Typ Scope Adresse

b b$01 int lokal -4

c c$01 int lokal -8

Symtab0.1

Page 13: Vorlesung Compilertechnik Sommersemester 2008

13

Übersetzung von Anweisungsfolgen

ir[Stmt,1] := "BlBegin_i:" ir1 "BlEnde_i", wobei i =

getNextBlock(). Einfügen von Labeln, um an den Blockgrenzen auch

Basisblöcke zu abzuschließen. Für die übrigen Alternativen 2,…,k zu Stmt:

ir[Stmt,k] := ir1, für 1 < k. ir[StmtL,1] := ir1 ir3

ir[StmtL,2] := ir[Block,1] := ir3

ir[Program,1] := ir1

Page 14: Vorlesung Compilertechnik Sommersemester 2008

14

Beispiel

Program ir= 1 BlBegin_1: 2122 BlEnd_1:34

Syntaxbaum für das Programm:{ Stmt1; { Stmt21; Stmt12; }; Stmt3; Stmt4;}

Block

{ DeclL StmtL }

Stmt1 ; StmtL

Stmt2 ; StmtL

Block

Stmt21 ; StmtL

{ DeclL StmtL }

Stmt22 ; StmtL

Stmt3 ; StmtL

Stmt4 ; StmtL

ir=22

ir=22ir=21

ir=2122

ir=4

ir=4

ir=34

ir=3ir=2122

ir=BlBegin_1: 2122 BlEnd_1:

ir=1

ir=BlBegin_1: 2122 BlEnd_1:34

ir= 1 BlBegin_1: 2122 BlEnd_1:34

Page 15: Vorlesung Compilertechnik Sommersemester 2008

15

Erweiterung der Grammatik um Schleifen und bedingte Verzweigungen

Program ::= BlockBlock ::= { DeclL StmtL }DeclL ::= Type VarL ; DeclL | Type ::= TYPELITVarL ::= IDENT , VarL | IDENTStmtL ::= Stmt ; StmtL | Stmt ::= Block | Assign | While | IfAssign ::= LVal = ExprLVal ::= IDENTExpr ::= Expr + Expr |

Expr - Expr | Expr * Expr |Expr / Expr | ( Expr ) | IDENT | ( Type ) Expr | INTLIT | FLOATLIT

While ::= while Cond BlockIf ::= if Cond then Block else BlockCond ::= …

Page 16: Vorlesung Compilertechnik Sommersemester 2008

16

Übersetzung einer While-Anweisung

ir[While,1],0 := "while_i_cond:" irc "tcn:= not tc" "if tcn then goto while_i_end" irb "goto while_i_cond" "while_i_end:", wobei:

ir2 = (irc,tc), ir3 = irb, tcn = getNextTemp(), i = getNextWhile().

Page 17: Vorlesung Compilertechnik Sommersemester 2008

17

Übersetzung einer if-Anweisung

ir[If,1],0 := (irc "if tc then goto then_i" irb2 "goto if_i_end" "then_i:" irb1 "if_i_end:"), wobei:

ir2 = (irc,tc), ir4 = irb1, ir6 = irb2, i = getNextIf().

Page 18: Vorlesung Compilertechnik Sommersemester 2008

18

Typkonstruktoren

Es sei = {int, float} die Menge der Basisdatentypen. Zu jedem Programm gehört eine Menge von Typen

mit , die auch selbst definierte Datentypen enthält. Es existieren die Typkonstruktoren:

array(T, n), wobei T ein Datentyp ist und n , struct(T1 k1,…,Tn kn).

Für T stehen folgende Hilfsfunktionen bereit: sizeof(T): Speicherbedarf des Datentyps T in Byte. typeOfElem(T) = T', falls T = array(T', n). typeOfElem(T,k) = Ti, falls T = struct(T1 k1,…,Tn kn) und ki = k.

Für einen Variablenbezeichner a: lookUp(a) = T, falls T der Datentyp des Bezeichners a ist.

Page 19: Vorlesung Compilertechnik Sommersemester 2008

19

Deklarationen eigener Datentypen

Program ::= TypeDeclL BlockTypeDeclL ::= TypeDecl ";" TypeDeclL | TypeDecl ::= IDENT = NewTypeNewType ::= IDENT |

"array" [INTLIT] "of" NewType | "struct" { NewTypeL }

newTypeL ::= NewType IDENT| NewType IDENT , NewTypeLBlock ::= { DeclL StmtL }

myint = int;a1int = array [10] of int;a2int = array [10] of array [5] of int;t = array [20] of struct {int a, a2int b};

Typbezeichner Typkonstruktor Größe in Byte

myint int 4

a1int array (int, 10) 40

anonym_1 array (int, 5) 20

a2int array (anonym_1,10) 200

anonym_2 struct (int a, a2int b) 204

t array (anonym_2,20) 4080

Grammatik erweitert um Deklaration eigener Datentypen:

Beispiel zur Deklaration eigener Datentypen:

Beim Parsen erzeugte Datentyptabelle:

Page 20: Vorlesung Compilertechnik Sommersemester 2008

20

Attributierte Grammatik zur Übersetzung von Struktur- und Feldzugriffen

Stmt ::= Block | Assign | While | IfAssign ::= LVal = ExprLVal ::= IDENTExpr ::= Expr + Expr |

Expr - Expr | Expr * Expr |Expr / Expr | ( Expr ) | IDENT | ( Type ) Expr | INTLIT | FLOATLIT |IDENT Q

While ::= while Cond BlockQ ::= [ Expr ] Q | . IDENT Q | [ Expr ] | . IDENT

Page 21: Vorlesung Compilertechnik Sommersemester 2008

21

Beispiel

Für Felder und Strukturen sind folgende Informationen Im Syntaxbaum annotiert: In der Regel Expr IDENT Q ist t2 = array(T,n), falls lookUp(id1) = array(T,n) ist. In der Regel Expr IDENT Q ist t2 = struct(T1 n1,…,Tm kn),

falls lookUp(id1) = struct(T1 n1,…,Tm kn) ist. In der Regel Q [ Expr ] Q ist t4 = T', falls t0 = array(T',n). In der Regel Q . IDENT Q ist t3 = Ti, falls t0 = struct(T1 n1,…,Tm kn) und

id2 = ki. Beispiel: Expr

IDENT id=i Qt=array(anonym_2,20)

[ Expr ] Qt=struct(int a, a2int b)

Qt=array(anonym_1,10)IDENTid=b.

[ Expr ] Qt=array(int, 5)

[ Expr ]

INTVAL iVal=15

.INTVAL iVal=8

.INTVAL iVal=4

Deklaration:t i;

Zugriff:i[15].b[8][4];

Typbezeichner

Typkonstruktor

myint int

a1int array (int, 10)

anonym_1 array (int, 5)

a2int array (anonym_1,10)

anonym_2 struct (int a, a2int b)

t array (anonym_2,20)

Page 22: Vorlesung Compilertechnik Sommersemester 2008

22

Übersetzung von Feld- und Strukturzugriffen in Ausdrücken (1)

ir[Expr,10],0 := ( iro "tb:=&id1" "tp:=tb+to" "t:=@tp", t), wobei

(iro,to) = ir2, tb := getNextTemp(), tp := getNextTemp(), t := getNextTemp().

ir[Q,3],0 := ( ire "ts:=s" "to:=ts*te", to), wobei

ir2 = (ire,te) t0 = array(T',n) und s = sizeof(T') ts := getNextTemp() to := getNextTemp()

ir[Q,4],0 := ( "to:=off", to), wobei

off = , falls t0 = struct(T1 n1,…,Tm nm) und id2 = ni

to := getNextTemp().

Expr ::= IDENT Q

Q ::= [ Expr ]

Q ::= . IDENT1

1

( )i

ik

sizeof T-

Page 23: Vorlesung Compilertechnik Sommersemester 2008

23

Übersetzung von Feld- und Strukturzugriffen in Ausdrücken (2)

ir[Q,1],0 := (ire irlo "ts:=s" "to:=ts*te" "tno:=to+tlo", tno), wobei

ir2 = (ire,te), ir4 = (irlo,tlo), t0 = array(T',n) und s = sizeof(T'), ts := getNextTemp(), tno := getNextTemp(), to := getNextTemp().

ir[Q,2],0 := (irlo "to:=off" "tno:=to+tlo", tno), wobei

off = offset(ni), falls t0 = struct(T1 n1,…,Tm nm) und id2 = ni, ir3 = (irlo,tlo), to := getNextTemp(), tno := getNextTemp().

Q ::= [ Expr ] Q

Q ::= . IDENT Q

Page 24: Vorlesung Compilertechnik Sommersemester 2008

24

Beispiel

Expr

IDENT id=i Qt=array(anonym_2,20)

[ Expr ] Qt=struct(int a, a2int b)

Qt=array(anonym_1,10)IDENTid=b.

[ Expr ] Qt=array(int, 5)

[ Expr ]

INTVAL iVal=15

.INTVAL iVal=8

.INTVAL iVal=4

ir=( t2:=4,t2)

ir=( t2:=4t3:=4 //sizeof(int)t4:=t3*t2,t4)

ir=( t1:=8,t1)

ir=( t0:=15,t0)

ir=( t1:=8t2:=4t3:=4 //sizeof(int)t4:=t3*t2t5:=20 //sizof(anonym_1)t6:=t5*t1t7:=t6+t4,t7)

ir=( t1:=8t2:=4t3:=4 //sizeof(int)t4:=t3*t2t5:=20 //sizof(anonym_1)t6:=t5*t1t7:=t6+t4t8:=4 //offset bt9:=t8+t7,t9)

ir=( t0:=15t1:=8t2:=4t3:=4 //sizeof(int)t4:=t3*t2t5:=20 //sizof(anonym_1)t6:=t5*t1t7:=t6+t4t8:=4 //offset bt9:=t8+t7t10:=204 //sizeof(anonym_2t11:=t10*t0t12:=t11+t9,t12)

ir=( t0:=15t1:=8t2:=4t3:=4 //sizeof(int)t4:=t3*t2t5:=20 t6:=t5*t1t7:=t6+t4t8:=4 //offset bt9:=t8+t7t10:=204t11:=t10*t0t12:=t11+t9t13:=&it14:=t13+t12,t15:=@t14,t15)

ir=( t0:=15t1:=8t2:=4t3:=4 //sizeof(int)t4:=t3*t2t5:=20 //sizof(anonym_1)t6:=t5*t1t7:=t6+t4t8:=4 //offset bt9:=t8+t7t10:=204 //sizeof(anonym_2t11:=t10*t0t12:=t11+t9,t12)

Page 25: Vorlesung Compilertechnik Sommersemester 2008

25

Grammatik zur Übersetzung von Funktionsaufrufen und -deklarationen

Program ::= TypeDeclL FuncLFuncL ::= IDENT IDENT ( ) Block |

IDENT IDENT ( FormalParam ) BlockFormalPram::= IDENT IDENT , FormalParam | IDENT IDENTBlock ::= { DeclL StmtL }

…Expr ::= Expr + Expr |

Expr - Expr | Expr * Expr |Expr / Expr | ( Expr ) | IDENT | ( Type ) Expr | INTLIT | FLOATLIT |IDENT Q |IDENT ( ParamList ) | IDENT ( )

ParamL ::= Expr | Expr , ParamLWhile ::= while Cond BlockQ ::= [ Expr ] Q | . IDENT Q | [ Expr ] | . IDENT

Page 26: Vorlesung Compilertechnik Sommersemester 2008

26

Übersetzung einer Deklaration

Eine Funktion funcDecl speichert die Signaturen der im Programm deklarierten Funktionen: Rückgabetyp, Name, Typen der formalen Parameter.

Eine Deklaration der Art t f(t1 i1,…,tn in) im Programm führt zu einem Eintrag (f, (t, t1, …,tn)) in funcDecl.

Leicht durch geeignete Attribute zu realisieren.

Page 27: Vorlesung Compilertechnik Sommersemester 2008

27

Übersetzung von Funktionsaufrufen

ParamL erhält ein Attribut pl zur Speicherung der aktuellen Parameterliste und ein Attribut ir zur Speicherung des Zwischencodes, der bei der Übersetzung der Ausdrücke in der Parameterliste entstanden ist:

pl[ParamL,1],0 := te und ir[ParamL,1],0 := ire, wobei (ire,te) = ir1. pl[ParamL,2],0 := (te ,pl3) und ir[ParamL,2],0 := ire ir3, wobei (ire,te) = ir1.

Expr

IDENT ( ParamList )

ParamListExpr ,

Exprir=(,t)

ir=(',t') pl=tir=

pl=(t', t)ir= '

,

ir=( ' tr := call f(t',t), tr)

id=f

Page 28: Vorlesung Compilertechnik Sommersemester 2008

28

Basisblöcke

Ein Basisblock ist eine Folge maximaler Länge von Anweisungen im 3-Adress-Code, für die gilt:

Nur die erste Anweisung darf ein Label sein (d.h., dass ein Sprung in einen Basisblock nur zu seiner ersten Anweisung führen kann) und

nur die letzte Anweisung darf eine Sprunganweisung sein (d.h., dass alle Anweisungen des Basisblocks ausgeführt werden, wenn die erste Anweisung ausgeführt wird).

Anmerkung: Unterprogrammaufrufe können als k-näre Operation betrachtet werden, falls sie keine Seiteneffekte verursachen. return-Anweisungen sind Sprunganweisungen.

Der erzeugte unoptimierte Zwischencode hat folgende nützlichen Eigenschaften:

Vor jeder Benutzung einer temporären Variablen wird diese im selben Basisblock beschrieben.

Nachdem eine temporäre Variable beschrieben wurde, wird sie genau einmal im selben Basisblock benutzt.

Programmvariablen treten nur in Anweisungen der Art x := y auf, wobei entweder x oder y eine Programmvariable ist.

Es ist eine totale Ordnung für die Anweisungen innerhalb eines Basisblocks vorgegeben.

Page 29: Vorlesung Compilertechnik Sommersemester 2008

29

DFGs zur Repräsentation von Basisblöcken

Totale Ordnung einer Anweisungsfolge im 3-Adress-Code wird zu einer partiellen Ordnung abgeschwächt.

G = (N, E, A, ord, label) sei ein gerichteter azyklischer Graph (DAG):

Knoten repräsentieren Operationen in den 3-Adress-Code-Anweisungen.

Kanten in E repräsentieren durch Variablen modellierte Datenabhängigkeiten:

Lese-Schreib-Abhängigkeit (input-dependence), Kanten in A repräsentieren durch Speicherzugriffe entstehende

Datenabhängigkeiten: Schreib-Lese-Abhängigkeit (anti-dependence), Schreib-Schreib-Abhängigkeit (output-dependence)

ord : E modelliert die Reihenfolge der eingehenden Kanten (Operanden) eines Knotens. Bei ord(e) < ord(e') ist e linker und e' rechter Operand.

label : N {const k, store, load, write a, read a, | k , a +, ist Operation im 3-Adress-Code} ist eine Beschriftung der Knoten mit Operationen.

Page 30: Vorlesung Compilertechnik Sommersemester 2008

30

Konstruktion eines DAGs zu einem Basisblock mit Eliminierung gemeinsamer Teilausdrücke

Eingabe: Basisblock als Folge von 3-Adress-Code-Anweisungen ir0,…,irn Ausgabe: DAG (N, E, A, ord, label) Algorithmus:

Hilfsfunktionen:

N := , E := , A := , ord := , label := S := // Enthält für die aktuelle Situation bei der Übersetzung für jede Variable // des Zwischencodes u.a. den Knoten im DAG, der ihren Wert berechnetfor i = 0 to n do switch(iri) case "x := y z": TranslateBinStmt(iri); break; case "x := y : TranslateUnaStmt(iri); break; case "x := y" : TranslateCopy(iri); break; case "@x := y" : TranslateStore(iri); break; case "x := @y" : TranslateLoad(iri); break; endodFür jedes (a,n,W) S mit a ist Programmvariable erzeuge Knoten m mit label(m) = write a, N := N {m}, E := E {(n,m)}, A := A {(h,m) | label(h) = read a oder label(h) = load oder label(h) = store}

findVar(var) if (var,n,x) S then return n else return 0 fi

findLabel(label,l,r) if n N mit Beschriftung label und ((l,n) E oder l = 0) und ((r,n) E oder r = 0) then return n else return 0 fi

Page 31: Vorlesung Compilertechnik Sommersemester 2008

31

Übersetzung von Kopieranweisungen

TranslateCopy(x := y) if findVar(y) = 0 then Erzeuge Knoten n mit label(n) = read y // passiert nur, wenn y Programmvariable N := N {n} S := S {(y,n,R)} fi l := findVar(y) S := S – {(x,n,k) | n N und k {R,W}) S := S {(x,l,W)}

TranslateConst(x := k) if findLabel(const k,0,0) = 0 then Erzeuge Knoten n mit label(n) = const k N := N {n} fi n := findLabel(const k,0,0) S := S {(x,n,W)}

Page 32: Vorlesung Compilertechnik Sommersemester 2008

32

Übersetzung binärer und unärer Operationen

TranslateBinStmt(x := y z) l := findVar(y) r := findVar(z) if n N mit label(n) = und (l,n) E und (r,n) E und not ((r,n) < (l,n)) then m := n else Erzeuge einen Knoten m mit Beschriftung N := N {m} E := E {(l,m),(r,m)} ord((l,m)) := 0; ord((r,m)) := 0, falls kommutativ, sonst ord((r,m)) := 1 fi S := S – {(x,n,k) | n N und k {R,W}) S := S {(x,m,W)}

TranslateUnaStmt(x := y) l := findVar(y) // immer erfolgreich if findLabel(, l) then m := n else Erzeuge neuen Knoten m mit label(m) = N := N {m} E := E {(l,m)} fi S := S – {(x,n,k) | n N und k {R,W}) S := S {(x,m,W)}

Page 33: Vorlesung Compilertechnik Sommersemester 2008

33

Beispiel 1

Beispiel: a = 2*(b+a-2) * (b+a)

t0 := 2t1 := bt2 := at3 := t1+t2t4 := 2t5 := t3 – t4t6 := t0 * t5t7 := bt8 := at9 := t7 + t8t10 := t6 * t9a := t10

1const 2

2read b

3read a

4

(t0,1,W)

S

(b,2,R)

(t1,2,W)(a,3,R)

(t2,3,W)

+

(t3,4,W)

(t4,1,W)

5-

(t5,5,W)6*

(t7,2,W)(t8,3,W)(t9,4,W)

(t6,6,W)

7* (t10,7,W)

(a,7,W)

8write a

Page 34: Vorlesung Compilertechnik Sommersemester 2008

34

Übersetzung von Speicherzugriffen

TranslateStore(@x := y) l := findVar(x) r := findVar(y) Erzeuge neuen Knoten n mit label(n)=store N := N {n} E := E {(l,n),(r,n)}; ord((l,n)):=0; ord((r,n)):=1; A := A {(k,n) | k N und label(k)=store oder label(k)=load oder label(k) = read a}

TranslateLoad(x := @y) l := findVar(y) Erzeuge neuen Knoten n mit label(n)=load N := N {n} E := E {(l,n)} S := S – {(x,n,k) | n N und k {R,W}) S := S {(x,n,W)} A := A {(k,n) | k N und label(k) = store oder label(k) = write a}

Page 35: Vorlesung Compilertechnik Sommersemester 2008

35

Beispiel 2

Beispiel: a[i] = b[i] + a[j]

t0 := it1 := 4t2 := t1 * t0t3 := &bt4 := t3 + t2t5 := @t4t6 := jt7 := 4t8 := t7 * t6t9 := &at10 := t9 + t8t11 := @t10t12 := t5 + t11t13 := it14 := 4t15 := t14 * t13t16 := &at17 := t16 + t15@t17 := t12

1read i

2const 4

3

(t0,1,W)

S

(t1,2,W)(t2,3,W)

*4

const &b(t3,4,W)

5+

(t4,5,W)

6 load

(t5,6,W)

7read j

(i,1,R)

(t6,7,W)(j,7,R)

(t7,2,W)

8*

(t8,8,W)

9const &a

(t9,9,W)

10+

(t10,10,W)11 load

(t11,11,W)

12+ (t12,12,W)

(t13,1,W)(t14,2,W)(t15,3,W)(t16,9,W)

13+

(t17,13,W)14 store

Page 36: Vorlesung Compilertechnik Sommersemester 2008

36

Rücktransformation (List-Scheduling)

Transformation eines DAGs (N, E, A, ord, label) in 3-Adress-Code ist trivial:

Eine Menge ready speichert Knoten, deren Eingabedaten berechnet wurden.

Eine Menge scheduled speichert die geplanten Knoten. Initial: scheduled := Genau die Kanten e E mit demselben Quellknoten werden mit

derselben temporären Variablen beschriftet. ready = {n | n N und m (N – scheduled): (m,n) E und (m,n) A}. Für einen Knoten n ready wird die zugehörige 3-Adress-Code-

Anweisung mit den Operanden an den ein- und ausgehenden Kanten von n erzeugt und scheduled := scheduled {n} gesetzt.

Der letzte Schritt wird solange wiederholt, bis scheduled = N. Konsequenz: Variablen werden mehrfach verwendet. Reihenfolge der Operationen ist nur partiell festgelegt und kann

durch die Auswahl des nächsten Knotens aus ready beeinflusst werden (evtl. Nutzung einer Prioritätsfunktion).

Page 37: Vorlesung Compilertechnik Sommersemester 2008

37

Steuerflussgraph

Eine Folge von 3-Adress-Code-Befehlen sei in eine Menge von Basisblöcken b1,…bn zerlegt.

Ein Basisblock bj ist Steuerflussnachfolger eines Basisblocks bi, gdw.

(die letzte Anweisung in bi kein Sprungbefehl oder ein bedingter Sprungbefehl ist und im 3-Adress-Code die erste Anweidung von bj auf die letzte Anweisung von bi folgt) oder

die letzte Anweisung in bi ein Sprungbefehl mit dem Ziellabel x ist und die erste Anweisung in bj das Label x ist.

Ein Steuerflussgraph (N,E,q,s) ist ein gerichteter Graph: dessen Knoten Basisblöcke repräsentieren und der eine Kante vom Knoten n zum Knoten m besitzt, falls m

Steuerflussnachfolger von n ist. q,s N sind ausgezeichnete Startknoten / Endknoten.

Ein Steuerflussgraph enthält alle möglichen Abarbeitungspfade innerhalb einer Prozedur.

Wird verwendet zur Sammlung von Informationen im Programm, um diese für Optimierungszwecke zu nutzen.

Page 38: Vorlesung Compilertechnik Sommersemester 2008

38

Statischer Aufrufgraph

In einem statischen Aufrufgraphen (N,E,label) repräsentieren die Knoten die Funktionen des Programms.

Eine gerichtete Kante von einem Knoten n zu einem Knoten m existiert genau dann, wenn die Funktion f die Funktion f' aufruft und label(n) = f und label(m) = f'.

Verwendung zur interprozeduralen Datenflussanalyse und Programmoptimierung.

Page 39: Vorlesung Compilertechnik Sommersemester 2008

39

SSA-Code (Static-Single-Assignment)

SSA-Code ist 3-Adress-Code mit folgenden Eigenschaften bzw. Erweiterungen:

Eigenschaft: Jeder Variablen wird statisch nur einmal ein Wert zugewiesen.

Erweiterung: Es gibt eine Operation x := (y1,…yn), die, abhängig vom Programmablauf, der zu dieser Operation geführt hat, der Variablen x den Wert einer Variablen yi zuordnet.

Datenabhängigkeiten sind direkt erkennbar, da jede Variablenverwendung genau eine Definition besitzt.

a := 2b := aa:= 1

a:= 2

d:= a

a2:= 2

d:= (a1,a2)

a0 := 2b := a0

a1:= 1

Steuerflussgraph mit 3-Adress-Code Steuerflussgraph mit SSA-Code

Page 40: Vorlesung Compilertechnik Sommersemester 2008

Ende der Zwischencodeerzeugung

Weiter zur Zielcodeerzeugung