2
0
Fork 0
mirror of https://github.com/MartinThoma/LaTeX-examples.git synced 2025-04-25 22:38:04 +02:00
LaTeX-examples/documents/Programmierparadigmen/Compilerbau.tex

187 lines
7.9 KiB
TeX
Raw Normal View History

%!TEX root = Programmierparadigmen.tex
2014-02-07 12:04:12 +01:00
\chapter{Compilerbau}
\index{Compilerbau|(}
2014-02-14 09:23:58 +01:00
Wenn man über Compiler redet, meint man üblicherweise \enquote{vollständige Übersetzer}:
2014-02-07 12:04:12 +01:00
2014-02-14 09:23:58 +01:00
\begin{definition}\xindex{Compiler}%
Ein \textbf{Compiler} ist ein Programm $C$, das den Quelltext eines Programms
$A$ in eine ausführbare Form übersetzen kann.
\end{definition}
2014-02-07 12:04:12 +01:00
2014-02-14 09:23:58 +01:00
Jedoch gibt es verschiedene Ebenen der Interpretation bzw. Übersetzung:
\begin{enumerate}
\item \textbf{Reiner Interpretierer}: TCL, Unix-Shell
\item \textbf{Vorübersetzung}: Java-Bytecode, Pascal P-Code, Python\footnote{Python hat auch \texttt{.pyc}-Dateien, die Python-Bytecode enthalten.}, Smalltalk-Bytecode
\item \textbf{Laufzeitübersetzung}: JavaScript\footnote{JavaScript wird nicht immer zur Laufzeit übersetzt. Früher war es üblich, dass JavaScript nur interpretiert wurde.}
\item \textbf{Vollständige Übersetzung}: C, C++, Fortran
\end{enumerate}
Zu sagen, dass Python eine interpretierte Sprache ist, ist in etwa so korrekt
wie zu sagen, dass die Bibel ein Hardcover-Buch ist.\footnote{Quelle: stackoverflow.com/a/2998544, danke Alex Martelli für diesen Vergleich.}
Reine Interpretierer lesen den Quelltext Anweisung für Anweisung und führen
diese direkt aus.
\todo[inline]{Bild}
Bei der \textit{Interpretation nach Vorübersetzung} wird der Quelltext analysiert
und in eine für den Interpretierer günstigere Form übersetzt. Das kann z.~B.
durch
\begin{itemize}
\item Zuordnung Bezeichnergebrauch - Vereinbarung\todo{?}
\item Transformation in Postfixbaum
\item Typcheck, wo statisch möglich
\end{itemize}
geschehen. Diese Vorübersetzung ist nicht unbedingt maschinennah.
\todo[inline]{Bild}
Die \textit{Just-in-time-Compiler}\xindex{Compiler!Just-in-time}\index{JIT|see{Just-in-time Compiler}} (kurz: JIT-Compiler) betreiben
Laufzeitübersetzung. Folgendes sind Vor- bzw. Nachteile von Just-in-time Compilern:
\begin{itemize}
\item schneller als reine Interpretierer
\item Speichergewinn: Quelle kompakter als Zielprogramm\todo{Was ist hier gemeint?}
\item Schnellerer Start des Programms
\item Langsamer (pro Funktion) als vollständige Übersetzung
\item kann dynamisch ermittelte Laufzeiteigenschaften berücksichtigen (dynamische Optimierung)
\end{itemize}
Moderne virtuelle Maschinen für Java und für .NET nutzen JIT-Compiler.
Bei der \textit{vollständigen Übersetzung} wird der Quelltext vor der ersten
Ausführung des Programms $A$ in Maschinencode (z.~B. x86, SPARC) übersetzt.
\todo[inline]{Bild}
\section{Funktionsweise}
Üblicherweise führt ein Compiler folgende Schritte aus:
\begin{enumerate}
\item Lexikalische Analyse
\item Syntaktische Analyse
\item Semantische Analyse
\item Zwischencodeoptimierung
\item Codegenerierung
\item Assemblieren und Binden
\end{enumerate}
\section{Lexikalische Analyse}\xindex{Analyse!lexikalische}%
2014-02-14 09:23:58 +01:00
In der lexikalischen Analyse wird der Quelltext als Sequenz von Zeichen betrachtet.
Sie soll bedeutungstragende Zeichengruppen, sog. \textit{Tokens}\xindex{Token},
erkennen und unwichtige Zeichen, wie z.~B. Kommentare überspringen. Außerdem
sollen Bezeichner identifiziert und in einer \textit{Stringtabelle}\xindex{Stringtabelle}
zusammengefasst werden.
\begin{beispiel}
\todo[inline]{Beispiel erstellen}
\end{beispiel}
\subsection{Reguläre Ausdrücke}\xindex{Ausdrücke!reguläre}
\begin{beispiel}[Regulärere Ausdrücke]
Folgender regulärer Ausdruck erkennt Float-Konstanten in C nach
ISO/IEC 9899:1999 §6.4.4.2:
$((0|\dots|9)^*.(0|\dots|9)^+)|((0|\dots|9)^+.)$
\end{beispiel}
\begin{satz}
Jede reguläre Sprache wird von einem (deterministischen) endlichen
Automaten akzeptiert.
\end{satz}
TODO: Bild einfügen
Zu jedem regulären Ausdruck im Sinne der theoretischen Informatik kann ein
nichtdeterministischer Automat generiert werden. Dieser kann mittels
Potenzmengenkonstruktion\footnote{\url{http://martin-thoma.com/potenzmengenkonstruktion/}}
in einen deterministischen Automaten überführen. Dieser kann dann mittels
Äquivalenzklassen minimiert werden.
\todo[inline]{Alle Schritte beschreiben}
\subsection{Lex}\index{Lex|(}\index{Flex|see{Lex}}
Lex ist ein Programm, das beim Übersetzerbau benutzt wird um Tokenizer für die
lexikalische Analyse zu erstellen. Flex ist eine Open-Source Variante davon.
Eine Flex-Datei besteht aus 3 Teilen, die durch \texttt{\%\%} getrennt werden:
\begin{verbatim}
Definitionen: Definiere Namen
%%
Regeln: Definiere reguläre Ausdrücke und
zugehörige Aktionen (= Code)
%%
Code: zusätzlicher Code
\end{verbatim}
\subsubsection{Reguläre Ausdrücke in Flex}
\begin{table}
\begin{tabular}{ll}
x & Zeichen 'x' erkennen \\
"xy" & Zeichenkette 'xy' erkennen \\
\textbackslash & Zeichen 'x' erkennen (TODO) \\
$[xyz]$ & Zeichen $x$, $y$ oder $z$ erkennen \\
$[a-z]$ & Alle Kleinbuchstaben erkennen \\
$[\^a-z]$ & Alle Zeichen außer Kleinbuchstaben erkennen \\
$x|y$ & $x$ oder $y$ erkennen \\
(x) & x erkennen \\
x* & 0, 1 oder mehrere Vorkommen von x erkennen \\
x+ & 1 oder mehrere Vorkommen von x erkennen \\
x? & 0 oder 1 Vorkommen von x erkennen \\
\{Name\} & Expansion der Definition Name \\
\textbackslash t, \textbackslash n, \textbackslash rq & Tabulator, Zeilenumbruch, Wagenrücklauf erkennen \\
\end{tabular}
\end{table}
\index{Lex|)}
2014-02-14 09:23:58 +01:00
\section{Syntaktische Analyse}\xindex{Analyse!syntaktische}%
In der syntaktischen Analyse wird überprüft, ob die Tokenfolge zur
kontextfreien Sprache\todo{Warum kontextfrei?} gehört. Außerdem soll die
hierarchische Struktur der Eingabe erkannt werden.\todo{Was ist gemeint?}
Ausgegeben wird ein \textbf{abstrakter Syntaxbaum}\xindex{Syntaxbaum!abstrakter}.
\begin{beispiel}[Abstrakter Syntaxbaum]
TODO
\end{beispiel}
\section{Semantische Analyse}\xindex{Analyse!semantische}%
Die semantische Analyse arbeitet auf einem abstrakten Syntaxbaum und generiert
einen attributierten Syntaxbaum\xindex{Syntaxbaum!attributeriter}.
Sie führt eine kontextsensitive Analyse durch. Dazu gehören:
\begin{itemize}
\item \textbf{Namensanalyse}: Beziehung zwischen Deklaration und Verwendung\todo{?}
\item \textbf{Typanalyse}: Bestimme und prüfe Typen von Variablen, Funktionen, \dots \todo{?}
\item \textbf{Konsistenzprüfung}: Wurden alle Einschränkungen der Programmiersprache eingehalten?\todo{?}
\end{itemize}
\begin{beispiel}[Attributeriter Syntaxbaum]
TODO
\end{beispiel}
\section{Zwischencodeoptimierung}
Hier wird der Code in eine sprach- und zielunabhängige Zwischensprache transformiert.
Dabei sind viele Optimierungen vorstellbar. Ein paar davon sind:
\begin{itemize}
\item \textbf{Konstantenfaltung}: Ersetze z.~B. $3+5$ durch $8$.
\item \textbf{Kopienfortschaffung}: Setze Werte von Variablen direkt ein
\item \textbf{Code verschieben}: Führe Befehle vor der Schleife aus, statt in der Schleife \todo{?}
\item \textbf{Gemeinsame Teilausdrücke entfernen}: Es sollen doppelte Berechnungen vermieden werden \todo{Beispiel?}
\item \textbf{Inlining}: Statt Methode aufzurufen, kann der Code der Methode an der Aufrufstelle eingebaut werden.
\end{itemize}
\section{Codegenerierung}
Der letzte Schritt besteht darin, aus dem generiertem Zwischencode den
Maschinencode oder Assembler zu erstellen. Dabei muss folgendes beachtet werden:
\begin{itemize}
\item \textbf{Konventionen}: Wie werden z.~B. im Laufzeitsystem Methoden aufgerufen?
\item \textbf{Codeauswahl}: Welche Befehle kennt das Zielsystem?
\item \textbf{Scheduling}: In welcher Reihenfolge sollen die Befehle angeordnet werden?
\item \textbf{Registerallokation}: Welche Zwischenergebnisse sollen in welchen Prozessorregistern gehalten werden?
\item \textbf{Nachoptimierung}\todo{?}
\end{itemize}
\index{Compilerbau|)}