Vorlesung compilertechnik sommersemester 2008
Download
1 / 40

Vorlesung Compilertechnik Sommersemester 2008 - PowerPoint PPT Presentation


  • 75 Views
  • Uploaded on

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:

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' Vorlesung Compilertechnik Sommersemester 2008' - oke


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
Vorlesung compilertechnik sommersemester 2008

Vorlesung CompilertechnikSommersemester 2008

Zwischencodeerzeugung

M. Schölzel


Aufgabe der zwischencodeerzeugung
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.


Beispiele f r oft genutzte arten der zwischencodedarstellung
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.


Einbettung der zwischencodeerzeugung in den compiler
Einbettung der Zwischencodeerzeugung in den Compiler Zwischencodedarstellung

  • 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.

Abstraktionsebene

Hoch

Niedrig

Syntaxbaum

3-Adress-Code

Parser

Zielcode-

erzeugung

t2 := t1 + t0

t3 := a

t4 := t2 * t3

Zwischencodeerzeugung


3 adress code
3-Adress-Code Zwischencodedarstellung

  • Folge von 3-Adress-Code-Anweisungen.

  • Anweisungsarten:

    • Binäranweisungen: x := y  z

    • Unäranweisungen: x :=  y

    • Kopieranweisungen: x:=y, x:=k, @x:=y, x:[email protected], 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)


Beispiel 3 adress code
Beispiel: 3-Adress-Code Zwischencodedarstellung

Function f:

t0 := 1

fak = t0

while_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_cond

while_0_end:

t11 := fak

return t11

int f(int n){

int fak = 1;

while(n > 0) {

fak = fak * n;

n = n – 1;

}

return fak;

}


Speichermodell
Speichermodell Zwischencodedarstellung

  • 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.


Modell f r die zwischencodeerzeugung
Modell für die Zwischencodeerzeugung Zwischencodedarstellung

  • 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…Anspeichert 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.


Prinzipien bei der bersetzung eines syntaxbaums in 3 adress code
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.


Hilfsfunktionen f r die bersetzung in 3 adress code
Hilfsfunktionen, für die Übersetzung in 3-Adress-Code 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.


Bersetzung von zuweisungen und ausdr cken
Übersetzung von Zuweisungen und Ausdrücken 3-Adress-Code

  • 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')


Beispiel
Beispiel 3-Adress-Code

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

Quelltextfragment:

Assign

ir= t0:=a$0

t1:=2

t2:=b$01

t3:=t1*t2

t4:=t0+t3

c$01:=t4

{

int a;

{

int b,c;

c := a+2*b;

}

}

ir=( t0:=a$0

t1:=2

t2:=b$01

t3:=t1*t2

t4:=t0+t3,t4)

LVal

Expr

id=c$01

IDENT

id=c

Expr

Expr

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

ir=( t1:=2

t2:=b$01

t3:=t1*t2,t3)

Symtab0

IDENT

Expr

Expr

id=a

ir=(t1:=2,t1)

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

INTLIT

IDENT

id=b

iVal=2

Symtab0.1


Bersetzung von anweisungsfolgen
Übersetzung von Anweisungsfolgen 3-Adress-Code

  • 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


Beispiel1
Beispiel 3-Adress-Code

Syntaxbaum für das Programm:

{

Stmt1;

{

Stmt21;

Stmt12;

};

Stmt3;

Stmt4;

}

Program

ir= 1 BlBegin_1: 2122 BlEnd_1:34

Block

{

DeclL

StmtL

}

ir= 1 BlBegin_1: 2122 BlEnd_1:34

Stmt1

;

StmtL

ir=BlBegin_1: 2122 BlEnd_1:34

ir=1

Stmt2

;

StmtL

ir=34

ir=BlBegin_1: 2122 BlEnd_1:

Block

Stmt3

;

StmtL

ir=2122

ir=4

ir=3

{

DeclL

StmtL

}

