www.wikidata.de-de.nina.az
Kompilieren ist eine Weiterleitung auf diesen Artikel Zu weiteren Bedeutungen des Begriffs siehe Kompilation Ein Compiler auch Kompilierer von englisch compile zusammentragen bzw lateinisch compilare aufhaufen ist ein Computerprogramm das Quellcodes einer bestimmten Programmiersprache in eine Form ubersetzt die von einem Computer direkter ausgefuhrt werden kann Daraus entsteht ein mehr oder weniger direkt ausfuhrbares Programm Davon zu unterscheiden sind Interpreter etwa fur fruhe Versionen von BASIC die keinen Maschinencode erzeugen Teils wird zwischen den Begriffen Ubersetzer und Compiler unterschieden Ein Ubersetzer uberfuhrt ein Programm aus einer formalen Quellsprache in ein semantisches Aquivalent in einer formalen Zielsprache Compiler sind spezielle Ubersetzer die Programmcode aus hoheren Programmiersprachen in ausfuhrbare Maschinensprache einer bestimmten Rechnerarchitektur oder einen Zwischencode uberfuhren Diese Trennung zwischen den Begriffen Ubersetzer und Compiler wird nicht in allen Fallen vorgenommen Der Vorgang der Ubersetzung durch den Compiler wird als Kompilierung oder Umwandlung bezeichnet Das Gegenteil also die Ruckubersetzung von Maschinensprache in Quelltext einer bestimmten Programmiersprache wird Dekompilierung und entsprechende Programme Decompiler genannt Inhaltsverzeichnis 1 Terminologie 2 Geschichte 3 Arbeitsweise 4 Aufbau eines Compilers 4 1 Frontend auch Analysephase 4 1 1 Lexikalische Analyse 4 1 2 Syntaktische Analyse 4 1 3 Semantische Analyse 4 2 Backend auch Synthesephase 4 2 1 Zwischencodeerzeugung 4 2 2 Programmoptimierung 4 2 3 Codegenerierung 5 Einordnung verschiedener Compiler Arten 6 Sonderformen 7 Programmoptimierung ausfuhrlich 7 1 Einsparung von Maschinenbefehlen 7 2 Statische Formelauswertung zur Ubersetzungszeit 7 3 Eliminierung von totem Programmcode 7 4 Erkennung unbenutzter Variablen 7 5 Optimierung von Schleifen 7 6 Einfugen von Unterprogrammen 7 7 Halten von Werten in Registern 7 8 Verwendung schnellerer aquivalenter Anweisungen 7 9 Weglassen von Laufzeituberprufungen 7 10 Reduktion von Paging zur Laufzeit 7 11 Vorziehen bzw Verzogern von Speicherzugriffen 8 Ein Beispielcompiler 8 1 Grammatiken 8 2 Ausgabe des Beispiels 9 Literatur 10 Weblinks 11 EinzelnachweiseTerminologie BearbeitenEin Ubersetzer ist ein Programm das als Eingabe ein in einer Quellsprache formuliertes Programm akzeptiert und es in ein semantisch aquivalentes Programm in einer Zielsprache ubersetzt 1 Es wird also insbesondere gefordert dass das erzeugte Programm die gleichen Ergebnisse wie das gegebene Programm liefert Als Ausnahme wird oft die Quell Sprache Assembler angesehen ihr Ubersetzer in Maschinencode heisst Assembler und wird i A nicht als Compiler bezeichnet Die Aufgabe des Ubersetzers umfasst ein grosses Spektrum an Teilaufgaben von der Syntaxanalyse bis zur Zielcodeerzeugung Eine wichtige Aufgabe besteht auch darin Fehler im Quellprogramm zu erkennen und zu melden Das Wort Compiler stammt vom Englischen to compile dt zusammentragen zusammenstellen ab und heisst im eigentlichen Wortsinn also Zusammentrager In den 1950er Jahren war der Begriff noch nicht fest in der Computerwelt verankert 2 Ursprunglich bezeichnete Compiler ein Hilfsprogramm das ein Gesamtprogramm aus einzelnen Unterprogrammen oder Formelauswertungen zusammentrug um spezielle Aufgaben auszufuhren Diese Aufgabe erfullt heute der Linker der jedoch auch im Compiler integriert sein kann Die einzelnen Unterprogramme wurden noch von Hand in Maschinensprache geschrieben Ab 1954 kam der Begriff algebraic compiler fur ein Programm auf das die Umsetzung von Formeln in Maschinencode selbstandig ubernahm Das algebraic fiel im Laufe der Zeit weg 3 Ende der 1950er Jahre wurde der Begriff des Compilers im englischsprachigen Raum noch kontrovers diskutiert So hielt das Fortran Entwicklerteam noch jahrelang am Begriff translator deutsch Ubersetzer fest um den Compiler zu bezeichnen Diese Bezeichnung ist sogar im Namen der Programmiersprache Fortran selbst enthalten Fortran ist zusammengesetzt aus Formula und Translation heisst also in etwa Formel Ubersetzung Erst 1964 setzte sich der Begriff Compiler auch im Zusammenhang mit Fortran gegenuber dem Begriff Translator durch Nach Carsten Busch liegt eine besondere Ironie der Geschichte darin dass der Begriff Compiler im Deutschen mit Ubersetzer ubersetzt wird 2 4 Einige deutsche Publikationen verwenden jedoch auch den englischen Fachbegriff Compiler anstelle von Ubersetzer 5 In einem engeren Sinne verwenden einige deutschsprachige Publikationen den Fachbegriff Compiler jedoch nur wenn die Quellsprache eine hohere Programmiersprache ist als die Zielsprache 6 Typische Anwendungsfalle sind die Ubersetzung einer hoheren Programmiersprache in die Maschinensprache eines Computers sowie die Ubersetzung in Bytecode einer virtuellen Maschine Zielsprache von Compilern in diesem Sinne kann auch eine Assemblersprache sein Ein Ubersetzer zur Ubertragung von Assembler Quellprogrammen in Maschinensprache wird als Assembler oder Assemblierer bezeichnet 7 Geschichte BearbeitenBereits fur die erste entworfene hohere Programmiersprache den Plankalkul von Konrad Zuse plante dieser nach heutiger Terminologie einen Compiler Zuse bezeichnete ein einzelnes Programm als Rechenplan und hatte schon 1944 die Idee fur ein sogenanntes Planfertigungsgerat welches automatisch aus einem mathematisch formulierten Rechenplan einen gestanzten Lochstreifen mit entsprechendem Maschinenplan fur den Zuse Z4 Computer erzeugen sollte 8 Konkreter als die Idee von Zuse eines Planfertigungsgerats war ein Konzept von Heinz Rutishauser 9 zur automatischen Rechenplanfertigung In einem Vortrag vor der Gesellschaft fur Angewandte Mathematik und Mechanik GAMM wie auch 1951 in seiner Habilitationsschrift an der ETH Zurich beschrieb er welche zusatzlichen Programmierbefehle Instruktionen und Hardware Erganzungen an der damals an der ETHZ genutzten Z4 notig seien um den Rechner ebenfalls als Hilfsmittel zur automatischen Programmerstellung einzusetzen 10 11 12 nbsp Grace Hopper 1984 Ein fruher Compiler wurde 1949 von der Mathematikerin Grace Hopper konzipiert Bis zu diesem Zeitpunkt mussten Programmierer direkt Maschinencode erstellen Der erste Assembler wurde zwischen 1948 und 1950 von Nathaniel Rochester fur eine IBM 701 geschrieben Um diesen Prozess zu vereinfachen entwickelte Grace Hopper eine Methode die es ermoglichte Programme und ihre Unterprogramme in einer mehr an der menschlichen als der maschinellen Sprache orientierten Weise auszudrucken 13 Am 3 Mai 1952 stellte Hopper den ersten Compiler A 0 vor der Algorithmen aus einem Katalog abrief Code umschrieb in passender Reihenfolge zusammenstellte Speicherplatz reservierte und die Zuteilung von Speicheradressen organisierte 14 Anfang 1955 prasentierte Hopper bereits einen Prototyp des Compilers B 0 der nach englischen franzosischen oder deutschen Anweisungen Programme erzeugte 15 Hopper nannte ihren Vortrag zum ersten Compiler The Education of a Computer Die Erziehung eines Computers Die Geschichte des Compilerbaus wurde von den jeweils aktuellen Programmiersprachen vgl Zeittafel der Programmiersprachen und Hardwarearchitekturen gepragt Weitere fruhe Meilensteine sind 1957 der erste Fortran Compiler und 1960 der erste COBOL Compiler Viele Architekturmerkmale heutiger Compiler wurden aber erst in den 1960er Jahren entwickelt Fruher wurden teilweise auch Programme als Compiler bezeichnet die Unterprogramme zusammenfugen 16 Dies geht an der heutigen Kernaufgabe eines Compilers vorbei weil Unterprogramme heutzutage mit anderen Mitteln eingefugt werden konnen Entweder im Quelltext selbst beispielsweise von einem Praprozessor siehe auch Precompiler oder bei ubersetzten Komponenten von einem eigenstandigen Linker Arbeitsweise BearbeitenDie prinzipiellen Schritte bei der Ubersetzung eines Quellcodes in einen Zielcode lauten Syntaxprufung Es wird gepruft ob der Quellcode ein gultiges Programm darstellt also der Syntax der Quellsprache entspricht Festgestellte Fehler werden protokolliert Ergebnis ist eine Zwischendarstellung des Quellcodes Analyse und Optimierung Die Zwischendarstellung wird analysiert und optimiert Dieser Schritt variiert im Umfang je nach Compiler und Benutzereinstellung stark Er reicht von einfacheren Effizienzoptimierungen bis hin zu Programmanalyse Codeerzeugung Die optimierte Zwischendarstellung wird in entsprechende Befehle der Zielsprache ubersetzt Hierbei konnen weitere zielsprachenspezifische Optimierungen vorgenommen werden Beachte Moderne Compiler fuhren mittlerweile meist keine Codegenerierung mehr selbst durch C bei eingeschalteter globaler Optimierung Die Codegenerierung erfolgt beim Linken C Die Codegenerierung erfolgt aus wahrend der Kompilierung erzeugtem Common Intermediate Language Code wahrend der Laufzeit durch den JIT oder NGEN Compiler der NET Umgebung gleiches gilt fur andere Sprachen die die Common Language Infrastructure nutzen wie F und VB NET siehe Liste von NET Sprachen Java Die Codegenerierung erfolgt aus wahrend der Kompilierung erzeugtem Java Byte Code wahrend der Laufzeit durch den Java JIT Compiler Codegenerierung wahrend der Runtime ermoglicht modulubergreifende Optimierungen exakte Anpassungen an die Zielplattform Befehlssatz Anpassung an die Fahigkeiten der CPU Nutzung von Profiling Informationen Aufbau eines Compilers BearbeitenDer Compilerbau also die Programmierung eines Compilers ist eine eigenstandige Disziplin innerhalb der Informatik Moderne Compiler werden in verschiedene Phasen gegliedert die jeweils verschiedene Teilaufgaben des Compilers ubernehmen Einige dieser Phasen konnen als eigenstandige Programme bzw Softwarekomponenten realisiert werden z B Precompiler oder Praprozessor Sie werden sequentiell ausgefuhrt Im Wesentlichen lassen sich zwei Phasen unterscheiden das Frontend auch Analysephase das den Quelltext analysiert und daraus einen attributierten Syntaxbaum erzeugt sowie das Backend auch Synthesephase das daraus den Programmcode der Zielsprache erzeugt Frontend auch Analysephase Bearbeiten Im Compiler Frontend wird der Code analysiert strukturiert und auf Fehler gepruft Es ist selbst wiederum in Phasen gegliedert Sprachen wie modernes C erlauben aufgrund von Mehrdeutigkeiten in ihrer Grammatik keine Aufteilung der Syntaxanalyse in lexikalische Analyse syntaktische Analyse und semantische Analyse Ihre Compiler sind entsprechend komplex Lexikalische Analyse Bearbeiten Hauptartikel Lexikalische Analyse Die lexikalische Analyse zerteilt den eingelesenen Quelltext in lexikalische Einheiten Tokens verschiedener Typen zum Beispiel Schlusselworter Bezeichner Zahlen Zeichenketten oder Operatoren Dieser Teil des Compilers heisst Tokenizer Scanner oder Lexer Ein Scanner benutzt gelegentlich einen separaten Screener um Whitespace Leerraum also Leerzeichen Tabulatorzeichen Zeilenenden usw und Kommentare zu uberspringen Eine weitere Funktion der lexikalischen Analyse ist es erkannte Tokens mit ihrer Position z B Zeilennummer im Quelltext zu assoziieren Werden in der weiteren Analysephase deren Grundlage die Tokens sind Fehler im Quelltext gefunden z B syntaktischer oder semantische Art konnen die erzeugten Fehlermeldungen mit einem Hinweis auf den Ort des Fehlers versehen werden Lexikalische Fehler sind Zeichen oder Zeichenfolgen die keinem Token zugeordnet werden konnen Zum Beispiel erlauben die meisten Programmiersprachen keine Bezeichner die mit Ziffern beginnen z B 3foo Syntaktische Analyse Bearbeiten Hauptartikel Parser Die syntaktische Analyse uberpruft ob der eingelesene Quellcode in einer korrekten Struktur der zu ubersetzenden Quellsprache vorliegt das heisst der kontextfreien Syntax Grammatik der Quellsprache entspricht Dabei wird die Eingabe in einen Syntaxbaum umgewandelt Der syntaktische Analysierer wird auch als Parser bezeichnet Falls der Quellcode nicht zur Grammatik der Quellsprache passt gibt der Parser einen Syntaxfehler aus Der so erzeugte Syntaxbaum ist fur die nachste Phase semantische Analyse mit den Inhalten der Knoten annotiert d h z B Variablenbezeichner und Zahlen werden neben der Information dass es sich um solche handelt weitergegeben Die syntaktische Analyse pruft beispielsweise ob die Klammerung stimmt also zu jeder offnenden Klammer eine schliessende desselben Typs folgt sowie ohne Klammer Verschrankung Auch geben die Schlusselworte bestimmte Strukturen vor Semantische Analyse Bearbeiten Die semantische Analyse uberpruft die statische Semantik also uber die syntaktische Analyse hinausgehende Bedingungen an das Programm Zum Beispiel muss eine Variable in der Regel deklariert worden sein bevor sie verwendet wird und Zuweisungen mussen mit kompatiblen vertraglichen Datentypen erfolgen Dies kann mit Hilfe von Attributgrammatiken realisiert werden Dabei werden die Knoten des vom Parser generierten Syntaxbaums mit Attributen versehen die Informationen enthalten So kann zum Beispiel eine Liste aller deklarierten Variablen erstellt werden Die Ausgabe der semantischen Analyse nennt man dann dekorierten oder attributierten Syntaxbaum Backend auch Synthesephase Bearbeiten Das Compiler Backend erzeugt den Programmcode der Zielsprache aus dem attributierten Syntaxbaum welcher vom Frontend erstellt wurde Zwischencodeerzeugung Bearbeiten Viele moderne Compiler erzeugen aus dem Syntaxbaum einen Zwischencode der schon relativ maschinennah sein kann und fuhren auf diesem Zwischencode zum Beispiel Programmoptimierungen durch Das bietet sich besonders bei Compilern an die mehrere Quellsprachen oder verschiedene Zielplattformen unterstutzen Hier kann der Zwischencode auch ein Austauschformat sein Programmoptimierung Bearbeiten Der Zwischencode ist Basis vieler Programmoptimierungen Siehe Programmoptimierung Codegenerierung Bearbeiten Hauptartikel Codegenerator Bei der Codegenerierung wird der Programmcode der Zielsprache entweder direkt aus dem attributierten Syntaxbaum oder aus dem Zwischencode erzeugt Falls die Zielsprache eine Maschinensprache ist kann das Ergebnis direkt ein ausfuhrbares Programm sein oder eine sogenannte Objektcode Datei die durch das Linken mit der Laufzeitbibliothek und evtl weiteren Objektcodedateien zu einer Bibliothek oder einem ausfuhrbaren Programm fuhrt Dies alles wird vom Codegenerator ausgefuhrt der Teil des Compilersystems ist manchmal als Programmteil des Compilers manchmal als eigenstandiges Modul Einordnung verschiedener Compiler Arten BearbeitenNative Compiler Compiler der den Zielcode fur die Plattform erzeugt auf der er selbst lauft Der Code ist plattformspezifisch Cross Compiler Hauptartikel Cross Compiler Compiler der auf einer Plattform ausgefuhrt wird und Zielcode fur eine andere Plattform zum Beispiel ein anderes Betriebssystem oder eine andere Prozessorarchitektur erzeugt Eine typische Anwendung ist die Erstellung von Programmen fur ein eingebettetes System das selbst keine oder keine guten Werkzeuge zur Softwareerstellung enthalt sowie die Erstellung oder Portierung eines Betriebssystems auf eine neue Plattform Single pass Compiler Compiler der in einem einzigen Durchlauf aus dem Quellcode den Zielcode erzeugt im Gegensatz zum Multi pass Compiler der Compiler liest also den Quelltext von vorne nach hinten nur ein Mal und erzeugt zugleich das Ergebnisprogramm Ublicherweise ist ein derartiger Compiler sehr schnell aber kann nur einfache Optimierungen durchfuhren Nur fur bestimmte Programmiersprachen zum Beispiel Pascal C und C kann ein Single Pass Compiler erstellt werden denn dazu darf die Programmiersprache keine Vorwartsbezuge enthalten es darf nichts verwendet werden was nicht bereits weiter oben im Quelltext deklariert wurde Multi pass Compiler Bei diesem Compilertyp wird der Quellcode in mehreren Schritten in den Zielcode ubersetzt ursprunglich der Quellcode wird mehrmals eingelesen bzw mehrfach von vorne nach hinten komplett durchgearbeitet In den Anfangszeiten des Compilerbaus wurde der Ubersetzungsprozess hauptsachlich deshalb in mehrere Durchlaufe zerlegt weil die Kapazitat der Computer oft nicht ausreichte um den vollstandigen Compiler und das zu ubersetzende Programm gleichzeitig im Hauptspeicher zu halten Heutzutage dient ein Multi pass Compiler vor allem dazu Vorwartsreferenzen Deklaration eines Bezeichners weiter unten im Quelltext als dessen erste Verwendung aufzulosen und aufwandige Optimierungen durchzufuhren Sonderformen BearbeitenBei einem Transcompiler auch als Transpiler oder Quer Ubersetzer bezeichnet handelt es sich um einen speziellen Compiler der Quellcode einer Programmiersprache in den Quellcode einer anderen Programmiersprache ubersetzt zum Beispiel von Pascal in C 17 Man nennt diesen Vorgang Code Transformation oder ubersetzen Da jedoch viele Programmiersprachen besondere Eigenschaften und Leistungsmerkmale besitzen kann es wenn diese nicht vom Transcompiler berucksichtigt werden zu Effizienzverlusten kommen 18 Da Programmiersprachen meist unterschiedlichen Programmierparadigmen folgen ist der neu generierte Quelltext oft nur schwer fur Entwickler lesbar Manchmal ist auch eine manuelle Nachbearbeitung des Codes notig da die automatische Ubersetzung nicht in allen Fallen reibungsfrei funktioniert Ausserdem gibt es in vielen modernen Programmiersprachen umfangreiche Unterprogrammbibliotheken Das Umsetzen von Bibliotheksaufrufen erschwert den Ubersetzungsvorgang zusatzlich Compiler Compiler und Compilergeneratoren sind Hilfsprogramme zur automatischen Generierung von Compilerteilen oder vollstandigen Compilern Siehe auch ANTLR Coco R JavaCC Lex Yacc Just in time Compiler oder JIT Compiler ubersetzen Quellcode oder Zwischencode erst bei der Ausfuhrung des Programms in Maschinencode Dabei werden Programmteile erst ubersetzt wenn diese erstmals oder mehrmals ausgefuhrt werden Meist ist der Grad der Optimierung abhangig von der Benutzungshaufigkeit der entsprechenden Funktion Beim Compreter wird der Programm Quellcode zunachst in einen Zwischencode ubersetzt der dann zur Laufzeit interpretiert wird Compreter sollten die Vorteile des Compilers mit den Vorteilen des Interpreters verbinden Effektiv sind viele heutige Interpreter zur Verringerung der Ausfuhrungszeit intern als Compreter implementiert die den Quellcode zur Laufzeit ubersetzen bevor das Programm ausgefuhrt wird Auch ein Bytecode Interpreter ist ein Compreter z B die virtuellen Maschinen von Java bis Version 1 2 Programmoptimierung ausfuhrlich BearbeitenViele Optimierungen die fruher Aufgabe des Compilers waren werden mittlerweile innerhalb der CPU wahrend der Codeabarbeitung vorgenommen Maschinencode ist gut wenn er kurze kritische Pfade und wenig Uberraschungen durch falsch vorhergesagte Sprunge aufweist Daten rechtzeitig aus dem Speicher anfordert und alle Ausfuhrungseinheiten der CPU gleichmassig auslastet nbsp Parallelisierte Berechnung der Mandelbrotmenge auf einer Haswell i7 CPU 2014 Die Grafik zeigt die gleichzeitig auf einem Kern stattfindenden Berechnungen Datentyp Gleitkomma einfache Genauigkeit das sind zwischen 64 und 128 Berechnungen in 8 bis 16 Befehlen pro Kern aufgeteilt auf 2 Threads Auf einem Haswell Core i7 5960X 8 Kerne findet damit bis zu 1024 parallele Berechnungen 96 Mrd Iterationen sec statt auf einem Haswell Xeon E7 8890 V3 bis zu 2304 180 Mrd Iterationen sec pro Sockel Die Aufgabe moderner Compiler ist es Code so zu optimieren um diese Verschachtelung von Befehlen zu ermoglichen Dies ist eine grundsatzlich andere Aufgabe als Compiler in den spaten 1980er Jahren hatten Zur Steuerung des Ubersetzens kann der Quelltext neben den Anweisungen der Programmiersprache zusatzliche spezielle Compiler Anweisungen enthalten Ublicherweise bietet ein Compiler Optionen fur verschiedene Optimierungen mit dem Ziel die Laufzeit des Zielprogramms zu verbessern oder dessen Speicherplatzbedarf zu minimieren Die Optimierungen erfolgen teilweise in Abhangigkeit von den Eigenschaften der Hardware zum Beispiel wie viele und welche Register der Prozessor des Computers zur Verfugung stellt Es ist moglich dass ein Programm nach einer Optimierung langsamer ausgefuhrt wird als das ohne die Optimierung der Fall gewesen ware Dies kann zum Beispiel eintreten wenn eine Optimierung fur ein Programmkonstrukt langeren Code erzeugt der zwar an sich schneller ausgefuhrt werden wurde aber mehr Zeit benotigt um erst einmal in den Cache geladen zu werden Er ist damit erst bei haufigerer Benutzung vorteilhaft Einige Optimierungen fuhren dazu dass der Compiler Zielsprachenkonstrukte erzeugt fur die es gar keine direkten Entsprechungen in der Quellsprache gibt Ein Nachteil solcher Optimierungen ist dass es dann kaum noch moglich ist den Programmablauf mit einem interaktiven Debugger in der Quellsprache zu verfolgen Optimierungen konnen sehr aufwendig sein Vielfach muss vor allem in modernen JIT Compilern daher abgewogen werden ob es sich lohnt einen Programmteil zu optimieren Bei Ahead of time Compilern werden bei der abschliessenden Ubersetzung alle sinnvollen Optimierungen verwendet haufig jedoch nicht wahrend der Software Entwicklung reduziert den Kompilier Zeitbedarf Fur nichtautomatische Optimierungen seitens des Programmierers konnen Tests und Anwendungsszenarien durchgespielt werden s Profiler um herauszufinden wo sich komplexe Optimierungen lohnen Im Folgenden werden einige Optimierungsmoglichkeiten eines Compilers betrachtet Das grosste Optimierungspotenzial besteht allerdings oft in der Veranderung des Quellprogramms selbst zum Beispiel darin einen Algorithmus durch einen effizienteren zu ersetzen Dieser Vorgang kann meistens nicht automatisiert werden sondern muss durch den Programmierer erfolgen Einfachere Optimierungen konnen dagegen an den Compiler delegiert werden um den Quelltext lesbar zu halten Einsparung von Maschinenbefehlen Bearbeiten In vielen hoheren Programmiersprachen benotigt man beispielsweise eine Hilfsvariable um den Inhalt zweier Variablen zu vertauschen Einsparung von Maschinenbefehlen MB Quellcode Maschinenbefehleohne Optimierung mit Optimierunghilf a a Register 1Register 1 hilf a Register 1a b b Register 1Register 1 a b Register 2Register 2 ab hilf hilf Register 1Register 1 b Register 1 bBenotigte Variablen 3 2Register 1 2Operationen 6 4Mit der Optimierung werden statt 6 nur noch 4 Assemblerbefehle benotigt ausserdem wird der Speicherplatz fur die Hilfsvariable hilf nicht gebraucht D h diese Vertauschung wird schneller ausgefuhrt und benotigt weniger Hauptspeicher Dies gilt jedoch nur wenn ausreichend Register im Prozessor zur Verfugung stehen Die Speicherung von Daten in Registern statt im Hauptspeicher ist eine haufig angewendete Moglichkeit der Optimierung Die oben als optimiert gezeigte Befehlsfolge hat noch eine weitere Eigenschaft die bei modernen CPUs mit mehreren Verarbeitungs Pipelines einen Vorteil bedeuten kann Die beiden Lesebefehle und die beiden Schreibbefehle konnen problemlos parallel verarbeitet werden sie sind nicht vom Resultat des jeweils anderen abhangig Lediglich der erste Schreibbefehl muss auf jeden Fall abwarten bis der letzte Lesebefehl ausgefuhrt wurde Tiefer gehende Optimierungsverfahren fugen deshalb unter Umstanden zwischen b Register 2 und Register 2 a noch Maschinenbefehle ein die zu einer ganz anderen hochsprachlichen Befehlszeile gehoren Statische Formelauswertung zur Ubersetzungszeit Bearbeiten Die Berechnung des Kreisumfangs mittels pi 3 14159 u 2 pi r kann ein Compiler bereits zur Ubersetzungszeit zu u 6 28318 r auswerten Diese Formelauswertung spart die Multiplikation 2 pi zur Laufzeit des erzeugten Programms Diese Vorgehensweise wird als Konstantenfaltung englisch constant folding bezeichnet Eliminierung von totem Programmcode Bearbeiten Hauptartikel Dead code elimination Wenn der Compiler erkennen kann dass ein Teil des Programmes niemals durchlaufen wird dann kann er diesen Teil bei der Ubersetzung weglassen Beispiel 100 goto 900 200 k 3 900 i 7 Wenn in diesem Programm niemals ein GOTO auf die Sprungmarke 200 erfolgt kann auf die Anweisung 200 k 3 verzichtet werden Der Sprungbefehl 100 goto 900 ist dann ebenfalls uberflussig Erkennung unbenutzter Variablen Bearbeiten Wird eine Variable nicht benotigt so muss dafur kein Speicherplatz reserviert und kein Zielcode erzeugt werden Beispiel subroutine test a b b 2 a c 3 14 b return b Hier wird die Variable c nicht benotigt Sie steht nicht in der Parameterliste wird in spateren Berechnungen nicht verwendet und wird auch nicht ausgegeben Deshalb kann die Anweisung c 3 14 b entfallen Optimierung von Schleifen Bearbeiten Insbesondere Schleifen versucht man zu optimieren indem man zum Beispiel moglichst viele Variablen in Registern halt normalerweise mindestens die Schleifenvariable statt eines Index mit dem auf Elemente eines Feldes englisch array zugegriffen wird Zeiger auf die Elemente verwendet dadurch wird der Aufwand beim Zugriff auf Feldelemente geringer Berechnungen innerhalb der Schleife die in jedem Durchlauf dasselbe Ergebnis liefern nur einmal vor der Schleife ausfuhrt Loop invariant code motion zwei Schleifen die uber denselben Wertebereich gehen zu einer Schleife zusammenfasst damit fallt der Verwaltungsaufwand fur die Schleife nur einmal an die Schleife teilweise oder bei Schleifen mit konstanter niedriger Durchlaufzahl komplett auflost englisch loop unrolling sodass die Anweisungen innerhalb der Schleife mehrfach direkt hintereinander ausgefuhrt werden ohne dass jedes Mal nach den Anweisungen eine Prufung der Schleifenbedingung und ein Sprung zum Schleifenbeginn erfolgen die Schleife vor allem bei Zahlschleifen mit for umdreht da beim Herunterzahlen auf 0 effiziente Sprungbefehle Jump Not Zero benutzt werden konnen die Schleife umformt damit die Uberprufung der Abbruchbedingung am Ende der Schleife durchgefuhrt wird Schleifen mit Anfangsuberprufung haben stets eine bedingte und eine unbedingte Sprunganweisung wahrend Schleifen mit Enduberprufung nur eine bedingte Sprunganweisung haben die ganze Schleife entfernt wenn sie nach einigen Optimierungen einen leeren Rumpf besitzt Dies kann allerdings dazu fuhren dass Warteschleifen die ein Programm absichtlich verlangsamen sollen entfernt werden Allerdings sollten fur diesen Zweck soweit moglich sowieso Funktionen des Betriebssystems benutzt werden verschachtelte Schleifen Schleifen in Schleifen wenn es die verwendete Programmierlogik erlaubt aufsteigend anordnet von der aussersten Schleife mit den wenigsten Schleifendurchlaufen bis zur innersten Schleife mit den meisten Schleifendurchlaufen Damit verhindert man vielfache Mehrinitialisierungen der inneren Schleifenkorper Manche dieser Optimierungen sind bei aktuellen Prozessoren ohne Nutzen oder sogar kontraproduktiv Einfugen von Unterprogrammen Bearbeiten Hauptartikel Inline Ersetzung Bei kleinen Unterprogrammen fallt der Aufwand zum Aufruf des Unterprogrammes verglichen mit der vom Unterprogramm geleisteten Arbeit starker ins Gewicht Daher versuchen Compiler den Maschinencode kleinerer Unterprogramme direkt einzufugen ahnlich wie manche Compiler Assembler Pracompiler Makro Anweisungen in Quellcode auflosen Diese Technik wird auch als Inlining bezeichnet In manchen Programmiersprachen ist es moglich durch inline Schlusselworter den Compiler darauf hinzuweisen dass das Einfugen von bestimmten Unterprogrammen gewunscht ist Das Einfugen von Unterprogrammen eroffnet oft abhangig von den Parametern weitere Moglichkeiten fur Optimierungen Halten von Werten in Registern Bearbeiten Anstatt mehrfach auf dieselbe Variable im Speicher beispielsweise in einer Datenstruktur zuzugreifen kann der Wert nur einmal gelesen und fur weitere Verarbeitungen in Registern oder im Stack zwischengespeichert werden In C C und Java muss dieses Verhalten ggf mit dem Schlusselwort volatile abgeschaltet werden Eine als volatile bezeichnete Variable wird bei jeder Benutzung wiederholt vom originalen Speicherplatz gelesen da ihr Wert sich unterdessen geandert haben konnte Das kann beispielsweise der Fall sein wenn es sich um einen Hardware Port handelt oder ein parallel laufender Thread den Wert geandert haben konnte Beispiel int a array 25 gt element x int b 3 array 25 gt element x Im Maschinenprogramm wird nur einmal auf array 25 gt element x zugegriffen der Wert wird zwischengespeichert und zweimal verwendet Ist x volatile dann wird zweimal zugegriffen Es gibt ausser volatile noch einen anderen Grund der eine Zwischenspeicherung in Registern unmoglich macht Wenn der Wert der Variablen v durch Verwendung des Zeigers z im Speicher verandert werden konnte kann eine Zwischenspeicherung von v in einem Register zu fehlerhaftem Programmverhalten fuhren Da die in der Programmiersprache C oft verwendeten Zeiger nicht auf ein Array beschrankt sind sie konnten irgendwohin im Hauptspeicher zeigen hat der Optimizer oft nicht genugend Informationen um eine Veranderung einer Variablen durch einen Zeiger auszuschliessen Verwendung schnellerer aquivalenter Anweisungen Bearbeiten Statt einer Multiplikation oder Division von Ganzzahlen mit einer Zweierpotenz kann ein Schiebebefehl verwendet werden Es gibt Falle in denen nicht nur Zweierpotenzen sondern auch andere Zahlen einfache Summen von Zweierpotenzen fur diese Optimierung herangezogen werden So kann zum Beispiel span class p span span class n n span span class w span span class o lt lt span span class w span span class mi 1 span span class p span span class w span span class o span span class w span span class p span span class n n span span class w span span class o lt lt span span class w span span class mi 2 span span class p span schneller sein als span class n n span span class w span span class o span span class w span span class mi 6 span Statt einer Division durch eine Konstante kann eine Multiplikation mit dem Reziprokwert der Konstante erfolgen Selbstverstandlich sollte man solch spezielle Optimierungen auf jeden Fall dem Compiler uberlassen Weglassen von Laufzeituberprufungen Bearbeiten Programmiersprachen wie Java fordern Laufzeituberprufungen beim Zugriff auf Felder oder Variablen Wenn der Compiler ermittelt dass ein bestimmter Zugriff immer im erlaubten Bereich sein wird zum Beispiel ein Zeiger von dem bekannt ist dass er an dieser Stelle nicht NULL ist kann der Code fur diese Laufzeituberprufungen weggelassen werden Reduktion von Paging zur Laufzeit Bearbeiten Eng zusammenhangende Codebereiche zum Beispiel ein Schleifenrumpf sollte zur Laufzeit moglichst auf der gleichen oder in moglichst wenigen Speicherseiten Page zusammenhangend vom Betriebssystem verwalteter Speicherblock im Hauptspeicher liegen Diese Optimierung ist Aufgabe des optimierenden Linkers Dies kann zum Beispiel dadurch erreicht werden dass dem Zielcode an geeigneter Stelle Leeranweisungen NOPs No OPeration hinzugefugt werden Dadurch wird der erzeugte Code zwar grosser aber wegen der reduzierten Anzahl notwendiger TLB Cache Eintrage und notwendiger Pagewalks wird das Programm schneller ausgefuhrt Vorziehen bzw Verzogern von Speicherzugriffen Bearbeiten Durch das Vorziehen von Speicherlesezugriffen und das Verzogern von Schreibzugriffen lasst sich die Fahigkeit moderner Prozessoren zur Parallelarbeit verschiedener Funktionseinheiten ausnutzen So kann beispielsweise bei den Befehlen a b c d e f der Operand e bereits geladen werden wahrend ein anderer Teil des Prozessors noch mit der ersten Multiplikation beschaftigt ist Ein Beispielcompiler BearbeitenFolgendes in ANTLR erstelltes Beispiel soll die Zusammenarbeit zwischen Parser und Lexer erklaren Der Ubersetzer soll Ausdrucke der Grundrechenarten beherrschen und vergleichen konnen Die Parsergrammatik wandelt einen Dateiinhalt in einen abstrakten Syntaxbaum AST um Grammatiken Bearbeiten Die Baumgrammatik ist in der Lage die im AST gespeicherten Lexeme zu evaluieren Der Operator der Rechenfunktionen steht in der AST Schreibweise vor den Operanden als Prafixnotation Daher kann die Grammatik ohne Sprunge Berechnungen anhand des Operators durchfuhren und dennoch Klammerausdrucke und Operationen verschiedener Prioritat korrekt berechnen tree grammar Eval options tokenVocab Expression ASTLabelType CommonTree header import java lang Math start line Eine Datei besteht aus mehreren Zeilen line compare System out println compare value compare returns double value a compare b compare value a b a compare b compare value a b a compare b compare value a b a compare b compare value a b a compare b compare value a b UMINUS a compare value 1 a Token UMINUS ist notwendig um den binaren Operator nicht mit einem Vorzeichen zu verwechseln a compare b compare value Math pow a b a compare b compare value a b 1 0 wahr 1 falsch 0 INT value Integer parseInt INT text Ist eines der oben als compare bezeichnete Ausdrucke noch kein Lexem so wird es von der folgenden Lexer Grammatik in einzelne Lexeme aufgeteilt Dabei bedient sich der Lexer der Technik des rekursiven Abstiegs Ausdrucke werden so immer weiter zerlegt bis es sich nur noch um Token vom Typ number oder Operatoren handeln kann grammar Expression options output AST ASTLabelType CommonTree tokens UMINUS start line System out println line tree null null line tree toStringTree line compare NEWLINE gt compare Eine Zeile besteht aus einem Ausdruck und einem terminalen Zeichen compare sum sum Summen sind mit Summen vergleichbar sum product product product Summen bestehen aus Produkten Operatorrangfolge product pow pow pow pow Produkte Modulo Operation gehort hier dazu konnen aus Potenzen zusammengesetzt sein pow term pow Potenzen werden auf Terme angewendet term number Terme bestehen aus Nummern Subtermen oder Summen term gt term term gt UMINUS term Subterm mit Vorzeichen sum Subterm mit Klammerausdruck number INT Nummern bestehen nur aus Zahlen INT 0 9 NEWLINE r n WS t n r skip Whitespace wird ignoriert Die Ausgabe hinter dem Token start zeigt ausserdem den gerade evaluierten Ausdruck Ausgabe des Beispiels Bearbeiten Eingabe 5 2 3 32 2 8 2 2 3 2 3 Ausgabe in den ersten Zeilen wird nur der Ausdruck der Eingabe in der AST Darstellung ausgegeben 5 2 3 32 2 8 2 2 3 2 3 1 0 72 0 6 0 Der erste Ausdruck wird also als wahr 1 evaluiert bei den anderen Ausdrucken wird das Ergebnis der Rechnung ausgegeben Literatur BearbeitenUwe Meyer Grundkurs Compilerbau 1 Auflage Rheinwerk Computing Bonn 2021 ISBN 978 3 8362 7733 4 Niklaus Wirth Grundlagen und Techniken des Compilerbaus 3 bearbeitete Auflage Oldenbourg Wissenschaftsverlag Munchen 2011 ISBN 978 3 486 70951 3 Alfred V Aho Monica S Lam Ravi Sethi Jeffrey D Ullman Compiler Pearson 2008 ISBN 978 3 8273 7097 6 Deutsche Ubersetzung Alfred V Aho Monica S Lam Ravi Sethi Jeffrey D Ullman Compilers principles techniques amp tools Pearson Addison Wesley Boston 2007 ISBN 0 321 48681 1 Reinhard Wilhelm Dieter Maurer Ubersetzerbau Theorie Konstruktion Generierung Springer 1997 ISBN 3 540 61692 6 Weblinks Bearbeiten nbsp Wiktionary Compiler Bedeutungserklarungen Wortherkunft Synonyme Ubersetzungen nbsp Wiktionary kompilieren Bedeutungserklarungen Wortherkunft Synonyme UbersetzungenEinzelnachweise Bearbeiten Michael Eulenstein Generierung portabler Compiler Das portable System POCO Informatik Fachberichte 164 Springer Verlag Berlin u a 1988 S 1 Hans Jochen Schneider Hrsg Lexikon Informatik und Datenverarbeitung 4 Auflage Oldenbourg Verlag Munchen Berlin 1998 900 Manfred Broy Informatik Eine grundlegende Einfuhrung Band 2 Systemstrukturen und Theoretische Informatik 2 Auflage Springer Verlag Berlin Heidelberg 1998 S 173 a b Carsten Busch Mataphern in der Informatik Modellbildung Formalisierung Anwendung Springer Fachmedien Wiesbaden 1998 S 171 Axel Rogat Aufbau und Arbeitsweise von Compilern Kapitel 1 11 Geschichte Thomas W Parsons Introduction to Compiler Construction Computer Science Press New York 1992 S 1 Zur Ubersetzung des englischen compiler mit dem deutschen Ubersetzer siehe u a Hans Jurgen Siegert Uwe Baumgarten Betriebssysteme Eine Einfuhrung 6 Auflage Oldenbourg Verlag Munchen Wien 2007 S 352 Christoph Prevezanos Computer Lexikon 2011 Markt Technik Verlag Munchen 2010 S 940 Christoph Prevenzanos Technisches Schreiben Fur Informatiker Akademiker Techniker und den Berufsalltag Hanser Verlag Munchen 2013 S 130 So beispielsweise Alfred V Aho Monica S Lam Ravi Sethi Jeffrey D Ullman Compiler Prinzipien Techniken und Werkzeuge 2 Auflage Pearson Studium Munchen 2008 Siehe dazu Hans Jochen Schneider Hrsg Lexikon Informatik und Datenverarbeitung 4 Auflage Oldenbourg Verlag Munchen Berlin 1998 Artikel Compiler S 158 und Artikel Ubersetzer S 900 Hartmut Ernst Jochen Schmidt Gert Beneken Grundkurs Informatik Grundlagen und Konzepte fur die erfolgreiche IT Praxis Eine umfassende praxisorientierte Einfuhrung 5 Auflage Springer Wiesbaden 2015 S 409 Hans Dieter Hellige Geschichten der Informatik Visionen Paradigmen Leitmotive Springer Berlin 2004 ISBN 3 540 00217 0 S 45 104 105 Evelyn Boesch Trueb Heinz Rutishauser In Historisches Lexikon der Schweiz 12 Juli 2010 abgerufen am 21 Oktober 2014 Stefan Betschon Der Zauber des Anfangs Schweizer Computerpioniere In Franz Betschon Stefan Betschon Jurg Lindecker Willy Schlachter Hrsg Ingenieure bauen die Schweiz Technikgeschichte aus erster Hand Verlag Neue Zurcher Zeitung Zurich 2013 ISBN 978 3 03823 791 4 S 381 383 Friedrich L Bauer My years with Rutishauser Stefan Betschon Die Geschichte der Zukunft In Neue Zurcher Zeitung 6 Dezember 2016 S 11 Inventor of the Week Archive Massachusetts Institute of Technology Juni 2006 abgerufen am 25 September 2011 Kurt W Beyer Grace Hopper and the invention of the information age Massachusetts Institute of Technology 2009 ISBN 978 0 262 01310 9 Google Books abgerufen am 25 September 2011 Kathleen Broome Williams Grace Hopper Naval Institute Press 2004 ISBN 1 55750 952 2 Google Books abgerufen am 25 September 2011 F L Bauer J Eickel Compiler Construction An Advanced Course Springer 1975 Transcompiler In Neogrid IT Lexikon Abgerufen am 18 November 2011 Wenn ein Compiler aus dem Quellcode einer Programmiersprache den Quellcode einer anderen erzeugt z B C in C so spricht man von einem Transcompiler Transpiler bullhost de abgerufen am 18 November 2012 Normdaten Sachbegriff GND 4148248 7 lobid OGND AKS Abgerufen von https de wikipedia org w index php title Compiler amp oldid 237625687