Erklärung, Beschreibung und Bedeutung über Compiler

Compiler Bedeutung, Erklärung und Definition.

Ein Compiler (auch Kompilierer oder Übersetzer) ist ein Computerprogramm, das ein in einer Quellsprache geschriebenes Programm in ein semantisch äquivalentes Programm einer Zielsprache umwandelt. Üblicherweise handelt es sich dabei um die Übersetzung eines von einem Programmierer in einer Programmiersprache geschriebenen Quelltextes nach Assemblersprache, Bytecode oder Maschinensprache. Die Anwendung eines Compilers wird als Kompilierung bezeichnet.

Der Compilerbau, also die Programmierung eines Compilers, ist eine eigenständige Disziplin innerhalb der Informatik.

Die Bezeichnungen Compiler oder Kompilierer sind eigentlich irreführend, weil sie von der Zusammenstellung von Tabellen herrühren, die der Compiler für seine interne Datenverwaltung benötigt, was aber an der Kernaufgabe eines Compilers vorbeigeht.

Verwandt mit einem Compiler ist ein Interpreter, der ein Programm nicht in die Zielsprache übersetzt, sondern Schritt für Schritt direkt ausführt.

Table of contents
1 Geschichte
2 Sonderformen
3 Aufbau eines Compilers
4 Programmoptimierung
5 Bedeutende Compiler
6 Hilfsmittel / Compilercompiler
7 Literatur

Geschichte

Der erste Compiler (A-0) wurde 1952 von der Mathematikerin Grace Hopper entwickelt.

Sonderformen

Aufbau eines Compilers

]] Es lassen sich im Wesentlichen zwei Phasen unterscheiden: eine Analysephase, die den Quelltext analysiert und daraus einen Syntaxbaum erzeugt, sowie die Synthesephase, die daraus das Zielprogramm erzeugt.

Analysephase (auch "Frontend")

Lexikalische Analyse

Die
lexikalische Analyse zerteilt den eingelesenen Quelltext in zusammengehörende Token verschiedener Klassen, z.B. Schlüsselwörter, Bezeichner, Zahlen und Operatoren. Dieser Teil des Compilers heißt Scanner oder Lexer.

Ein Scanner benutzt gelegentlich einen separaten Screener, um Whitespace (Leerraum, also Leerzeichen, Zeilenenden, usw.) und Kommentare zu überspringen.

Syntaktische Analyse

Die syntaktische Analyse überprüft, ob der eingelesene Quellcode formal richtig ist, d.h. der Syntax (Grammatik) der Quellsprache entspricht. Dabei wird die Eingabe in einen Syntaxbaum umgewandelt. Dieser Teil wird auch als Parser bezeichnet.

Semantische Analyse

Die semantische Analyse überprüft die statische Semantik, also "logische Rahmenbedingungen". Zum Beispiel muss eine Variable deklariert worden sein, bevor sie verwendet wird, und Zuweisungen müssen mit kompatibelen (verträglichen) Datentypen erfolgen.

Synthesephase (auch "Backend")

Die Synthesephase erzeugt aus dem in der Analysephase erstellten Baum den Programmcode der Zielsprache.

Zwischencode Erzeugung

Die Phase erzeugt vorerst einen Objektcode, einen Zwischencode, der schon nah an der Zielmaschinensprache ist.

Programmoptimierung

Die Optimierungen finden hier meist auf den zuvor erstellten Objektcode statt. Siehe Programmoptimierung.

Codegenerierung

Bei der Codegenerierung wird endgültig aus dem Syntaxbaum der Programmcode in der Zielsprache erzeugt. Falls die Zielsprache die Maschinensprache ist, kann das Ergebnis direkt ein ausführbares Programm sein oder eine so genannte Objektdatei, die durch das Linken mit der Laufzeitbibliothek und evtl. weiteren Objektdateien zu einer Bibliothek oder einem ausführbaren Programm führt.

Programmoptimierung

Üblicherweise bietet ein Compiler Optionen für verschiedene Optimierungen mit dem Ziel, die Laufzeit oder den Speicherplatzbedarf des Zielprogramms zu verkleinern.

Die Optimierung erfolgt in Abhängigkeit von den Eigenschaften der Hardware, z. B. wieviele und welche Register (Computer) der Prozessor des Computers zur Verfügung stellt.

Einige Optimierungen führen dazu, dass der Compiler Programmkonstrukte in semantisch äquivalente, aber günstigere Konstrukte umwandelt, die keine Entsprechung im Quellcode haben. Eine Folge ist, dass es bei Aktivierung entsprechender Optimierungen kaum noch möglich ist, den Programmablauf mit einem interaktiven Debugger zu verfolgen.

Im Folgenden betrachten wir einige Optimierungsmöglichkeiten eines Compilers. Dabei handelt es sich naturgemäß nur um Feinabstimmung an einem bestehenden Programm. "Optimierung" bedeutet auch nicht, dass das Programm danach in irgendeiner Weise optimal wäre, nur besser. Es ist auch möglich, dass das Programm nachher "totoptimiert" ist, also die Optimierung über das Ziel soweit hinausgeschossen ist, dass das Programm effektiv langsamer ausgeführt wird. Viele Optimierungen moderner Compiler sind solche Abwägungen zwischen dem, was möglich ist, und dem, was sinnvoll ist. Die Grenze zwischen beiden ist meist nicht klar ersichtlich und muss durch Tests herausgefunden werden.