ir=2122

Stmt4

;

StmtL

ir=4

Stmt21

;

StmtL

ir=21

ir=22

Stmt22

;

StmtL

ir=22


Erweiterung der grammatik um schleifen und bedingte verzweigungen
Erweiterung der Grammatik um Schleifen und bedingte Verzweigungen

Program ::= Block

Block ::= { DeclL StmtL }

DeclL ::= Type VarL ; DeclL | 

Type ::= TYPELIT

VarL ::= IDENT , VarL | IDENT

StmtL ::= Stmt ; StmtL | 

Stmt ::= Block | Assign | While | If

Assign ::= LVal = Expr

LVal ::= IDENT

Expr ::= Expr + Expr |

Expr - Expr |

Expr * Expr |

Expr / Expr |

( Expr ) |

IDENT |

( Type ) Expr |

INTLIT |

FLOATLIT

While ::= while Cond Block

If ::= if Cond then Block else Block

Cond ::= …


Bersetzung einer while anweisung
Übersetzung einer While-Anweisung Verzweigungen

  • ir[While,1],0 := "while_i_cond:"  irc  "tcn:= nottc"  "iftcnthen goto while_i_end"  irb  "goto while_i_cond" "while_i_end:", wobei:

    • ir2 = (irc,tc),

    • ir3 = irb,

    • tcn = getNextTemp(),

    • i = getNextWhile().


Bersetzung einer if anweisung
Übersetzung einer Verzweigungenif-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().


Typkonstruktoren
Typkonstruktoren Verzweigungen

  • Es sei B = {int, float} die Menge der Basisdatentypen.

  • Zu jedem Programm gehört eine Menge T von Typen mit B T, die auch selbst definierte Datentypen enthält.

  • Es existieren die Typkonstruktoren:

    • array(T, n), wobei T  ein Datentyp ist und n,

    • struct(T1k1,…,Tnkn).

  • 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(T1k1,…,Tnkn) und ki = k.

  • Für einen Variablenbezeichner a:

    • lookUp(a) = T, falls T der Datentyp des Bezeichners a ist.


Deklarationen eigener datentypen
Deklarationen eigener Datentypen Verzweigungen

Grammatik erweitert um Deklaration eigener Datentypen:

Program ::= TypeDeclL Block

TypeDeclL ::= TypeDecl ";" TypeDeclL | 

TypeDecl ::= IDENT = NewType

NewType ::= IDENT |

"array" [INTLIT] "of" NewType |

"struct" { NewTypeL }

newTypeL ::= NewType IDENT| NewType IDENT , NewTypeL

Block ::= { DeclL StmtL }

Beispiel zur Deklaration eigener Datentypen:

myint = int;

a1int = array [10] of int;

a2int = array [10] of array [5] of int;

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

Beim Parsen erzeugte Datentyptabelle:


Attributierte grammatik zur bersetzung von struktur und feldzugriffen
Attributierte Grammatik zur Übersetzung von Struktur- und Feldzugriffen

Stmt ::= Block | Assign | While | If

Assign ::= LVal = Expr

LVal ::= IDENT

Expr ::= Expr + Expr |

Expr - Expr |

Expr * Expr |

Expr / Expr |

( Expr ) |

IDENT |

( Type ) Expr |

INTLIT |

FLOATLIT |

IDENT Q

While ::= while Cond Block

Q ::= [ Expr ] Q | . IDENT Q | [ Expr ] | . IDENT


Beispiel2
Beispiel Feldzugriffen

  • 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(T1n1,…,Tmkn), falls lookUp(id1) = struct(T1n1,…,Tmkn) 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(T1n1,…,Tmkn) und id2 = ki.

  • Beispiel:

Expr

Deklaration:

t i;

Zugriff:

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

IDENT

Q

id=i

t=array(anonym_2,20)

[ Expr ]

Q

t=struct(int a, a2int b)

INTVAL

Q

iVal=15

