Compilerbau.tex 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. %!TEX root = Programmierparadigmen.tex
  2. \chapter{Compilerbau}
  3. \index{Compilerbau|(}
  4. Wenn man über Compiler redet, meint man üblicherweise \enquote{vollständige Übersetzer}:
  5. \begin{definition}\xindex{Compiler}%
  6. Ein \textbf{Compiler} ist ein Programm $C$, das den Quelltext eines Programms
  7. $A$ in eine ausführbare Form übersetzen kann.
  8. \end{definition}
  9. Jedoch gibt es verschiedene Ebenen der Interpretation bzw. Übersetzung:
  10. \begin{enumerate}
  11. \item \textbf{Reiner Interpretierer}: TCL, Unix-Shell
  12. \item \textbf{Vorübersetzung}: Java-Bytecode, Pascal P-Code, Python\footnote{Python hat auch \texttt{.pyc}-Dateien, die Python-Bytecode enthalten.}, Smalltalk-Bytecode
  13. \item \textbf{Laufzeitübersetzung}: JavaScript\footnote{JavaScript wird nicht immer zur Laufzeit übersetzt. Früher war es üblich, dass JavaScript nur interpretiert wurde.}
  14. \item \textbf{Vollständige Übersetzung}: C, C++, Fortran
  15. \end{enumerate}
  16. Zu sagen, dass Python eine interpretierte Sprache ist, ist in etwa so korrekt
  17. wie zu sagen, dass die Bibel ein Hardcover-Buch ist.\footnote{Quelle: stackoverflow.com/a/2998544, danke Alex Martelli für diesen Vergleich.}
  18. Reine Interpretierer lesen den Quelltext Anweisung für Anweisung und führen
  19. diese direkt aus.
  20. \todo[inline]{Bild}
  21. Bei der \textit{Interpretation nach Vorübersetzung} wird der Quelltext analysiert
  22. und in eine für den Interpretierer günstigere Form übersetzt. Das kann z.~B.
  23. durch
  24. \begin{itemize}
  25. \item Zuordnung Bezeichnergebrauch - Vereinbarung\todo{?}
  26. \item Transformation in Postfixbaum
  27. \item Typcheck, wo statisch möglich
  28. \end{itemize}
  29. geschehen. Diese Vorübersetzung ist nicht unbedingt maschinennah.
  30. \todo[inline]{Bild}
  31. Die \textit{Just-in-time-Compiler}\xindex{Compiler!Just-in-time}\index{JIT|see{Just-in-time Compiler}} (kurz: JIT-Compiler) betreiben
  32. Laufzeitübersetzung. Folgendes sind Vor- bzw. Nachteile von Just-in-time Compilern:
  33. \begin{itemize}
  34. \item schneller als reine Interpretierer
  35. \item Speichergewinn: Quelle kompakter als Zielprogramm\todo{Was ist hier gemeint?}
  36. \item Schnellerer Start des Programms
  37. \item Langsamer (pro Funktion) als vollständige Übersetzung
  38. \item kann dynamisch ermittelte Laufzeiteigenschaften berücksichtigen (dynamische Optimierung)
  39. \end{itemize}
  40. Moderne virtuelle Maschinen für Java und für .NET nutzen JIT-Compiler.
  41. Bei der \textit{vollständigen Übersetzung} wird der Quelltext vor der ersten
  42. Ausführung des Programms $A$ in Maschinencode (z.~B. x86, SPARC) übersetzt.
  43. \todo[inline]{Bild}
  44. \section{Funktionsweise}
  45. Üblicherweise führt ein Compiler folgende Schritte aus:
  46. \begin{enumerate}
  47. \item Lexikalische Analyse
  48. \item Syntaktische Analyse
  49. \item Semantische Analyse
  50. \item Zwischencodeoptimierung
  51. \item Codegenerierung
  52. \item Assemblieren und Binden
  53. \end{enumerate}
  54. \section{Lexikalische Analyse}\xindex{Analyse!lexikalische}%
  55. In der lexikalischen Analyse wird der Quelltext als Sequenz von Zeichen betrachtet.
  56. Sie soll bedeutungstragende Zeichengruppen, sog. \textit{Tokens}\xindex{Token},
  57. erkennen und unwichtige Zeichen, wie z.~B. Kommentare überspringen. Außerdem
  58. sollen Bezeichner identifiziert und in einer \textit{Stringtabelle}\xindex{Stringtabelle}
  59. zusammengefasst werden.
  60. \begin{beispiel}
  61. \todo[inline]{Beispiel erstellen}
  62. \end{beispiel}
  63. \subsection{Reguläre Ausdrücke}\xindex{Ausdrücke!reguläre}
  64. \begin{beispiel}[Regulärere Ausdrücke]
  65. Folgender regulärer Ausdruck erkennt Float-Konstanten in C nach
  66. ISO/IEC 9899:1999 §6.4.4.2:
  67. $((0|\dots|9)^*.(0|\dots|9)^+)|((0|\dots|9)^+.)$
  68. \end{beispiel}
  69. \begin{satz}
  70. Jede reguläre Sprache wird von einem (deterministischen) endlichen
  71. Automaten akzeptiert.
  72. \end{satz}
  73. TODO: Bild einfügen
  74. Zu jedem regulären Ausdruck im Sinne der theoretischen Informatik kann ein
  75. nichtdeterministischer Automat generiert werden. Dieser kann mittels
  76. Potenzmengenkonstruktion\footnote{\url{http://martin-thoma.com/potenzmengenkonstruktion/}}
  77. in einen deterministischen Automaten überführen. Dieser kann dann mittels
  78. Äquivalenzklassen minimiert werden.
  79. \todo[inline]{Alle Schritte beschreiben}
  80. \subsection{Lex}\index{Lex|(}\index{Flex|see{Lex}}
  81. Lex ist ein Programm, das beim Übersetzerbau benutzt wird um Tokenizer für die
  82. lexikalische Analyse zu erstellen. Flex ist eine Open-Source Variante davon.
  83. Eine Flex-Datei besteht aus 3 Teilen, die durch \texttt{\%\%} getrennt werden:
  84. \begin{verbatim}
  85. Definitionen: Definiere Namen
  86. %%
  87. Regeln: Definiere reguläre Ausdrücke und
  88. zugehörige Aktionen (= Code)
  89. %%
  90. Code: zusätzlicher Code
  91. \end{verbatim}
  92. \subsubsection{Reguläre Ausdrücke in Flex}
  93. \begin{table}
  94. \begin{tabular}{ll}
  95. x & Zeichen 'x' erkennen \\
  96. "xy" & Zeichenkette 'xy' erkennen \\
  97. \textbackslash & Zeichen 'x' erkennen (TODO) \\
  98. $[xyz]$ & Zeichen $x$, $y$ oder $z$ erkennen \\
  99. $[a-z]$ & Alle Kleinbuchstaben erkennen \\
  100. $[\^a-z]$ & Alle Zeichen außer Kleinbuchstaben erkennen \\
  101. $x|y$ & $x$ oder $y$ erkennen \\
  102. (x) & x erkennen \\
  103. x* & 0, 1 oder mehrere Vorkommen von x erkennen \\
  104. x+ & 1 oder mehrere Vorkommen von x erkennen \\
  105. x? & 0 oder 1 Vorkommen von x erkennen \\
  106. \{Name\} & Expansion der Definition Name \\
  107. \textbackslash t, \textbackslash n, \textbackslash rq & Tabulator, Zeilenumbruch, Wagenrücklauf erkennen \\
  108. \end{tabular}
  109. \end{table}
  110. \index{Lex|)}
  111. \section{Syntaktische Analyse}\xindex{Analyse!syntaktische}%
  112. In der syntaktischen Analyse wird überprüft, ob die Tokenfolge zur
  113. kontextfreien Sprache\todo{Warum kontextfrei?} gehört. Außerdem soll die
  114. hierarchische Struktur der Eingabe erkannt werden.\todo{Was ist gemeint?}
  115. Ausgegeben wird ein \textbf{abstrakter Syntaxbaum}\xindex{Syntaxbaum!abstrakter}.
  116. \begin{beispiel}[Abstrakter Syntaxbaum]
  117. TODO
  118. \end{beispiel}
  119. \section{Semantische Analyse}\xindex{Analyse!semantische}%
  120. Die semantische Analyse arbeitet auf einem abstrakten Syntaxbaum und generiert
  121. einen attributierten Syntaxbaum\xindex{Syntaxbaum!attributeriter}.
  122. Sie führt eine kontextsensitive Analyse durch. Dazu gehören:
  123. \begin{itemize}
  124. \item \textbf{Namensanalyse}: Beziehung zwischen Deklaration und Verwendung\todo{?}
  125. \item \textbf{Typanalyse}: Bestimme und prüfe Typen von Variablen, Funktionen, \dots \todo{?}
  126. \item \textbf{Konsistenzprüfung}: Wurden alle Einschränkungen der Programmiersprache eingehalten?\todo{?}
  127. \end{itemize}
  128. \begin{beispiel}[Attributeriter Syntaxbaum]
  129. TODO
  130. \end{beispiel}
  131. \section{Zwischencodeoptimierung}
  132. Hier wird der Code in eine sprach- und zielunabhängige Zwischensprache transformiert.
  133. Dabei sind viele Optimierungen vorstellbar. Ein paar davon sind:
  134. \begin{itemize}
  135. \item \textbf{Konstantenfaltung}: Ersetze z.~B. $3+5$ durch $8$.
  136. \item \textbf{Kopienfortschaffung}: Setze Werte von Variablen direkt ein
  137. \item \textbf{Code verschieben}: Führe Befehle vor der Schleife aus, statt in der Schleife \todo{?}
  138. \item \textbf{Gemeinsame Teilausdrücke entfernen}: Es sollen doppelte Berechnungen vermieden werden \todo{Beispiel?}
  139. \item \textbf{Inlining}: Statt Methode aufzurufen, kann der Code der Methode an der Aufrufstelle eingebaut werden.
  140. \end{itemize}
  141. \section{Codegenerierung}
  142. Der letzte Schritt besteht darin, aus dem generiertem Zwischencode den
  143. Maschinencode oder Assembler zu erstellen. Dabei muss folgendes beachtet werden:
  144. \begin{itemize}
  145. \item \textbf{Konventionen}: Wie werden z.~B. im Laufzeitsystem Methoden aufgerufen?
  146. \item \textbf{Codeauswahl}: Welche Befehle kennt das Zielsystem?
  147. \item \textbf{Scheduling}: In welcher Reihenfolge sollen die Befehle angeordnet werden?
  148. \item \textbf{Registerallokation}: Welche Zwischenergebnisse sollen in welchen Prozessorregistern gehalten werden?
  149. \item \textbf{Nachoptimierung}\todo{?}
  150. \end{itemize}
  151. \index{Compilerbau|)}