Was ist ein Compiler?

24.01.2023
Von 
Martin Heller schreibt als freier Autor für die Schwesterpublikation InfoWorld.
Compiler sind für die Softwareentwicklung essenziell. Lesen Sie, wie auch Quellcode Maschinencode wird.
Compiler übersetzen Code in anderen Code. Das sollten Sie zum Thema wissen.
Compiler übersetzen Code in anderen Code. Das sollten Sie zum Thema wissen.
Foto: Yurchanka Siarhei - shutterstock.com

Ein Compiler ist ein Computerprogramm, das von einem Format in ein anderes übersetzt - meist von einer High-Level-Programmiersprache in Byte- und Maschinencode. Es gibt eine ganze Reihe von Compiler-Varianten, die wir in diesem Artikel umfassend beleuchten.

Compiler erklärt

Etwas konkreter übersetzen Compiler häufig Quellcode - etwa der Programmiersprache C++ - in Objektcode für die jeweilige Rechnerarchitektur - beispielsweise Intel x64. Die aus mehreren Programmiersprachen-Files erstellten Objektmodule werden anschließend zu einem Executable verbunden. Dabei unterscheidet man folgende Compiler-Arten:

  • Cross-Compiler, die Objektcode für Architekturen erzeugen, die sich von derjenigen unterscheiden, auf der der Compiler läuft. Sie werden üblicherweise verwendet, um Executables für eingebettete Systeme zu erzeugen.

  • Transpiler, die von einer Programmiersprache in eine andere übersetzen - etwa von TypeScript in JavaScript oder von C++ in C.

  • Bootstrap-Compiler, die in der Sprache geschrieben sind, die sie kompilieren.

  • Compiler für Sprachen, die maschinenunabhängig sein sollen - wie Java, Python oder C# - übersetzen den Quellcode in Bytecode für eine virtuelle Maschine, der dann in einem Interpreter für die aktuelle Architektur ausgeführt wird. Dieser Interpreter kann durch einen Just-in-Time-Compiler (JIT) unterstützt werden, der einen Teil des Byte-Codes zur Laufzeit in native Code-Anweisungen übersetzt. JIT-Compiler führen manchmal zu Verzögerungen beim Start, die aber in der Regel durch die höhere Geschwindigkeit im weiteren Verlauf der Ausführung aufgewogen werden, insbesondere bei CPU-intensivem Code.

  • Ein Ansatz zur Verringerung der Startverzögerung für JIT-kompilierte ausführbare Dateien ist es, AOT-Compiler (AOT = ahead of time) zu verwenden, um ausführbare Images zu erstellen.

Sprachcompiler werden häufig zunächst in einer bestehenden, niedrigeren Sprache implementiert und später in der Sprache, die sie kompilieren, neu implementiert. Dieses Vorgehen soll Portabilität und Upgrades durch Bootstrapping ermöglichen. Andererseits werden Hochsprachen zunehmend in Bytecode für eine virtuelle Maschine kompiliert, der dann interpretiert und JIT-kompiliert wird.

Die wichtigsten Compiler im Überblick

Betrachten wir nun einige der wichtigsten Programmiersprachen-Compiler im Detail.

FORTRAN

Formula Translator (ab 1977 FORTRAN genannt) war die erste erfolgreiche Programmiersprache für wissenschaftliche und technische Anwendungen. Der FORTRAN-I-Compiler wurde von 1954 bis 1957 für den IBM 704 von einem All-Star-Team unter der Leitung von John W. Backus entwickelt.

Es handelte sich um einen optimierenden Compiler, der in Assemblersprache geschrieben war und 23.000 Anweisungen umfasste. Dabei nahm der FORTRAN-I-Compiler bedeutende Optimierungen vor und analysierte beispielsweise arithmetische Ausdrücke oder optimierte die Zuordnung von Indexregistern. Inzwischen stehen mehr als ein Dutzend FORTRAN-Compiler zur Verfügung, wovon einige quelloffen und kostenlos sind, obwohl sie kommerziell angeboten werden.

LISP

List Processor (LISP) ist eng mit dem Feld der Künstlichen Intelligenz verknüpft, wurde am MIT entworfen und die Spezifikation im Jahr 1960 veröffentlicht. Kurz nach der Veröffentlichung der Spezifikation entdeckte ein Forscher, dass die LISP-Eval-Funktion in Maschinencode implementiert werden konnte - und setzte das für den IBM 704 um. So wurde der erste LISP-Interpreter geboren.