.

IDENT

t=array(anonym_1,10)

id=b

[ Expr ]

Q

t=array(int, 5)

INTVAL

[ Expr ]

iVal=8

.

INTVAL

iVal=4

.


Bersetzung von feld und strukturzugriffen in ausdr cken 1
Übersetzung von Feld- und Strukturzugriffen in Ausdrücken (1)

Expr ::= IDENT Q

  • ir[Expr,10],0 := ( iro "tb:=&id1"  "tp:=tb+to"  "t:[email protected]", 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(T1n1,…,Tmnm) und id2 = ni

    • to := getNextTemp().

Q ::= [ Expr ]

Q ::= . IDENT


Bersetzung von feld und strukturzugriffen in ausdr cken 2
Übersetzung von Feld- und Strukturzugriffen in Ausdrücken (2)

Q ::= [ Expr ] Q

  • 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(T1n1,…,Tmnm) und id2 = ni,

    • ir3 = (irlo,tlo),

    • to := getNextTemp(),

    • tno := getNextTemp().

Q ::= . IDENT Q


Beispiel3
Beispiel (2)

ir=( t0:=15

t1:=8

t2:=4

t3:=4 //sizeof(int)

t4:=t3*t2

t5:=20

t6:=t5*t1

t7:=t6+t4

t8:=4 //offset b

t9:=t8+t7

t10:=204

t11:=t10*t0

t12:=t11+t9

t13:=&i

t14:=t13+t12,

t15:[email protected],t15)

ir=( t0:=15

t1:=8

t2:=4

t3:=4 //sizeof(int)

t4:=t3*t2

t5:=20 //sizof(anonym_1)

t6:=t5*t1

t7:=t6+t4

t8:=4 //offset b

t9:=t8+t7

t10:=204 //sizeof(anonym_2

t11:=t10*t0

t12:=t11+t9,t12)

ir=( t0:=15

t1:=8

t2:=4

t3:=4 //sizeof(int)

t4:=t3*t2

t5:=20 //sizof(anonym_1)

t6:=t5*t1

t7:=t6+t4

t8:=4 //offset b

t9:=t8+t7

t10:=204 //sizeof(anonym_2

t11:=t10*t0

t12:=t11+t9,t12)

ir=( t1:=8

t2:=4

t3:=4 //sizeof(int)

t4:=t3*t2

t5:=20 //sizof(anonym_1)

t6:=t5*t1

t7:=t6+t4

t8:=4 //offset b

t9:=t8+t7,t9)

Expr

IDENT

Q

id=i

t=array(anonym_2,20)

[ Expr ]

Q

t=struct(int a, a2int b)

ir=( t0:=15,t0)

INTVAL

Q

ir=( t1:=8

t2:=4

t3:=4 //sizeof(int)

t4:=t3*t2

t5:=20 //sizof(anonym_1)

t6:=t5*t1

t7:=t6+t4,t7)

iVal=15

.

IDENT

t=array(anonym_1,10)

id=b

ir=( t2:=4

t3:=4 //sizeof(int)

t4:=t3*t2,t4)

[ Expr ]

Q

ir=( t1:=8,t1)

t=array(int, 5)

INTVAL

[ Expr ]

ir=( t2:=4,t2)

iVal=8

.

INTVAL

iVal=4

.


Grammatik zur bersetzung von funktionsaufrufen und deklarationen
Grammatik zur Übersetzung von Funktionsaufrufen und -deklarationen

Program ::= TypeDeclL FuncL

FuncL ::= IDENT IDENT ( ) Block |

IDENT IDENT ( FormalParam ) Block

FormalPram ::= IDENT IDENT , FormalParam | IDENT IDENT

Block ::= { 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 , ParamL

While ::= while Cond Block

Q ::= [ Expr ] Q | . IDENT Q | [ Expr ] | . IDENT


Bersetzung einer deklaration
Übersetzung einer Deklaration -deklarationen

  • 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.


Bersetzung von funktionsaufrufen
Übersetzung von Funktionsaufrufen -deklarationen

  • 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

ir=( ' 

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

IDENT

(

ParamList

)

id=f

pl=(t', t)

ir= '

,

Expr

,

ParamList

ir=(',t')

pl=t

ir=

Expr

ir=(,t)


Basisbl cke
Basisblöcke -deklarationen

  • 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.


Dfgs zur repr sentation von basisbl cken
DFGs zur Repräsentation von Basisblöcken -deklarationen

  • 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.


Konstruktion eines dags zu einem basisblock mit eliminierung gemeinsamer teilausdr cke
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 berechnet

for 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;

end

od

Fü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


Bersetzung von kopieranweisungen
Übersetzung von Kopieranweisungen gemeinsamer Teilausdrücke

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)}

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)}


