480 likes | 620 Views
Vorlesung Compilertechnik Sommersemester 2009. Optimierung M. Schölzel. Einbettung der Optimierung in den Compiler. Gründe für die Optimierung:
E N D
Vorlesung CompilertechnikSommersemester 2009 Optimierung M. Schölzel
Einbettung der Optimierung in den Compiler • Gründe für die Optimierung: • Vom Frontend generierter Zwischencode ist ineffizient, da er aus der Struktur des Quellprogramms entstanden ist und sich nicht an der Zielarchitektur orientiert. • Programmierer schreiben "verbesserungsfähigen" Quellcode. Kontext- prüfung Zielcodeabhängige Optimierungen Backend Quell-text Zwischencode und Symbol- tabelle Ziel-code Scanner Parser Zielcode- erzeugung Zielcodeunab- hängige Optimierungen Frontend
Klassifizierung der Optimierung Eliminierung redun- danter Berechnungen, Berechnung konstanter Ausdrücke Eliminierung redun- danter Berechnungen, Berechnung konstanter Ausdrücke, Codeverschiebung Maschinenunabhängig Registerplanung, Zielcodeauswahl Registerplanung Maschinenabhängig Lokal Global
Grundbegriffe (1) • S = (N,E,q,s) sei ein Steuerflussgraph. • N = {b0,…,bn} sind die Basisblöcke im Steuerflussgraphen, wobei ir0(bi),…,ir#(bi)(bi) die Folge der 3-Adress-Code-Anweisungen in bi ist. • Über b0 = q wird S betreten und über b1 = s (z.B. return) verlassen. • Eine Programmposition ist ein Tupel (i,j) wobei damit die Position unmittelbar vor der 3-Adress-Code-Anweisung irj(bi) gemeint ist. • Mit (0,0) wird Startposition und mit (1,0) Stoppposition bezeichnet. • Ein Pfad im Steuerflussgraphen von Programmposition (i,j) zur Programmposition (m,n) ist eine Folge 0,…,k von 3-Adress-Code-Anweisungen, für die gilt: • 0…k kann in 3-Adress-Code-Folgen 0…c = 0…k zerlegt werden, so dass: • z= ir0(baz)…ir#(baz)(baz) für 1 z < c und • (0 = irj(ba0)…ir#(ba0)(ba0) und c= ir0(bac)…irn-1(bac) falls c > 0) oder (0 = irj(ba0)…irn-1(ba0) falls c = 0) und • (ba0, ba1),(ba1, ba2),…,(bac-1, bac) E
Beispiel 0 3 0: Function f: 1: t0 := 1 2: fak = t0 3: while_0_cond: 4: t1 := n 5: t2 := 0 6: t3 := t1 > t2 7: t4 := not t3 8: if t4 then while_0_end 9: t5 := fak 10: t6 := n 11: t7 := t5 * t6 12: fak := t7 13: t8 := n 14: t9 := 1 15: t10 := t8 – t9 16: n := t10 17: goto while_0_cond 18: while_0_end: 19: t11 := fak 20: return t11 t0 := 1 4 fak = t0 6 t1 := n 7 t2 := 0 8 t3 := t1 > t2 9 t4 := not t3 10 if t4 then 21 11 21 t5 := fak t11 := fak 12 22 t6 := n f = t11 13 1 t7 := t5 * t6 return f 14 fak := t7 15 t8 := n 16 t9 := 1 17 t10 := t8 – t9 18 n := t10
Qualität des Modells • Steuerflussgraph modelliert alle möglichen Abarbeitungspfade des Programms, unabhängig davon, ob alle diese Pfade bei der Ausführung des Programms betreten werden können. • Falls Turingmaschine i auf Eingabe i nach n Schritten stoppt, dann gibt es eine Eingabe n, so dass 4 nach 2 ausgeführt wird, sonst nicht. 0 Eingabe von n 2 if (TMi(i)n) then 4 3 … 4 … 1 …
Grundbegriffe (2) • Verwendung einer Variablen v:Eine Variable v wird in der Zwischencodeanweisung iri(j) verwendet, falls iri(j) eine der Formen x := v, x := v, x := y v, x := v y, x := @v, @v := x, return v, if v then goto label, x := (Type) v, x := call f(…,v,…) hat. • Definition einer Variablen v:Eine Variable v wird in der Zwischencodeanweisung iri(j) definiert, falls iri(j) eine der Formen v := … hat. • Eine definierende Anweisung für v ist eine Anweisung, die v definiert. • Eine verwendende Anweisung für v ist eine Anweisung, die v verwendet. • Erreichende Definition: Die Definition einer Programmvariablen v erreicht eine Verwendung von v, falls es einen Pfad im Steuerflussgraphen von dieser Definition zur Verwendung gibt, auf dem keine andere Definition von v liegt.
Beispiel Erreichende Definitionen 0 3 t0 := 1 4 fak = t0 6 t1 := n 7 0 t2 := 0 8 3 t3 := t1 > t2 t0 := 1 9 t4 := not t3 4 fak = t0 10 6 if t4 then while_0_end t1 := n 7 11 21 t2 := 0 t11 := fak t5 := fak 8 12 22 t3 := t1 > t2 t6 := n kgv = t11 9 13 1 t4 := not t3 t7 := t5 * t6 return kgv 10 14 if t4 then while_0_end fak := t7 Definition zu einer Verwendung ist in unverzweigtem Steuerfluss einfach zu finden. 15 t8 := n 16 t9 := 1 17 t10 := t8 – t9 18 n := t10 Definition zu einer Verwendung ist bei Verzweigungen im Steuerfluss schwieriger zu finden.
Grundidee Datenflussanalyse am Beispiel der Erreichenden Definitionen Es gibt eingehende Informationen I in einen Knoten: I = in(2) 0 t2 := 2 Durch die Anweisung(en) in einem Knoten werden Informationen zerstört: I := I – Kill(2) 1 t1 := 1 in(2) = {(t2,0),(t1,1)} Kill(2) = {(t1,i) | i } 2 t1 := n Gen(2) = {(t1,2)} Durch die Anweisung(en) in einem Knoten werden neue Informationen generiert: I := IGen(2) out(2) = {(t2,0),(t1,2)} 3 t3 := t1 > t2 Es entstehen ausgehende Informationen an einen Knoten: out(2) 4 if t3 then while_0_end
Transferfunktion für einen Basisblock • Steuerfluss in einem Basisblock i mit den Anweisungen ir0(i),…,ir#i(i) ist bekannt. • Damit ist die Transferfunktion für diesen Basisblock: • Vereinfachung der Transferfunktion zu out(i) = (in(i) – kill(i)) gen(i). in(i) - Kill(0) Gen(0) 0 t2 := 2 1 - Kill(1) Gen(1) t1 := 1 2 - Kill(2) Gen(2) t1 := n - Kill(3) Gen(3) 3 t3 := t1 > t2 - Kill(4) Gen(4) 4 if t3 then while_0_end out(i)
Datenflussanalyse bei Verzweigungen im Steuerflussgraphen • Ausgehende Informationen out(i) gelangen zu jedem Steuerflussnachfolger von i. • Treffen Informationen von mehreren Steuerflussvorgängern zusammen, müssen diese zu einer eingehenden Information zusammengefasst werden. Transformiert eingehende Informationen … t3 := t1 + t2 … Ausgehende Informationen I I Transformiert eingehende Informationen I … t0 := t1 – t9 … … t2 := t3 * t5 … Transformiert eingehende Informationen Hier müssen Informationen kombiniert werden
Grundprinzip Datenflussanalyse • Informationen I breiten sich entweder mit oder gegen den Steuerfluss aus. • Für jeden Knoten b gibt es: • Eingehenden Informationen: in(b), • ausgehende Informationen: out(b), • erzeugte Informationen: gen(b), • zerstörte Informationen: kill(b). • Abhängig von der Ausbreitungsrichtung der Informationen sind: • Vorwärtsanalyse: • in(b) = out(b1) out(b2) … out(bn), wobei die bi die Steuerflussvorgänger von b sind und • out(b) = (in(b) – kill(b)) gen(b) (Transferfunktion genannt) • Rückwärtsanalyse: • out(b) = in(b1) in(b2) … in(bn), wobei die bi die Steuerflussnachfolger von b sind und • in(b) = (out(b) – kill(b)) gen(b) (Transferfunktion genannt) • Durch den Steuerflussgraphen wird eine Mengengleichungssystem definiert. • Falls der Steuerflussgraph Zyklen enthält, ist das Mengengleichungssystem rekursiv; Lösung durch Fixpunktiteration.
Information Erreichende Definitionen • An einer Programmposition (i,j) interessiert für eine Variable v, an welchen Programmpositionen der Wert der Variablen v definiert und bis zur Position (i,j) nicht mehr verändert wurde. • Menge aller Informationen: (()V) • Vorwärtsanalyse mit := • gen(bi) := {((i,j),v) | irj(i) ist letzte Definition von v in bi} • kill(bi) := {((m,n),v) | m, n und bi enthält eine Definition von v} gen(b2) = {((2,0),t0), ((2,1),t1), ((2,2),t2)} kill(b3) = {((m,n),t0), ((m,n),t1), ((m,n),t2)} in(b2) = in(b3) = in(b4) = in(b1) = {((2,0),t0), ((2,1),t1), ((2,2),t2)} {((2,0),t0), ((2,1),t1), ((2,2),t2)} {((4,0),t3), ((4,1),t4), ((4,2),t2)} {((2,1),t1), ((2,2),t2), ((3,1),t0), ((2,0),t0), ((4,0),t3), ((4,1),t4), ((4,2),t2)} {((2,0),t0), ((2,1),t1), ((2,2),t2)} {((2,0),t0), ((2,1),t1), ((2,2),t2)} {((4,0),t3), ((4,1),t4), ((4,2),t2)} {((3,1),t0)} {((4,0),t3), ((4,1),t4), ((4,2),t2)} b0 t0 := a t1 := b t2 := t0 + t1 b2 gen(b3) = {((3,1),t0)} kill(b3) = {((m,n),t0)} out(b0) = out(b2) = out(b3) = out(b4) = {((2,0),t0), ((2,1),t1), ((2,2),t2)} {((3,1),t0)} {((4,0),t3), ((4,1),t4), ((4,2),t2)} {((2,0),t0), ((2,1),t1), ((2,2),t2)} {((2,1),t1), ((2,2),t2), ((3,1),t0)} {((2,0),t0), ((2,1),t1)} {((4,0),t3), ((4,1),t4), ((4,2),t2)} b3 t0 := t2 – t0 t0 := b b4 t3 := t2 t4 := t3 * t2 t2 := t2 – t4 b1 gen(b4) = {((4,0),t3), ((4,1),t4), ((4,2),t2)} kill(b4) = {((m,n),t3), ((m,n),t4), ((m,n),t2)}
Information Lebendige Variablen • An einer Programmposition (i,j) interessiert für eine Variable v, ob es einen Pfad zu einer Programmposition gibt, an der v verwendet wird, ohne auf diesem Pfad definiert zu werden. • Menge aller Informationen: (V) • Rückwärtsanalyse mit := • gen(bi) := {v | irj(i) ist Verwendung von v und für alle 0 k < j gilt: irk(i) ist keine Definition von v} • kill(bi) := {v | v wird in bi definiert} gen(b2) = {a,b} kill(b2) = {t0,t1,t2} b0 in(b0) = in(b2) in(b2) = (in(b3) in(b4) – kill(b2)) gen(b2) in(b3) = (in(b1) – kill(b3)) gen(b3) in(b4) = ((in(b4) in(b1)) – kill(b4)) gen(b4) b2 t0 := a t1 := b t2 := t0 + t1 gen(b4) = {t2} kill(b4) = {t2,t3,t4} gen(b3) = {b, t2, t0} kill(b3) = {t0} b3 t0 := t2 – t0 t0 := b b4 t3 := t2 t4 := t3 * t2 t2 := t2 – t4 b1 gen(b4) = kill(b4) =
Kopier- / Konstanten-Popagierung • Ersetze die Verwendung der Variablen y in einer Anweisung iri(j) durch zfalls: • auf allen Pfaden von Programmposition (0,0) zur Programmposition (j,i), eine Anweisung irn(m) = "y := z" existiert und • es auf allen Pfaden von Positionen (m,n+1) zur Position (j,i) keine definierende Anweisung für y und z gibt. • Nach der Ersetzung kann es sein, dass die Variable y nicht mehr benutzt wird. • z darf eine Variable (copy propagation) oder Konstante (constant propagation) sein. • Constant Folding: Ersetze Zwischencodeanweisungen der Form x := y z bzw. x := z durch x := k, falls y und z konstant sind und k das Ergebnis der Operation ist. t0 := 8 y := t0 t0 := 7 x := t0 t1 := x t2 := y t3 := t1 + t2 z := t3 t1 := x t2 := y t3 := t1 + t2 z := t3 y darf durch t0 ersetzt werden y darf nicht durch t0 ersetzt werden t0 := y t1 := x
Information für Kopier-/Konstanten-Propagierung • Berechnung der Information Erreichende Kopien RC(x) = in(x) für Knoten x im Steuerflussgraphen S = (N,E,q,s) durch eine Datenflussanalyse mit den Parametern: • Menge aller Informationen: ({x:=y | x:=y ist Kopierbefehl im Programm}) • Vorwärtsanalyse mit := • gen(bi) := {x:=y | x:=y ist Zwischencodebefehl irk(bi) und in allen Zwischencodebefehlen irk+1(bi)… ir#bi(bi) wird weder x noch y definiert} • kill(bi) := {x:=z | x wird in Block bi definiert und x:=z ist Kopieranweisung im Programm} {z:=x | x wird in Block bi definiert und z:=x ist Kopieranweisung im Programm} 2: t0 := 8 y := t0 "t0:=8","y:=t0" "t0:=8","y:=t0" 3: t0 := 7 x := t0 t1 := x t2 := y t3 := t1 + t2 z := t3 4: t1 := x t2 := y t3 := t1 + t2 z := t3 "t1:=x","t2:=y","z:=t3" 1: t0 := y t1 := x
Kopier-/Konstanten-Propagierung im Basisblock • In einem Basisblock b wird die Verwendung von x in Anweisung i durch y ersetzt, falls: • x := y in(b) und in allen Anweisungen k mit 0 k < i weder x noch y definiert werden oder • Anweisung j < i die Form x := y hat und in allen Anweisungen k mit j < k < i weder x noch y definiert werden. • Diese Ersetzung muss gegebenenfalls wiederholt werden, bis keine weiteren Ersetzungen möglich sind. 2: t0 := 8 y := t0 2: t0 := 8 y := 8 "t0:=8","y:=t0" Erneute Informationssammlung ergibt, dass y durch 8 ersetzt werden darf "t0:=8","y:=t0" 3: t0 := 7 x := t0 t1 := x t2 := y t3 := t1 + t2 z := t3 4: t1 := x t2 := y t3 := t1 + t2 z := t3 3: t0 := 7 x := 7 t1 := 7 t2 := y t3 := 7 + y z := t3 4: t1 := x t2 := 8 t3 := x + 8 z := t3 t1 := x t2 := t0 t3 := t1 + t2 z := t3 "t1:=x","t2:=y","z:=t3" 1: t0 := y t1 := x 1: t0 := y t1 := x
Eliminierung toten Codes • Eine Zwischencodeanweisung iri(j) = "x := e" kann aus dem Zwischencode entfernt werden, falls • für alle Programmpositionen (m,n) an denen x verwendet wird und alle Pfade von (j,i+1) nach (m,n) gilt: x wird auf diesen Pfaden definiert. Dabei ist e ein beliebiger Ausdruck des Zwischencodes oder • kein Pfad von (0,0) nach (j,i) existiert. • Es kann neuer toter Code entstehen. 2: t0 := 8 y := 8 Darf nicht gestrichen werden. 3: t0 := 7 x := 7 t1 := 7 t2 := 8 t3 := 7 + 8 z := 15 4: t1 := x t2 := 8 t3 := t1 + 8 x := t3 Kann gestrichen werden. 1: t0 := 8 t1 := x …
Information zur Eliminierung toten Codes • Berechnung der Information Lebendig: LE(x) = out(x) für Knoten x im Steuerflussgraphen S = (N,E,q,s) durch eine Datenflussanalyse mit den Parametern: • Menge aller Informationen: (V) • Rückwärtsanalyse mit := • gen(bi) := {v | irj(i) ist Verwendung von v und für alle 0 k < j gilt: irk(i) ist keine Definition von v} • kill(bi) := {v | v wird in bi definiert} 2: t0 := 8 y := 8 "x" 3: t0 := 7 x := 7 t1 := 7 t2 := 8 t3 := 7 + 8 z := 15 4: t1 := x t2 := 8 t3 := t1 + 8 x := t3 "x" "x" 1: t0 := 8 t1 := x …
Eliminierung toten Codes im Basisblock • Entferne die Anweisung x := expr an Position i im Basisblock b, falls: • x an Position j > i definiert wird und für alle Anweisungen irk(b) mit i < kj gilt: irk(b) verwendet x nicht oder • x out(b) und in keiner Anweisung irk(b) mit i < k < #b wird x verwendet. • Durch das Entfernen einer Anweisung x := expr entfällt die Verwendung der Variablen in expr. • Konsequenz: Wiederholung der Eliminierung toten Codes erforderlich. 2: t0 := 8 y := 8 2: "x" 3: t0 := 7 x := 7 t1 := 7 t2 := 8 t3 := 7 + 8 z := 15 4: t1 := x t2 := 8 t3 := t1 + 8 x := t3 3: x := 7 4: t1 := x t3 := t1 + 8 x := t3 "x" "x" 1: t0 := 8 t1 := x … 1: t0 := 8 t1 := x …
Weitere Anwendung der Information Lebendig • Registerallokation: • Treffen der Spill-Entscheidung in Basisblöcken. • Bestimmung der zu sichernden Variablen am Ende eines Basisblocks. • Globale Registerallokation durch Graphfärbung: • Konstruktion eines Interferenzgraphen • Variablen sind Knoten • Eine Kante zwischen zwei Knoten existiert gdw. es eine Programmposition gibt, an der beide Variablen lebendig sind. • Färbung des Interferenzgraphen liefert eine Registerallokation (Jede Farbe entspricht einem Prozessorregister). • Details…
Globale Eliminierung gemeinsamer Teilausdrücke • Die Zuweisung iri(j) = "x := e" mit dem Ausdruck e kann durch x := t ersetzt werden, falls: • auf allen Pfaden von Programmposition (0,0) zur Programmposition (j,i), eine Anweisung irn(m) = "y := e" existiert und • es auf allen Pfaden von Positionen (m,n) zur Position (j,i) keine definierenden Anweisungen für die Verwendungen in e gibt und • an allen Positionen (m,n+1) die Anweisung "t := y" eingefügt wird. • Konsequenz: Es entstehen neue Kopierbefehle. y := 4 * i x := 6 * j Hier darf 6 * j ersetzt werden. j := j +1 m := 5 * y n := 6 * j x := x * 5 i := i + 1 Hier darf 6 * j nicht ersetzt werden. n := 6 * j m := i + 1
Information zur globalen Eliminierung gemeinsamer Teilausdrücke • Berechnung der Information Verfügbare Ausdrücke AE(x) = in(x) für Knoten x im Steuerflussgraphen S = (N,E,q,s) durch eine Datenflussanalyse mit den Parametern: • Menge aller Informationen: ({e | e ist Ausdruck in einem Zwischencodebefehl}) • Vorwärtsanalyse mit := • gen(bi) := {e | irj(i) hat die Form x := e und für alle jk < #bi gilt: irk(i) definiert keine in e verwendete Variable} • kill(bi) := {e | Variable im Ausdruck e wird in bi definiert} 6*j 2: y := 4 * i x := 6 * j 4*i, 6*j 3: j := j +1 4: m := 5 * y n := 6 * j x := x * 5 i := i + 1 1: n := 6 * j m := i + 1
Globale Eliminierung gemeinsamer Teilausdrücke in Basisblöcken • Ersetze die Anweisung x := e an Position i im Basisblock b durch x := t falls: • die Anweisung irj(b) die Form y := e hat und j < i und für alle Anweisungen irk(b) mit jk < i gilt: irk(b) definiert keine Variable, die in e verwendet wird oder • e in(b) und in keiner Anweisung irk(b) mit 0 k < i wird eine Variable definiert, die in e verwendet wird. • Nach den Anweisungen y := e, die e berechnen, wird t := e eingefügt (t ist neue Variable). • Durch Ersetzung mit x := t entstehen neue Kopieranweisungen. • Konsequenz: Wiederholung der Copy-/Constant-Propagation erforderlich. y := 4 * i x := 6 * j t := x 6*j 2: y := 4 * i x := 6 * j 4*i, 6*j 3: j := j +1 4: m := 5 * y n := 6 * j x := x * 5 i := i + 1 j := j +1 m := 5 * y n := t x := x * 5 i := i + 1 1: n := 6 * j m := i + 1 n := 6 * j m := i + 1
Entfernen schleifeninvarianter Berechnungen • Es sei L die Menge der Knoten des Steuerflussgraphen, die auf einem Zykel liegen und d Lder einzige Knoten der nicht zu L gehört und eine Kante zu einem Knoten in L besitzt (d dominiert L). • Eine Anweisung iri(j) = "x := e" kann im Block bj Ldurch x := t ersetzt und am Ende des Blocks d t := e eingefügt werden, falls kein Block in L eine Definition einer Verwendung in e enthält. • Um eine spekulative Ausführung von e zu vermeiden, Einfügen eines neuen Basisblocks auf der Kante von d nach L und Einfügen von t := e in diesen neuen Basisblock. y := 4 * i x := 6 * j t := x 5*y ist schleifeninvariant i und x sind nicht schleifeninvariant j := j +1 m := 5 * y n := t x := x * 5 i := i + 1 n := 6 * j m := i + 1
Informationen zum Finden schleifeninvarianter Berechnungen • Berechnung der Information Erreichende Definiton RD(x) = in(x) für Knoten x im Steuerflussgraphen S = (N,E,q,s) durch eine Datenflussanalyse mit den Parametern: • Menge aller Informationen: (()V) • Vorwärtsanalyse mit := • gen(bi) := {((i,j),v) | irj(i) ist letzte Definition von v in bi} • kill(bi) := {((m,n),v) | (m,n) ist Programmposition und bi enthält eine Definition von v} y := 4 * i x := 6 * j t := x ((2,0),y),((2,1),x),((2,2),t), ((4,0),m),((4,1),n),((4,2),x),((4,3),i) 2: ((2,0),y),((2,1),x),((2,2),t) 3: j := j +1 4: m := 5 * y n := t x := x * 5 i := i + 1 ((2,0),y),((2,1),x),((2,2),t),((3,0),j), ((4,0),m),((4,1),n),((4,2),x),((4,3),i) 1: n := 6 * j m := i + 1
Entfernen schleifeninvarianter Berechnungen in L • Finde in L Anweisungen der Form x := e, wobei e nur Konstanten und Variablen v enthält, die außerhalb der Blöcke von L definiert wurden. • Ersetze x := e durch x := inv (inv ist neue Variable) und füge am Ende von d die Anweisung inv := e ein. • Es entstehen neue Kopieranweisungen. • Konsequenz: Wiederholung von Konstanten- und Kopierpropagierung. y := 4 * i x := 6 * j t := x inv := 5 * y y := 4 * i x := 6 * j t := x ((2,0),y),((2,1),x),((2,2),t), ((4,0),m),((4,1),n),((4,2),x),((4,3),i) 2: 3: j := j +1 4: m := 5 * y n := t x := x * 5 i := i + 1 j := j +1 m := inv n := t x := x * 5 i := i + 1 1: n := 6 * j m := i + 1 n := 6 * j m := i + 1
Weitere Anwendung der Information Erreichende Definitionen • Erkennung der Benutzung einer Variablen vor ihrer Definition. • Definiere out(b0) := {((0,0),v) | v hat Verwendung im Steuerflussgraph}. • Berechne Erreichende Definitionen. • Falls bei einer Verwendung von v die Information ((0,0),v) vorhanden ist, dann kann es einen Pfad zu dieser Verwendung geben, auf dem v nicht initialisiert wird.
Strength-Reduction in Schleifen • Es sei L die Menge der Blöcke einer Schleife und d der Schleifenkopf (Dominator von L) • Suchen einer Variablen i (Induktionsvariablen), die in jedem Schleifendurchlauf um eine Konstante c erhöht wird. • Suchen nach einer Berechnung y := f(i), wobei f(i + c) – f(i) = di • Statt f(i) in jeder Iteration zu berechnen, wird in jeder Iteration zu ydi addiert. i := 0 y := 4 * i x := 6 * j x' := x m := 5 * y n := x' i ist Induktionsvariable und x schleifeninvariant. Dann wird z in jeder Iteration um 2*x größer. j := j +1 z := x * i i := i + 2 … n := 6 * j m := i + 1
Strength-Reduction im Schleifenkörper • Finden von Anweisungen der Form z := invi oder z := iinv, wobei i eine Induktionsvariable ist, die in jedem Schleifendurchlauf um cexpr erhöht wird und inv schleifeninvariant ist. • Füge am Ende von d ein: • dz := invcexpr und nz := invi • Im Schleifenkörper : • Füge nach der Anweisung i := i + cexpr die Anweisung nz := nz + dz ein und • ersetze z := invi durch z := nz. • Dadurch kann nz selbst wieder zu einer Induktionsvariablen werden. Konsequenz: Wiederholung der Strength-Reduction. i := 0 y := 4 * i x := 6 * j x' := x m := 5 * y n := x' dz := x * 2 nz := x * i i := 0 y := 4 * i x := 6 * j x' := x m := 5 * y n := x' i ist Induktionsvariable und x schleifeninvariant. Dann wird z in jeder Iteration um 2*x größer. j := j +1 z := x * i i := i + 2 … j := j +1 z := nz nz := nz + dz i := i + 2 nz und z sind Induktions-variable. n := 6 * j m := i + 1 n := 6 * j m := i + 1
Komplexbeispiel: Matrixmultiplikation innerLoop: t0 := &c t1 := j t2 := n t3 := t1*t2 t4 := i t5 := t3+t4 t6 := t0+t5 t7 := @t6 t8 := &a t9 := j t10:= n t11:= t9*t10 t12:= k t13:= t11+t12 t14:= t8+t13 t15:= @t14 t16:= &b t17:= k t18:= n t19:= t17*t18 t20:= i t21:= t19+t20 t22:= t16+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 t26 := &c t27 := j t28 := n t29 := t27*t28 t30 := i t31 := t29+t30 t32 := t26+t31 @t32:= t25 Quelltext für Matrixmultiplikation: for(i = 0; i < n; i = i+1) { for(j = 0; j < n; j = j+1) { c[j][i] = 0.0; for(k = 0; k < n; k = k+1) { c[j][i] = c[j][i] + a[j][k] * b[k][i] } } } Erzeugter Zwischencode
Konstanten- und Kopierpropagierung und Eliminierung toten Codes innerLoop: t0 := &c t1 := j t2 := n t3 := t1*t2 t4 := i t5 := t3+t4 t6 := t0+t5 t7 := @t6 t8 := &a t9 := j t10:= n t11:= t9*t10 t12:= k t13:= t11+t12 t14:= t8+t13 t15:= @t14 t16:= &b t17:= k t18:= n t19:= t17*t18 t20:= i t21:= t19+t20 t22:= t16+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 t26 := &c t27 := j t28 := n t29 := t27*t28 t30 := i t31 := t29+t30 t32 := t26+t31 @t32:= t25 innerLoop: t3 := j*n t5 := t3+i t6 := &c+t5 t7 := @t6 t11:= j*n t13:= t11+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 t29 := j*n t31 := t29+i t32 := &c+t31 @t32:= t25 innerLoop: t3 := j*n t5 := t3+i t6 := &c+t5 t7 := @t6 t11:= j*n t13:= t11+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 t29 := j*n t31 := t29+i t32 := &c+t31 @t32:= t25 Copy-/Constant-Propagation und anschließende Dead-Code-Elimination
Common Subexpression Elimination innerLoop: t3 := j*n t5 := t3+i t6 := &c+t5 t7 := @t6 t11:= j*n t13:= t11+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 t29 := j*n t31 := t29+i t32 := &c+t31 @t32:= t25 innerLoop: t3 := j*n s1 := t3 s0 := t3 t5 := t3+i t6 := &c+t5 t7 := @t6 t11:= s0 t13:= t11+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 t29:= s1 t31 := t29+i t32 := &c+t31 @t32:= t25 innerLoop: t3 := j*n t5 := t3+i t6 := &c+t5 t7 := @t6 t13:= t3+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 t31 := t3+i t32 := &c+t31 @t32:= t25 innerLoop: t3 := j*n t5 := t3+i t6 := &c+t5 t7 := @t6 t13:= t3+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 t32 := &c+t5 @t32:= t25 innerLoop: t3 := j*n t5 := t3+i t6 := &c+t5 t7 := @t6 t13:= t3+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @t6:= t25 Eliminierung des gemeinsamen Teilausdrucks j*n Eliminierung des gemeinsamen Teilausdrucks t3+i und anschließende Kopierpropagierung und anschließende Eliminierung toten Codes Eliminierung des gemeinsamen Teilausdrucks &c+t5 und anschließende Kopierpropagierung und anschließende Eliminierung toten Codes Kopierpropagierung und anschließende Eliminierung toten Codes c[j][i] = c[j][i] + a[j][k] * b[k][i]
Schleifeninvariante Ausdrücke entfernen k := 0 inv0 := j*n k := 0 inv0 := j*n k := 0 k := 0 innerLoopTest: t53:= k<n if t53 then innerLoop goto innerLoopEnd innerLoop: t3 := j*n t5 := t3+i t6 := &c+t5 t7 := @t6 t13:= t3+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @t6:= t25 t35:= k+1 k := t35 goto innerLoopTest innerLoopEnd: ... t53:= k<n if t53 then 3 t53:= k<n if t53 then 3 t53:= k<n if t53 then 3 t3 := j*n t5 := t3+i t6 := &c+t5 t7 := @t6 t13:= t3+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @t6:= t25 t35:= k+1 k := t35 t3 := inv0 t5 := t3+i t6 := &c+t5 t7 := @t6 t13:= t3+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @t6:= t25 t35:= k+1 k := t35 t5 := inv0+i t6 := &c+t5 t7 := @t6 t13:= inv0+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @t6:= t25 t35:= k+1 k := t35 ... ... ... for(k = 0; k < n; k = k+1) { c[j][i] = c[j][i] + a[j][k] * b[k][i] }
Schleifeninvariante Ausdrücke entfernen k := 0 inv0:= j*n inv1:= inv0+i inv2:= &c+inv1 k := 0 inv0 := j*n k := 0 inv0:= j*n inv1:= inv0+i t53:= k<n if t53 then 3 t53:= k<n if t53 then 3 t53:= k<n if t53 then 3 t5 := inv0+i t6 := &c+t5 t7 := @t6 t13:= inv0+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @t6:= t25 t35:= k+1 k := t35 t6 := &c+inv1 t7 := @t6 t13:= inv0+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @t6:= t25 t35:= k+1 k := t35 t7 := @inv2 t13:= inv0+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @inv2:= t25 t35:= k+1 k := t35 ... ... ...
Bestimmung der Induktionsvariablen k := 0 inv0:= j*n inv1:= inv0+i inv2:= &c+inv1 k := 0 inv0:= j*n inv1:= inv0+i inv2:= &c+inv1 t53:= k<n if t53 then 3 t53:= k<n if t53 then 3 t7 := @inv2 t13:= inv0+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @inv2:= t25 t35:= k+1 k := t35 t7 := @inv2 t13:= inv0+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @inv2:= t25 t35:= k+1 k := t35 ... ...
Strength-Reduction k := 0 inv0:= j*n inv1:= inv0+i inv2:= &c+inv1 d := 1*n nn := k*n k := 0 inv0:= j*n inv1:= inv0+i inv2:= &c+inv1 t53:= k<n if t53 then 3 t53:= k<n if t53 then 3 t7 := @inv2 t13:= inv0+k t14:= &a+t13 t15:= @t14 t19:= k*n t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @inv2:= t25 t35:= k+1 k := t35 t7 := @inv2 t13:= inv0+k t14:= &a+t13 t15:= @t14 t19:= nn nn := nn + d t21:= t19+i t22:= &b+t21 t23:= @t22 t24:= t15*t23 t25:= t7+t24 @inv2:= t25 t35:= k+1 k := t35 cexpr für k ist 1 ... ...
Rahmenwerk zur Datenflussanalyse (DFA) • DFA-Rahmenwerk: (D, I, , F) besteht aus: • Der Richtung D der Analyse (vorwärts oder rückwärts) • Einem Halbverband (I, ), d.h. für alle x, y, z I • x x = x • x y = y x • x (y z) = (x y) z • es ex. ein I, so dass für alle x I gilt x = x • Einer Menge F, die für jeden Basisblock des Steuerflussgraphen eine Transferfunktion f : I I enthält, die monoton und stetig ist. • Induzierte Ordnung auf I: x y gdw. x y = y • Voraussetzungen für den Fixpunktsatz von Tarski und Knaster sind erfüllt. Damit existiert der kleinste Fixpunkt und kann durch Iteration berechnet werden, weil: • (I, ) ist eine vollständig geordnete Menge, weil für alle x, y I gilt: x y ist kleinste oberer Schranke von x und y. • Jedes f F ist monoton und stetig.
(I,) ist eine geordnete Menge • Reflexivität:x x gilt wegen x x = x. • Antisymmetrisch:x y und y x x y = y und y x (= x y) = x x = y. • Transitivität:x y und y z x y = y und y z = z z = (x y) z = x (y z) = x z x z.
(I,) ist eine vollständig geordnete Menge • Wir zeigen: Für alle x, y I ist z = x y die kleinste obere Schranke von x und y: • x z x z = x (x y) = (x x) y) = x y = z. • y z y z = y (x y) = y (y x) = (y y) x) = y x = x y = z. • Angenommen x u und y u, dann x u = u und y u = u und z u = u u = u u = x u y u = x u y = z u z u • Damit ist für jede endliche nicht leere Kette K I mit K = {k1, k2, k3,…,kn} k1 k2 k3 … kn die kleinste obere Schranke von K.
Fixpunktiteration • Es ist der Fixpunkt einer Funktion F(out(b0),…,out(bm)) = F((out(b0),…,out(bm))) bzw. (in(b0),…,in(bm)) = F((in(b0),…,in(bm))) gesucht. • Durch den Steuerflussgraphen wird für jede Komponente out(bi) bzw. in(bi) eine Mengengleichung erzeugt: • Vorwärtsanalyse:out(bi) = fi(out(bk1)…out(bkn)), wobei bk1,…,bkn die Steuerflussvorgänger von bi sind und 0 kj m. • Rückwärtsanalyse:in(bi) = fi(in(bk1)…in(bkn)), wobei bk1,…,bkn die Steuerflussvorgänger von bi sind und 0 kj m. • Wir schreiben kurz: (b0,…,bm) = F((b0,…,bm)) und abstrahieren von der Richtung. • Die induzierte Ordnung auf I wird zu einer Ordnung auf Im+1 erweitert:(a0,…,am) (b0,…,bm) gdw. j: 0 j m aj bj. Analog wird der Operator für einen Vektor erweitert. • Unter der Voraussetzung, dass die Transferfunktionen fi monoton und stetig sind, ist es F auch (Beweis folgt).
Monotonie von F • F monoton gdw. aus (a0,…,am) (b0,…,bm) folgt F(a0,…,am) F(b0,…,bm). • a = (a0,…,am) (b0,…,bm) = b gdw. i: ai bi. • Für jedes ai und bi gilt nun: • Es seien ak1,…,akn bzw. bk1,…,bkndie Komponenten in a bzw. b, die den in/out-Resultaten der Steuerflussvorgänger/-nachfolger von ai bzw. bi entsprechen. • Wegen akj bkj ist ak1…akn bk1…bkn. (Bew. durch Induktion; im Schritt: a b ab = b und a' b' a'b' = b' bb' = ab a'b' = (aa') (bb') (aa') (bb')). • Wegen der Monotonie der Transferfunktion fi gilt dann fi(ak1…akn) fi(bk1…bkn) • Und damit F(a0,…,am) F(b0,…,bm)
Stetigkeit von F • F stetig gdw. F(a0,…,am) F(b0,…,bm) = F(a0b0,…,ambm). • Es seien ai' und bi' die i-ten Komponenten des Resultats von F(a) bzw. F(b), d.h.: b'i = fi(bk1,…,bkn ) und a'i = fi(ak1,…,akn) • Wegen der Stetigkeit von fi gilt: a'i b'i= fi(ak1,…,akn)fi(bk1,…,bkn) = fi(ak1)… fi(akn)fi(bk1)…fi(bkn) = fi(ak1) fi(bk1)…fi(akn)fi(bkn) = fi(ak1bk1)…fi(aknbkn) = fi(ak1bk1,…,aknbkn). • fi(ak1bk1,…,aknbkn) ist auch die i-te Komponente des Resultats von F(a0b0,…,ambm)
Ende der Optimierung Ende der Vorlesung
Beispiel Steuerflussgraph/Interferenzgraph d := 0 a := 1 c := 3 (d) (a,d) (a,d,c) u v t w e f := c (a,c,f,d) d:= d+1 r := 2*d s := 3*c t := r+s e := t+5 d:= a+f u := c v := u+1 w := v+1 e := v (c,d) (c,d,r) (d,s,r) (d,t) (d,e) (c,d) (d,u) (d,v) (d,w) (d,e) a d s f c r z c:= d+3 a := e*c (d,c) (d,c,a) z:= a+d (z)
Färbung des Interferenzgraphen u • Gesucht Färbung I : V R mit I(u) I(v) gdw. {u,v} E, wobei R die Menge der Prozessorregister ist. • Finden einer Färbung durch schrittweises Entfernen von Knoten v mit adjazenten Kanten, falls v mit echt weniger als |R| Knoten adjazent ist: • Falls kein Knoten mit weniger als |R| Nachbarn existiert, dann Spillentscheidung treffen. • Falls I zum leeren Graphen reduziert wurde, Einfügen der Knoten mit Kanten in umgekehrter Reihenfolge und Färben der Knoten. v t w e s a d f c r z I = (V, E), R = {0,1,2} w w v v u u t t e e a a d d s s d4 d4 c f f c r r z z w v u t e d4 z d1 d2 d3 s r c f a d1 d1 d2 d2 d3 d3
Spillen Spillen von d d := 0 @&d := d a := 1 c := 3 (d) () (a,d) (a,c) d := 0 a := 1 c := 3 (d) (a,d) (a,d,c) f := c (a,c,f) f := c (a,c,f,d) d1 := @&d d1:= d1+1 @&d := d1 r := 2*d s := 3*c t := r+s e := t+5 d2 := a+f @&d := d2 u := c v := u+1 w := v+1 e := v (c,d1) (c,d1) (c) (c,r) (s,r) (t) (e) (c,d2) (c) (u) (v) (w) (e) d:= d+1 r := 2*d s := 3*c t := r+s e := t+5 d:= a+f u := c v := u+1 w := v+1 e := v (c,d) (c,d,r) (d,s,r) (d,t) (d,e) (c,d) (d,u) (d,v) (d,w) (d,e) d3 := @&d c:= d3+3 a := e*c (d3,c) (c) (c,a) c:= d+3 a := e*c (d,c) (d,c,a) d4 := @&d z:= a+d (d4) (z) z:= a+d (z)
Auswahl der Spillvariablen • Falls ein Interferenzgraph nicht weiter reduziert werden kann, dann wird aus den verbleibenden Knoten der ausgewählt, für denminimal ist. Dabei sind DefUse(v) alle Programmpositionen, an denen v verwendet/definiert wird. und deepth(p) die Schachtelungstiefe der innersten Schleife, die die Programmposition p enthält. • Vor/nach allen Verwendungen/Definitionen von v wird Spillcode in den Zwischencode eingefügt. • Interferenzgraph muss neu konstruiert werden. • Es können mehrere solcher Iterationen erforderlich sein, bis eine Färbung des Interferenzgraphen gefunden wird. • Variablen zwischen deren Definition und Verwendung keine anderen Variablen sterben, werden nicht gespillt.