Die MIT-Forscher Tim Hart und Mike Levin entwickelten im Jahr 1962 den ersten LISP-Compiler in LISP: Der Compiler selbst wurde kompiliert, indem der LISP-Interpreter auf dem Quellcode des Compilers ausgeführt wurde. Das kompilierte LISP lief auf dem IBM 704 rund 40 Mal schneller als das interpretierte LISP. Dieser Compiler war einer der ersten Bootstrapped-Compiler und führte zudem die inkrementelle Kompilierung ein, die eine Vermischung von kompiliertem und interpretiertem Code ermöglicht. Für spätere Versionen von LISP und seinen Nachfolgern, wie Common Lisp, Emacs Lisp, Scheme und Clojure gab es zahlreiche Compiler und Interpreter.

COBOL

Common Business-Oriented Language (COBOL) wurde ab 1959 auf Anregung des US-Verteidigungsministeriums von einem Komitee (CODASYL) entwickelt und basiert auf drei bestehenden Sprachen:

  • FLOW-MATIC (entwickelt von Grace Hopper),

  • AIMACO (ein Derivat von FLOW-MATIC) und

  • COMTRAN (von Bob Bemer von IBM).

Das ursprüngliche Ziel von COBOL war es, eine portable Programmiersprache für die allgemeine Datenverarbeitung zu schaffen. Das erste COBOL-Programm lief im Jahr 1960. Im Jahr 1962 ergab eine Studie der Navy, dass COBOL drei bis elf Anweisungen pro Minute kompilieren konnte. Das verbesserte sich im Laufe der Jahre, als die Sprachspezifikationen und Compiler aktualisiert wurden. Im Jahr 1970 war COBOL die meist verbreitete Programmiersprache der Welt.

Derzeit existieren noch vier große COBOL-Compiler:

  • Fujitsu NetCOBOL kompiliert zur .NET Intermediate Language (Bytecode) und läuft auf der .NET CLR (Common Language Runtime);

  • GnuCOBOL kompiliert zu C-Code, der dann kompiliert und gelinkt werden kann;

  • IBM COBOL kompiliert zu Objektcode für IBM-Mainframes und Midrange-Computer - der Code wird dann ähnlich wie bei den frühen COBOL-Compilern verlinkt;

  • Micro Focus COBOL kompiliert entweder zu .NET- oder JVM-Bytecode (Java Virtual Machine).

ALGOL

Zwei niederländische Wissenschaftler schrieben zwischen 1959 und 1960 den ersten ALGOL-Compiler in X1-Assemblersprache am Zentrum für Mathematik in Amsterdam. Algorithmic Language (ALGOL) war - verglichen mit FORTRAN - ein enormer Fortschritt bei den Programmiersprachen für Wissenschaft und Technik. Sie hatte auch Einfluss auf die Entwicklung von imperativen Sprachen wie CPL, Simula, BCPL, B, Pascal oder C.

Der Compiler beinhaltete etwa 2.000 Anweisungen - die Laufzeitbibliothek war noch ebenso groß. Der Compiler wurde von Papierbändern geladen, ebenso wie der Programmquellcode und die Bibliotheken. Der Compiler durchlief den Code zweifach: Der erste Durchlauf (der sogenannte Prescan) diente dazu, Identifier und Blocks zu sammeln, der zweite (Main Scan) erzeugte den Objektcode auf einem anderen Papierband. Dieser Prozess wurde später durch einen "Store" (wahrscheinlich eine Magnettrommel) anstelle des Papierbandes beschleunigt. Von ALGOL 60 und seinen Dialekten gab es etwa 70 Implementierungen.

ALGOL 68 sollte ALGOL 60 ursprünglich ersetzen, war aber so komplex, dass Implementierungen und Akzeptanz weit hinter den Erwartungen zurückblieben. Zu den von ALGOL 68 beeinflussten Sprachen gehören zum Beispiel:

  • C,

  • C++,

  • Bourne Shell,

  • KornShell,

  • Bash,

  • Steelman,

  • Ada und

  • Python.

PL/I

Programming Language One (PL/I) wurde Mitte der 1960er Jahre von IBM und SHARE (der wissenschaftlichen Usergroup von IBM) als einheitliche Sprache für den Einsatz in Wissenschaft und Business entwickelt. Die erste Implementierung - PL/I F - wurde für das IBM-System S/360 vollständig in System/360-Assemblersprache geschrieben und 1966 ausgeliefert. Der F-Compiler bestand aus einer Kontrollphase und einer großen Anzahl von Compilerphasen (annähernd 100). Es gab mehrere spätere Implementierungen von PL/I bei IBM - auch für Multics (als Systemsprache) und die DEC VAX.