Es sollte nicht vergessen werden, dass das größte Optimierungspotenzial oft darin besteht, den Algorithmus selbst zu verändern bzw. durch einen besseren zu ersetzen. Dieser Vorgang kann nicht automatisiert werden, sondern muss durch den Programmierer erfolgen.

Reduzierung von Assemblerinstruktionen

Wenn man zum Beispiel in einer höheren Programmiersprache den Inhalt von 2 Variablen vertauscht, dann benötigt man eine Hilfsvariable:

Reduzierung von Assemblerinstruktionen
höhere
Programmiersprache
Assembler ohne Optimierung Assembler mit Optimierung
t = a
a --> Register 1
Register 1 --> t
a --> Register 1
a = b
b --> Register 2
Register 2 --> a
b --> Register 2
b = t
t --> Register 3
Register 3 --> b
Register 1 --> b
Register 2 --> a

Mit Optimierung benötigt man nur 4 Assemblerbefehle anstatt 6, außerdem wird der Speicherplatz für die Hilfsvariable t nicht gebraucht. D.h. diese Vertauschung wird schneller ausgeführt und benötigt weniger Hauptspeicher.

Statische Formelauswertung zur Übersetzungszeit

Die Berechnung des Kreisumfangs mittels

        pi = 3.1415
        u  = 2 * pi * r
kann ein Compiler bereits zum Übersetzungszeitpunkt zu "u = 6.283 * r" auswerten. Dies spart die Multipliktion "2*pi" zur Laufzeit des erzeugten Programms. Diese Vorgehensweise wird als Konstantenfaltung (engl. "constant folding") bezeichnet.

Eliminierung toten Programmcodes

Wenn der Compiler erkennen kann, dass ein Teil des Programmes niemals durchlaufen wird, dann kann er diesen Teil bei der Übersetzung weglassen.

  
Beispiel:  ...   ...
          100   goto 900
          200   k=3
          900   i=7
          ...   ...
Wenn in diesem Programm niemals ein GOTO auf das Label 200 erfolgt, dann kann auf die Anweisung "200 k=3" verzichtet werden.

Erkennung von nicht benötigten Variablen

Wird eine Variable nicht benötigt, dann wird sie auch nicht berechnet.

Beispiel:  subroutine test (a,b)
          b = 2 * a
          c = 3.14 * b
          return b
Hier wird die Variable c nicht benötigt: Sie steht nicht in der Parameterliste, wird in späteren Berechnungen nicht verwendet und wird auch nicht ausgegeben. Deshalb entfällt die Anweisung "c = 3.14 * b".

Optimierung von Schleifen

Insbesondere versucht man Schleifen zu optimieren, indem man z.B.:

  • möglichst viele Variablen in Registern hält (z.B. die Schleifenvariable)
  • Berechnungen innerhalb der Schleife, die in jedem Durchlauf dasselbe ergeben, nur einmal berechnet
  • die Schleife komplett auflöst (englisch loop unrolling), falls die Anzahl der Schleifendurchläufe konstant ist. Die Anweisungen innerhalb der Schleife werden dann sequentiell (hintereinander) ausgeführt ohne Verwendung einer Schleife.

Reduzierung von Paging zur Laufzeit

Zusammenhängender Code - z. B. eine Schleife - sollte zur Laufzeit möglichst auf der gleichen "Seite" (zusammenhängend vom Betriebssystem verwalteter Speicherblock) im Hauptspeicher liegen. Dies kann man evtl. dadurch erreichen, dass man dem Programmcode geeignete Leeranweisungen ("NOPs" - No OPeration) hinzufügt. Dadurch wird der Programmcode zwar größer, aber wegen des reduzierten Pagings wird das Programm schneller ausgeführt.

Bedeutende Compiler

oder auch viele andere, die zusammen mit IDEs (Integrierte Entwicklungsumgebung) ausgeliefert werden (Visual Basic, Delphi etc.)

Hilfsmittel / Compilercompiler

Da es oft sehr umfangreich ist, einen eigenen Compiler zu programmieren, gibt es Hilfsmittel, die auch frei im Internet zu Verfügung stehen. Einige werden auch oft als Compilercompiler bezeichnet. Beispiele hierfür sind:

  • LEX - ein lexikalischer Scanner, der für den ersten Schritt beim Kompilieren verantwortlich ist (lexikalische Analyse).
  • YACC - Yet Another CompilerCompiler, ein Parsergenerator.
  • JAVACC - Java CompilerCompiler, ein CC implementiert in Java mit vielen Möglichkeiten, der u.a. lexikalischer Scanner und Parsergenerator vereint.

Literatur

(englisch)

  • Compilers: Principles, Techniques, and Tools von Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman / Addison-Wesley Pub Co - ISBN: 0201100886
  • Lex & Yacc von John R. Levine, Tony Mason, Doug Brown / O'Reilly & Associates - ISBN: 1565920007


Diese Seite ist ein Artikel über Compiler. Seite Versuche, zum von von Beschreibung über bereitzustellen Compiler. Sie konnten Tatsachen über auch finden Compiler. Erklärung von Compiler.

Suche Alle