Bersetzung bin rer und un rer operationen
Übersetzung binärer und unärer Operationen gemeinsamer Teilausdrücke

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)}

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)}


Beispiel 1
Beispiel 1 gemeinsamer Teilausdrücke

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

read a

read b

S

const 2

3

2

1

(t0,1,W)

t0 := 2

t1 := b

t2 := a

t3 := t1+t2

t4 := 2

t5 := t3 – t4

t6 := t0 * t5

t7 := b

t8 := a

t9 := t7 + t8

t10 := t6 * t9

a := t10

(b,2,R)

+

(t1,2,W)

4

(a,3,R)

-

(t2,3,W)

5

(t3,4,W)

(t4,1,W)

(t5,5,W)

*

6

(t6,6,W)

(t7,2,W)

(t8,3,W)

(t9,4,W)

(t10,7,W)

*

7

(a,7,W)

write a

8


Bersetzung von speicherzugriffen
Übersetzung von Speicherzugriffen gemeinsamer Teilausdrücke

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}

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}


Beispiel 2
Beispiel 2 gemeinsamer Teilausdrücke

S

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

(i,1,R)

(t0,1,W)

read j

t0 := i

t1 := 4

t2 := t1 * t0

t3 := &b

t4 := t3 + t2

t5 := @t4

t6 := j

t7 := 4

t8 := t7 * t6

t9 := &a

t10 := t9 + t8

t11 := @t10

t12 := t5 + t11

t13 := i

t14 := 4

t15 := t14 * t13

t16 := &a

t17 := t16 + t15

@t17 := t12

read i

const 4

(t1,2,W)

7

1

2

(t2,3,W)

(t3,4,W)

*

const &b

*

const &a

(t4,5,W)

3

4

8

9

(t5,6,W)

(j,7,R)

+

+

+

(t6,7,W)

5

13

10

(t7,2,W)

(t8,8,W)

(t9,9,W)

load

load

6

11

(t10,10,W)

(t11,11,W)

+

(t12,12,W)

12

(t13,1,W)

(t14,2,W)

(t15,3,W)

(t16,9,W)

store

14

(t17,13,W)


R cktransformation list scheduling
Rücktransformation (List-Scheduling) gemeinsamer Teilausdrücke

  • 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 readywird 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).


Steuerflussgraph
Steuerflussgraph gemeinsamer Teilausdrücke

  • 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.


Statischer aufrufgraph
Statischer Aufrufgraph gemeinsamer Teilausdrücke

  • 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.


Ssa code static single assignment
SSA-Code (Static-Single-Assignment) gemeinsamer Teilausdrücke

  • 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 := 2

b := a

a:= 1

a0 := 2

b := a0

a1:= 1

a:= 2

a2:= 2

d:= a

d:= (a1,a2)

Steuerflussgraph mit 3-Adress-Code

Steuerflussgraph mit SSA-Code


Ende der zwischencodeerzeugung

Ende der Zwischencodeerzeugung gemeinsamer Teilausdrücke

Weiter zur Zielcodeerzeugung


ad