Pascal

Niklaus Wirth von der ETH Zürich war Mitglied des Ausschusses, der an der Nachfolge von ALGOL 60 arbeitete und reichte eine Sprache namens ALGOL W ein, die abgelehnt wurde. Wirth trat aus dem Komitee aus, arbeitete weiter an seinem Projekt und veröffentlichte es 1970 in vereinfachter Form unter dem Namen Pascal. Wirth versuchte zunächst, den Pascal-Compiler in FORTRAN 66 zu implementieren, was ihm aber nicht gelang. Daraufhin schrieb er einen Pascal-Compiler in der C-ähnlichen Sprache Scallop, der dann für das Bootstrapping in Pascal übersetzt wurde.

Zwei bemerkenswerte Ableger sind das Pascal P-System und Turbo Pascal: Der Züricher P-System-Compiler erzeugte "p-code" für eine virtuelle Stack-Maschine, der dann interpretiert wurde. Das führte zu UCSD Pascal für den IBM PC und zu Apple Pascal. Anders Hejlsberg schrieb Blue Label Pascal für den Nascom-2 und reimplementierte es dann für den IBM PC in 8088 Assembler. Diese Errungenschaft wurde schließlich von Borland aufgekauft und als Turbo Pascal wieder auf den Markt gebracht. Später portierte Hejlsberg Turbo Pascal auf den Macintosh, fügte Apples Object-Pascal-Erweiterungen hinzu und portierte die neue Sprache zurück auf den PC, woraus sich schließlich Delphi für Microsoft Windows entwickelte.

C

C wurde ursprünglich zwischen 1972 und 1973 von Dennis Ritchie in den Bell Labs entwickelt, um Dienstprogramme für Unix zu entwickeln. Der ursprüngliche C-Compiler war in PDP-7-Assembler geschrieben, wie auch Unix zu dieser Zeit. Die Portierung auf PDP-11 erfolgte ebenfalls in Assembler. Später wurde C verwendet, um den Unix-Kernel umzuschreiben und portabel zu machen.

C++

C++ wurde ab 1979 von Bjarne Stroustrup in den Bell Laboratories entwickelt. Weil es ein Versuch war, C um objektorientierte Funktionen (unter anderem) zu erweitern, nannte Stroustrup es zunächst "C with Objects". 1983 folgte die Umbenennung in C++ - 1985 wurde die Sprache auch außerhalb von Bell verfügbar. Der erste kommerzielle C++-Compiler, Cfront, wurde zur selben Zeit veröffentlicht. Er übersetzte C++ in C, das dann kompiliert und gelinkt werden konnte. Spätere C++-Compiler erzeugten Objektcode-Dateien, die direkt in einen Linker eingespeist wurden.

Java

Java wurde 1995 als portable Sprache (mit dem Marketing-Slogan "Write once, run anywhere") veröffentlicht, die in Bytecode für die JVM kompiliert und dann interpretiert wird - ähnlich wie das Pascal P-System. Der Java-Compiler war ursprünglich in C geschrieben und verwendete einige C++-Bibliotheken. In späteren JVM-Versionen wurde ein JIT-Compiler hinzugefügt, um den Interpreter zu beschleunigen. Der aktuelle Java-Compiler ist in Java geschrieben, wenngleich die Java-Laufzeitumgebung immer noch in C erstellt wurde.

In der GraalVM-Implementierung von Java und anderen Sprachen wird zur Build Time ein AOT-Compiler ausgeführt, um den Bytecode zu optimieren und die Startzeit zu verkürzen.

C#

C# wurde im Jahr 1999 von Anders Hejlsberg bei Microsoft entwickelt und im Jahr 2000 von Mads Torgersen in C und C++ für die CLR implementiert. C# kompiliert zu CLR-Bytecode (Intermediate Language) und wird zur Laufzeit interpretiert und JIT-kompiliert. Der C#-Compiler, die CLR und die Bibliotheken sind jetzt in C# geschrieben, und der Compiler wird von einer Version zur nächsten gebootet.

Ein Teil des Anstoßes für die Entwicklung von C# könnte darin bestanden haben, dass Microsoft nicht in der Lage war, Java von Sun zu lizenzieren (Microsoft bestreitet das). Laut Hejlsberg wurde C# sowohl von C++ als auch von Java beeinflusst. (fm)

Dieser Beitrag basiert auf einem Artikel unserer US-Schwesterpublikation Network World.