Präsentation herunterladen
Die Präsentation wird geladen. Bitte warten
Veröffentlicht von:Vergil Nett Geändert vor über 10 Jahren
1
Grundlagen der Übersetzung und Optimierung von Programmiersprachen
Seminar: Techniken der Code-Optimierung für moderne Rechnerarchitekturen Grundlagen der Übersetzung und Optimierung von Programmiersprachen Betreuer: Martin Schulz Referent: Markus Ibba
2
Übersicht Grundlagen der Übersetzung eines Programmes
Konzepte zur Optimierung Ausblick Die Grundlagen bieten eine Einsicht in die allgemeine Funktionsweise eines Compilers. Hierbei werden noch keine Optimierungstechniken berücksichtigt. Die Konzepte der Optimierung erklären den Ablauf eines optimierenden Compilers. Anhand eines Beispiels wird eine einfache Codetransformation durchgeführt. Schließlich werden einige gängige Optimierungen besprochen.
3
Grundlagen der Übersetzung eines Programmes
Übersicht eines Übersetzungsvorganges Analyse des Quellprogramms Lexikalische Analyse Syntaxanalyse Semantische Analyse Fehlerbehandlung In der Übersicht sehen wir uns einen Graphen an, welcher einen Überblick über die einzelnen Phasen des Compilers gibt Die Analysearten werden im Folgenden näher besprochen. Neben der Symboltabelle werden wir auch kurz auf Fehler eingehen, welche während der Compilierung auftreten können. Die Fehlerbehandlung zeigt Möglichkeiten, auf gefundene Fehler während der Analyse zu reagieren.
4
Übersicht eines Übersetzungsvorganges
Die Symboltabelle ist die wichtigste Datenstruktur die der Compiler verwendet. Sie wird während der lexikalischen Analyse aufgebaut, und steht für die restliche Compiler-Phasen zur Verfügung. Die Schnittstelle zum Betriebssystem beinhaltet alle IO-Operationen, welche während der Compilierung durchgeführt werden.
5
Analyse des Quellprogramms
Lexikalische Analyse (Scanner) (=Lineare Analyse) Lesen des Zeichenstromes Aufteilung in Symbole (tokens) Aufbau der Symboltabelle Während der lexikalischen Analyse wird der Zeichenstrom (Quellprogramm) von links nach rechts gelesen und in Symbole (tokens) aufgeteilt. Die Leerzeichen werden dabei im allgemeinen entfernt. Hier erfolgt der Aufbau der Symboltabelle.
6
Lexikalische Analyse (Scanner)
Aufbau der Symboltabelle position = initial + rate * 100 EOS steht hier für End Of String. Die einzelnen Bezeichner werden auch Lexeme genannt. In die Symboltabelle wird für jedes Symbol ein Pointer auf das entsprechende Lexem eingetragen. Dies ist eine stark vereinfachte Darstellung dieser Tabelle. Für die effiziente interne Verwaltung werden üblicherweise Hash-Funktionen verwendet. Gespeichert werden hier unter anderem verschiedene Attribute über das Symbol, wie der Name, den Typ (z.B. Integer) und die Größe in Bytes. Lexikalische Fehler treten z.B.: bei falsch geschriebenen Schlüsselwörtern und Operatoren auf.
7
Analyse des Quellprogramms
Syntaxanalyse (Parser) (= hierarchische Analyse) Beschreibung der Syntax der Programmiersprache (kontextfreie Grammatiken, BNF) Zusammenfassen der Symbole zu grammatikalischen Sätzen Über kontextfreie Grammatiken kann relativ einfach die hierarchische Struktur einer Programmiersprache beschrieben werden. Auf die Grammatiken und die BNF wird hier nicht näher eingegangen, da diese Bestandteil des Grundstudiums sind und für dieses Seminar als bekannt vorausgesetzt werden. Der Compiler faßt in dieser Phase die Symbole zu grammatikalischen Sätzen zusammen, welche durch einen Parse-Baum dargestellt werden. Hierbei werden die Symbole der Reihe nach (von links nach rechts) gelesen. In den meisten Fällen werden die Top-Down oder Bottom-Up Methoden verwendet, d.h. der Compiler baut den Parse-Baum entweder von der Wurzel nach unten auf, oder arbeitet sich von den Blättern zur Wurzel hoch. Syntaxfehler können zum Beispiel falsch geklammerte arithmetische Ausdrücke sein.
8
Syntaxanalyse (Parser)
Der Parse-Baum position = initial + rate * 100 Dies ist ein Beispiel eines aufgebauten Parse-Baumes. Da die Multiplikation vor der Addition ausgeführt wird, bildet rate * 100 hier eine Einheit.
9
Analyse des Quellprogramms
Semantische Analyse Konsistenz zwischen Deklaration und Definition (Methoden, Variablen) Statische Überprüfungen Typüberprüfung Überprüfung des Kontrollflusses Überprüfung auf Eindeutigkeit Die semantische Analyse benutzt die hierarchische Struktur, welche in der vorhergehenden Phase erzeugt wurde, um für die verschiedenen Anweisungen und Ausdrücke die Operatoren und Operanden zu bestimmen. Konsistenz zwischen Deklarationen und Definitionen von Variablen und Methoden bedeutet, daß es für jeden auftretenden Bezeichner eine explizite Deklaration geben muß, daß diese Deklaration korrekt ist und daß es z.B.: keine Doppeldeklarationen geben darf. Die Typüberprüfung ermittelt, ob Operanden eines Operators zulässig sind. Ist dies nicht der Fall (z.B.: Wenn eine Float-Zahl zur Indizierung eines Arrays verwendet wird), so meldet der Compiler einen Fehler. Überprüfung des Kontrollflusses: Anweisungen, durch welche der Kontrollfluß eines Konstrukts verlassen wird, wie z.B: die Break-Anweisung, durch welche ein While-Konstrukt verlassen werden kann, müssen sich innerhalb eines solchen Konstruktes befinden. Existiert eine solche Anweisung nicht, tritt ein Fehler auf. (in C) Überprüfung auf Eindeutigkeit: z.B: dürfen Elemente eines Aufzählungstypen nicht identisch sein. (in Pascal, in C)
10
Fehlerbehandlung Recovery-Strategien Panische Recovery
Konstrukt-orientierte Recovery Fehlerproduktion Globale Korrektur Alle Fehler, die während der Analysephase des Compilers auftreten, müssen in irgendeiner Art und Weise behandelt und dem Benutzer mitgeteilt werden. Natürlich ist es wünschenswert, daß der Compiliervorgang nicht schon nach einem gefundenen Fehler abbricht. Hierfür gibt es verschiedene Recovery-Strategien: Panische Recovery: Hierbei werden bei einem Fehler alle weiteren Symbole überlesen, bis ein sogenanntes synchronisierendes Symbol auftritt (In C z.B. Der Strichpunkt). Konstrukt-orientierte Recovery: Hier wird versucht lokale Fehler zu korrigieren. Typische Korrekturen wären die Ersetzung eines Kommas durch ein Semikolon oder Einfügen von fehlenden Semikolon. Hierbei besteht die Gefahr durch zu häufiges Einfügen in eine Endlos-Schleife zu geraten. Fehlerproduktion: Bei häufig auftretenden Fehlern, welche schon bekannt sind, kann in die Grammatik eine Produktion für diese Fehler einbaut werden, welche dann während des Parsens zur Anwendung kommt, und das entsprechende Konstrukt als falsch erkennt. Globale Korrektur: Hier werden Algorithmen verwendet, welche versuchen einen Eingabestring global mit minimalen Änderungen zu korrigieren. Dies ist momentan nur theoretisch, da solche Algorithmen zu kostspielig sind.
11
Konzepte zur Optimierung
-> Ziel: Laufzeit und/oder Größe eines Programmes reduzieren Übersicht eines Übersetzungsvorganges eines optimierenden Compilers Analyse Codetransformation Optimierungstechniken Die Übersicht bietet einen Graphen für den Übersetzungsvorgang eines optimierenden Compilers. Wir werden kurz auf erweiterte Analysen und schließlich auf die Code-Transformation selber eingehen.
12
Idee einer Optimierung
Anwendungsprogrammierer braucht kein umfangreiches Wissen über die Maschinenarchitektur auf der er programmiert. Hardwarehersteller brauchen nur noch Schnittstelle für den Compiler selber zu designen. Dies sind zwei grundsätzliche Ideen, warum Optimierungen durchgeführt werden sollen. Diese sind allerdings so noch nicht realisiert und stellen somit noch unerreichte Ziele dar.
13
Übersicht eines Übersetzungsvorganges
Der erste Kasten umfaßt die Analysearten, welche auf den vorhergehenden Folien besprochen wurden. Dies bezeichnen wir als ‚Front-End‘ eines optimierenden Compilers. In der eigentlichen Analyse des optimierenden Compilers wird der Kontrollfluß des Programmes untersucht, und dabei der sogenannte ‚Control Flow Graph‘ (CFG) aufgebaut, welchen wir uns an einem Beispiel-Programm kurz anschauen werden. Die Datenfluß- und Abhängigkeitsanalyse wird hier nur kurz erwähnt. Das Resultat ist der ‚Program Dependence Graph‘ (PDG) und die ‚Static Single-Assignment‘ (SSA) Form. Der Kasten Code-Transformation repräsentiert die Optimierungen, welche die Datenflußanalyse benötigen. ‚Back-End‘ bezeichnet die Codeerzeugung bis hin zum ausführbaren Programm.
14
‚Control dependence‘ 1: if (3 == a) 2: b = 10;
Abhängigkeit zwischen Ausdruck 1 und 2. -> Generierung des ‚Control Flow Graph‘ (CFG). Der Compiler benötigt ein umfassendes Verständnis des zu optimierenden Programmes, um die passenden Codestücke und dafür geeignete Optimierungen zu finden. Hierzu ist es nötig den Kontrollfluß des Programmes, das heißt jede Kontrollabhängigkeit zwischen den Codestücken, zu erkennen. In unserem Beispiel besteht eine Abhängigkeit zwischen Zeile 1 und 2, da für den Fall daß 3 == a der Kontrollfluß an Zeile 2 (b = 10) übergeben wird. Der CFG ist ein Graph der die Kontrollabhängigkeiten eines Programmes darstellt.
15
CFG - Control Flow Graph
Beispiel eines CFG. Die Knoten repräsentieren Basis-Blöcke. Ein Basis-Block ist ein Codestück, in das der Programmablauf nur an einer festen Stelle (Beginn des Blockes) eintreten und das er nur an einer festen Position (Ende des Blockes) verlassen kann. Jeder Knoten hat eine Kante zu jedem anderen, zu dem er die Kontrolle übergeben kann.
16
‚Data dependence‘ Die Datenflußanalyse wird im 3. Vortrag behandelt
a = c * 10; d = 2 * a + c; ‚flow dependence‘ e = f * 4 + g; g = 2 * h; ‚anti dependence‘ a = b * c; a = d + e; ‚output dependence‘ Die Datenflußanalyse wird im 3. Vortrag behandelt Hier werden nur kurz drei Arten der Datenabhängigkeit vorgestellt. 1) ‚flow dependence‘, wird auch ‚true dependence‘ genannt. In diesem Beispiel besteht eine Abhängigkeit zwischen den beiden Zeilen, da in a erst der Wert geschrieben wird, bevor er für die Variable d gelesen wird. 2) ‚anti dependence‘: Hier wird g für die Variable e zuerst gelesen, und danach überschrieben. 3) ‚output dependence‘: Beide Anweisungen schreiben in dieselbe Variable. Diese kurze Vorstellung soll hier reichen, da die Datenflußanalyse und die Abhängigkeiten im dritten Vortrag genauer besprochen werden. Für die meisten Optimierungen ist die Datenflußanalyse dringend notwendig, damit der Compiler Kenntnis erlangt, inwieweit sich die Werte der Variablen während des Programmablaufes an welcher Stelle ändern.
17
Codetransformation -> Bestandteil jeder Optimierung
Allgemeiner Ablauf einer Transformation Beispiel einer Transformation Jede Optimierung nimmt eine bestimmte Art Transformation an einem Codestück vor. Bevor wir verschiedene Optimierungstechniken kennenlernen werden, wenden wir uns kurz dem Ablauf einer Transformation und einem kleinen Beispiel zu.
18
Allgemeiner Ablauf einer Transformation
Programmstück finden, welches sich für die Optimierung eignet Sicherstellen, daß die Transformation die Semantik des Programmes nicht ändert Programm transformieren
19
Beispiel einer Transformation
Dies ist ein relativ einfaches Programmbeispiel. Es interessiert momentan nur, welche Transformation augenscheinlich durchgeführt werden könnte, ohne daß wir genau wissen welche Optimierung dies wäre. Bei der Besprechung der Optimierungen wird nochmal auf dieses Beispiel verwiesen.
20
Beispiel einer Transformation
Der schleifen-invariante Code b[k] wird vor die Schleife gestellt, damit er nur einmal ausgeführt werden muß. Obwohl diese Transformation augenscheinlich korrekt ist, und die Semantik des Programmes anscheinend nicht verändert, ist sie fehlerhaft und dürfte nicht durchgeführt werden.
21
Beispiel einer Transformation
Fehler 1: Überlauf Eingabe: b[k] = max. Floatzahl - 1.0; a[1] = -2.0; Überlauf bei C = b[k] + 2.0; Fehler 2: Ergebnisabweichung Unterschiede im Ergebnis durch die Vertauschung der Additionsreihenfolge Bei einer Eingabe von b[k] = maximale Floatzahl und a[1] = kommt es bei dem transformierten Programm auf jeden Fall zu einem Überlauf. Dieser würde zwar beim Originalprogramm auch auftreten, aber an einer anderen Stelle. Würde sich eine printf-Anweisung zwischen der Zuweisung und der Benutzung von C befinden, würde diese Transformation die Ausgabe des Programmes ändern. Da die Float-Zahlen nur Näherungen für die Real-Zahlen sind, kann die Vertauschung der Additionsreihenfolge eine Ergebnisänderung zur Folge haben.
22
Beispiel einer Transformation
Fehler 3: Zugriffsfehler Eingabe: k = m + 1; n = 0; Referenz zu b[k] nicht definiert. Tritt im Original durch n = 0 nicht auf. Bei einer Eingabe von k = m + 1 würde man mit b[m+1] auf eine Element außerhalb der Grenzen zugreifen. Ist nun n = 0, so würde das Originalprogramm nicht in die Schleife kommen, und somit keinen Zugriffsfehler melden. Beim transformierten Programm steht der Zugriff vor der Schleife und wird damit auch bei n = 0 durchgeführt.
23
Optimierungstechniken
Partial Evaluation (Early Optimizations) -> nur bei einigen Datenflußanalyse notwendig Redundancy Elimination -> Entfernung redundanter Codestücke -> Daten- und Kontrollflußanalyse notwendig Hier lernen wir einige Optimierungstechniken kennen, welche sowohl Größe als auch Laufzeit eines Programmstückes beeinflussen können. Es ist allerdings möglich, daß sich diese beiden Faktoren nicht zum Positiven verändern. In seltenen Fällen kann eine Optimierung (meist mehrere) auch die Größe und/oder Laufzeit negativ beeinflussen. Die Standardoptimierungstechniken, welche im Folgenden erläutert werden, können in zwei Gruppen aufgeteilt werden. Die Gruppe ‚Partial Evaluation‘ stellt Optimierungen vor, bei denen Berechnungen verschiedener Ausdrücke schon zur Kompilierungszeit durchgeführt werden. Einige davon brauchen keine Datenflußanalyse. ‚Redundancy Elimination‘ umfaßt Optimierungen, welche redundante Codestücke identifizieren und diese entfernen. Hierzu sind Daten- und Kontrollflußanalyse notwendig.
24
Partial Evaluation Constant Folding Algebraic Simplification
Reassociation Constant Propagation Copy Propagation Statement Substitution Induction Variable Elimination Function Cloning
25
Constant Folding Konstante Ausdrücke werden zur
Übersetzungszeit ausgerechnet. X = 3 * 2; -> X = 6; -> Es ist keine Datenflußanalyse notwendig Bei dieser Optimierung wird festgestellt, ob alle Operanden eines Ausdrucks konstant sind, um dann durch ihren Wert ersetzt werden zu können. Für Boolsche Werte ist diese Optimierung immer durchführbar. Handelt es sich bei den Operanden um Integer-Zahlen, so kann sie fast immer ausgeführt werden. Ausnahmen bilden hier die Fälle, welche beim Programmablauf Fehlermeldungen erzwingen, wie z.B. Division durch Null. In diesem Fall kann der Compiler eine Warnung ausgeben. Probleme können bei Float-Zahlen auftreten. Es muß sichergestellt sein, daß die Floating-Point Arithmetik des Compilers mit der des Prozessors übereinstimmt. Ansonsten kann eine Berechnung zur Kompilierzeit ein anderes Ergebnis liefern als zur Laufzeit des Programmes. Diese Optimierung ist zwar nicht von der Datenflußanalyse abhängig, ist aber viel effizienter, wenn sie in Kombination mit der Optimierung ‚Constant Propagation‘ durchgeführt wird.
26
Algebraic Simplification
Vereinfachung algebraischer Ausdrücke X = (Y + 1) / N; wobei N = 1; zu X = (Y + 1); -> Keine Datenflußanalyse notwendig Mit Hilfe dieser Optimierung können arithmetische Ausdrücke durch Anwendung algebraischer Regeln vereinfacht werden. Diese Optimierung ist unabhängig von der Datenflußanalyse.
27
Reassociation Umgruppierung von Additionen und Multiplikationen in einem Ausdruck Kann die Anzahl der ‚Common Subexpressions‘ vergrößern. Keine Datenflußanalyse notwendig Mit Hilfe von Assoziativ-, Kommutativ-, und Distributivgesetzen werden Ausdrücke, welche aus verschiedenen Summen und Multiplikationen bestehen umgeschrieben. Diese Optimierung wird meist in Zusammenhang mit der Vereinfachung von Index-Ausdrücken verwendet. Dies werden wir an einem Beispiel bei ‚Induction Variable Elimination‘ sehen. Diese Technik erhöht meist die ‚Common Subexpressions‘ im Code.
28
Constant Propagation Konstanten im Programm werden durch
ihren eigentlichen Wert ersetzt. Hierzu wird das Ergebnis der Datenflußanalyse benötigt. Diese Optimierung erfordert die Datenflußanalyse. Hierbei werden Konstanten durch das gesamte Programm ‚verfolgt‘ und dann durch ihren eigentlichen Wert ersetzt. Dies ist eine sehr wichtige Optimierung, und wird von nahezu jedem Compiler durchgeführt. Wie schon erwähnt, ist es sinnvoll ‚Constant Folding‘ in Zusammenhang mit dieser Optimierung anzuwenden. Interessant wird sie vor allem bei Schleifen, dessen Grenzen durch Konstante bestimmt werden. Kennt ein Compiler diese Grenzen können eventuell weitere Optimierungen durchgeführt werden. Beispielcode:
29
Copy Propagation Eliminiert redundante Kopien einer Variable.
-> Benötigt die Ergebnisse der Datenflußanalyse. -> Es wird eine Tabelle verwendet, um die einzelnen Instruktionen (copy instructions) zu speichern. Diese Optimierung entfernt redundante Kopien einer Variable. Hierbei werden die Variablennamen durch den gesamten Code ‚verfolgt‘ um redundante Kopien zu erkennen. Der Compiler verwendet dazu eine Tabelle, welche auf der kommenden Folie gezeigt wird. Hier kurz ein Beispiel dazu:
30
Copy Propagation - ACP Table of available copy instructions.
In Spalte 2 wird der Code vor der Transformation gezeigt. Spalte 4 enthält die Codestücke nach der Optimierung. In Spalte 3 (ACP) verwaltet der Compiler alle verfügbaren ‚Kopier-Instruktionen‘. Der Code in Position 1 kopiert b nach a. Somit merkt sich der Compiler <b,a> als copy instruction. Diese kann er dann an Position 2 anwenden, und b durch das ‚Original‘ a ersetzen. In Position 3 kommt eine neue Instruktion hinzu. Die Variable b wird nach d kopiert, und gleich durch a ersetzt. Die ACP erhält einen neuen Eintrag <d,a>. Im Code bei Position 4 wird d durch a ersetzt. Da b nun überschrieben wird, wird der Eintrag <b,a> aus den ‚available copy instructions‘ gestrichen. Position 5 kopiert d nach b. Hier kann wieder d durch a ersetzt werden, und der Eintrag <b,a> kommt wieder zur Spalte 3 hinzu.
31
Statement Substitution (Forward Substitution)
Ersetzung einer Variable durch ihre Definition. -> Allgemeine Form von ‚Copy Propagation‘ -> Kann die Analyse von Ausdrücken innerhalb einer Schleife vereinfachen Statement Substitution ist eine verallgemeinerte Form von ‚Copy Propagation‘. Eine Variable wird durch ihre Definition ersetzt. Diese Optimierung kann die Analyse von Ausdrücken in einer Schleife vereinfachen. Dies werden wir an folgendem Beispielcode nachvollziehen.
32
Induction Variable Elimination
-> Entfernen der Index-Variable - Folgt meist auf ‚Strength Reduction of Induction Variable Expressions‘ Als ‚Induction Variable‘ bezeichnet man eine Variable, deren Wert nur durch die Anzahl der Iterationen der einschließenden Schleife bestimmt wird. ‚Strength Reduction‘ ist eine Standard-Optimierungstechnik, welche aufwendige (kostspielige) Operationen durch ‚billigere‘ ersetzt. Eine der häufigsten Anwendungen ist die hier erwähnte Optimierung: ‚Strength Reduction of Induction Variable Expressions‘. Sie wird häufig zusammen mit der Optimierung ‚Induction Variable Elimination‘ angewandt, welche in folgendem Beispiel gezeigt wird.
33
Function Cloning Konstante Argumente einer Funktion
werden in einer Kopie durch ihren Wert ersetzt. -> Constant Propagation Dies erhöht zwar die Codegröße, die kopierten Funktionen können aber zum Teil wesentlich schneller ausgeführt werden. Wird eine Funktion mit einem konstanten Wert als Parameter aufgerufen, so kann der Compiler diese Funktion kopieren, und den entsprechenden Parameter entfernen. Alle Vorkommen von diesem werden durch diesen konstanten Wert ersetzt. Wiederum ist es sinnvoll, zuvor die Optimierung ‚Constant Propagation‘ durchzuführen. Durch diese Transformation wird natürlich die Codegröße erhöht, allerdings läßt sich trotzdem ein zum Teil erheblicher Geschwindigkeitsgewinn erreichen, wie das folgende Beispiel auf der nächsten Folie zeigt.
34
Function Cloning - Beispiel
Die Prozedur potenziert jedes Element des Array x mit p. Würde die Funktion der linken Seite mit dem konstanten Parameter p = 2 aufgerufen werden, so kann dieser Parameter entfernt werden, und alle Vorkommen von p werden durch eine 2 ersetzt. Dies würde bedeuten, daß alle Elemente x[i] quadriert werden. Der Compiler kann das erkennen, und der Ausdruck x[i] = x[i] ** 2 wird dann auch noch durch eine einzige Multiplikation ersetzt. Dies bringt bei diesem Beispiel eine ca fache Geschwindigkeitssteigerung.
35
Redundancy Elimination
Loop-invariant Code Motion Unreachable Code Elimination Useless Code Elimination Dead Variable Elimination Common-Subexpression Elimination
36
Loop-invariant Code Motion
Ausdrücke, welche ihren Wert in keiner Iteration ändern, werden außerhalb der Schleife gestellt. -> Datenflußanalyse notwendig Bei dieser Optimierung werden Ausdrücke, welche innerhalb einer Schleife stehen, und ihren Wert nicht ändern, vor die Schleife gestellt. Hierfür ist die Datenflußanalyse notwendig. Die fehlerhafte Transformation, welche wir zu Beginn betrachtet haben, wäre ein Beispiel für eine solche Optimierung. Ein gültiges Beispiel ist Folgendes:
37
Unreachable Code Elimination
Ein Codestück wird unerreichbar durch: Bedingungen,welche immer true oder immer false sind Schleifen, welche nie durchlaufen werden -> Datenflußanalyse notwendig Werden Codestücke gefunden, welche während des Programmablaufes nie erreicht werden können, so kann der Compiler diese entfernen. Beispiele wären hierfür Bedingungen, welche einen festen Wert haben, immer false oder immer true, oder auch Schleifen, welche nie durchlaufen werden. Hierzu ist natürlich die Datenflußanalyse notwendig. Wie wir an dem Beispiel bei ‚Dead Variable Elimination‘ sehen werden, ist es sinnvoll, zuvor andere Optimierungen, wie ‚Constant Propagation‘ durchzuführen.
38
Useless Code Elimination
‚Unnütze‘ Codestücke werden zum Teil durch andere Optimierungen (z.B.: Unreachable Code Elimination) verursacht. -> Datenflußanalyse notwendig Als Ursache der vorhin besprochenen ‚Unreachable Code Elimination‘ können Codestücke auftreten, welche ‚unnütz‘ sind, also keine Bedeutung für das restliche Programm besitzen. Diese werden auch durch den Compiler entfernt.
39
Dead Variable Elimination
Nicht benutzte Variablen werden entfernt. Folgt meist auf die Optimierungen: Unreachable Code Elimination Useless Code Elimination Durch die Datenflußanalyse kann festgestellt werden, ob die Variable noch benutzt wird. Diese Optimierung entfernt sogenannte ‚tote‘ Variablen. Dies sind solche, welche deklariert, aber nie mehr benutzt werden. Der häufigste Grund ist eine zuvor durchgeführte ‚Unreachable Code Elimination‘ und eine ‚Useless Code Elimination‘. Folgendes Beispiel veranschaulicht die letzten drei Optimierungen.
40
Common-Subexpression Elimination (CSE)
-> Identische Ausdrücke (Berechnungen) können gespeichert werden, damit der entsprechende Wert nicht neu berechnet werden muß. -> Datenflußanalyse notwendig Ein Ausdruck wird als ‚common subexpression‘ bezeichnet, wenn ein anderes Auftreten dieses Ausdrucks vorher berechnet wird, und die Operanden sich nicht ändern. Identische Ausdrücke können vom Compiler gespeichert werden, und bei nochmaligem Auftreten durch diesen gespeicherten Wert ersetzt werden. Dies vermeidet eine Neuberechnung. Die nächste Folie zeigt ein Beispiel einer CSE anhand eines CFG.
41
Common-Subexpression Elimination (CSE) - Beispiel
Hier wird der Ausdruck a+2 zweimal verwendet. Dieser kann in t1 gespeichert werden, und somit braucht in B1 der Variable b und in B3 der Variable d nur noch t1 zugewiesen werden.
42
Ausblick Weitere Optimierungen Laufzeitsysteme (Vortrag 8)
Function Inlining Schleifen-Optimierungen (Vortrag 4) Laufzeitsysteme (Vortrag 8) Literatur: 1) Advanced Compiler Design & Implementation, Steven S. Muchnick, 1997, Morgan Kaufmann Publishers, INC. San Francisco, Kalifornien 2) Compilerbau Teil 1, Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullmann, 2 Nachdruck 1992, Addison-Wesley GmbH 3) Übersetzerbau Theorie, Konstruktion, Generierung, R.Wilhelm, D.Maurer, 2. Auflage 1996/1997, Springer Verlag 4) Parallelizing Compilers, Implementation and Effectiveness, Karen L. Piper, Tech Report CSL-TR_91-XXX 5) Compiler Transformations for High-Performance Computing, David F. Bacon, Susan L. Graham, Oliver J. Sharp, Computer Science Division, University of California, Berkeley, Kalifornien 94720, Technical Report No. UCB/CSD
Ähnliche Präsentationen
© 2025 SlidePlayer.org Inc.
All rights reserved.