Compiler – Wikipedia

Computerprogramm, das Code von einer Programmiersprache in eine andere übersetzt

Beim Rechnen ist a Compiler ist ein Computerprogramm, das Computercode übersetzt, der in einer Programmiersprache (der ( Quelle Sprache) in eine andere Sprache (die Ziel Sprache). Der Name “Compiler” wird hauptsächlich für Programme verwendet, die Quellcode von einer höheren Programmiersprache in eine niedrigere Programmiersprache (zB Assemblersprache, Objektcode oder Maschinencode) übersetzen, um ein ausführbares Programm zu erstellen.[1][2]:p1

Es gibt viele verschiedene Arten von Compilern, die Ausgaben in verschiedenen nützlichen Formen erzeugen. Ein Compiler, der auf einem Computer ausgeführt werden kann, dessen CPU oder Betriebssystem sich von dem unterscheidet, auf dem der von ihm erzeugte Code ausgeführt wird, wird als a . bezeichnet Cross-Compiler. EIN Bootstrap-Compiler ist in der Sprache geschrieben, die es zu kompilieren beabsichtigt. Ein Programm, das von einer niedrigeren Sprache in eine höhere Sprache übersetzt, ist a Dekompilierer. Ein Programm, das zwischen höheren Sprachen übersetzt, heißt normalerweise a Source-to-Source-Compiler oder Transcompiler. Eine Sprache Umschreiber ist normalerweise ein Programm, das die Form von Ausdrücken ohne Sprachwechsel übersetzt. Der Begriff Compiler-Compiler bezieht sich auf Werkzeuge, die verwendet werden, um Parser zu erstellen, die eine Syntaxanalyse durchführen.

Ein Compiler führt wahrscheinlich viele oder alle der folgenden Operationen durch: Vorverarbeitung, lexikalische Analyse, Parsing, semantische Analyse (syntaxgerichtete Übersetzung), Umwandlung von Eingabeprogrammen in eine Zwischendarstellung, Codeoptimierung und Codegenerierung. Compiler implementieren diese Operationen in Phasen, die ein effizientes Design und korrekte Transformationen der Quelleingabe in die Zielausgabe fördern. Programmfehler, die durch falsches Compilerverhalten verursacht werden, können sehr schwer aufzuspüren und zu umgehen sein; Daher investieren Compiler-Implementierer erhebliche Anstrengungen, um die Korrektheit des Compilers sicherzustellen.[3]

Compiler sind nicht der einzige Sprachprozessor, der verwendet wird, um Quellprogramme zu transformieren. Ein Interpreter ist eine Computersoftware, die die angegebenen Operationen umwandelt und dann ausführt.[2]:p2 Der Übersetzungsprozess beeinflusst das Design von Computersprachen, was zu einer bevorzugten Kompilierung oder Interpretation führt. In der Praxis kann ein Interpreter für kompilierte Sprachen implementiert werden und Compiler können für interpretierte Sprachen implementiert werden.

Geschichte[edit]

Ein Diagramm der Funktionsweise eines typischen mehrsprachigen Compilers mit mehreren Zielen

Theoretische Computerkonzepte, die von Wissenschaftlern, Mathematikern und Ingenieuren entwickelt wurden, bildeten während des Zweiten Weltkriegs die Grundlage für die Entwicklung des digitalen modernen Computers. Primitive Binärsprachen haben sich entwickelt, weil digitale Geräte nur Einsen und Nullen und die Schaltungsmuster in der zugrunde liegenden Maschinenarchitektur verstehen. In den späten 1940er Jahren wurden Assemblersprachen entwickelt, um eine praktikablere Abstraktion der Computerarchitekturen anzubieten. Die begrenzte Speicherkapazität früher Computer führte bei der Entwicklung der ersten Compiler zu erheblichen technischen Herausforderungen. Daher musste der Kompilierungsprozess in mehrere kleine Programme aufgeteilt werden. Die Front-End-Programme produzieren die Analyseprodukte, die von den Back-End-Programmen verwendet werden, um Zielcode zu generieren. Da die Computertechnologie mehr Ressourcen bereitstellte, konnten Compilerdesigns besser auf den Kompilierungsprozess abgestimmt werden.

Normalerweise ist es für einen Programmierer produktiver, eine Hochsprache zu verwenden, daher folgte die Entwicklung von Hochsprachen natürlich auf die Fähigkeiten digitaler Computer. Hochsprachen sind formale Sprachen, die durch ihre Syntax und Semantik streng definiert sind und die Hochsprachenarchitektur bilden. Elemente dieser formalen Sprachen sind:

  • Alphabet, jede endliche Menge von Symbolen;
  • Zeichenfolge, eine endliche Folge von Symbolen;
  • Sprache, ein beliebiger Satz von Zeichenfolgen in einem Alphabet.

Die Sätze in einer Sprache können durch eine Reihe von Regeln definiert werden, die als Grammatik bezeichnet werden.[4]

Die Backus-Naur-Form (BNF) beschreibt die Syntax von “Sätzen” einer Sprache und wurde für die Syntax von Algol 60 von John Backus verwendet.[5] Die Ideen leiten sich von den kontextfreien Grammatikkonzepten des Linguisten Noam Chomsky ab.[6] “BNF und seine Erweiterungen sind zu Standardwerkzeugen geworden, um die Syntax von Programmiernotationen zu beschreiben, und in vielen Fällen werden Teile von Compilern automatisch aus einer BNF-Beschreibung generiert.”[7]

In den 1940er Jahren entwarf Konrad Zuse eine algorithmische Programmiersprache namens Plankalkül. Obwohl es bis in die 1970er Jahre keine tatsächliche Implementierung gab, wurden Konzepte vorgestellt, die später in APL von Ken Iverson in den späten 1950er Jahren zu sehen waren.[8] APL ist eine Sprache für mathematische Berechnungen.

Das Hochsprachendesign während der prägenden Jahre des Digital Computing lieferte nützliche Programmierwerkzeuge für eine Vielzahl von Anwendungen:

  • FORTRAN (Formula Translation) für ingenieur- und naturwissenschaftliche Anwendungen gilt als erste Hochsprache.[9]
  • COBOL (Common Business-Oriented Language) entwickelte sich aus A-0 und FLOW-MATIC zur dominierenden Hochsprache für Geschäftsanwendungen.[10]
  • LISP (List Processor) für die symbolische Berechnung.[11]

Die Compiler-Technologie hat sich aus der Notwendigkeit einer streng definierten Transformation des High-Level-Quellprogramms in ein Low-Level-Zielprogramm für den digitalen Computer entwickelt. Der Compiler könnte als Front-End betrachtet werden, um die Analyse des Quellcodes zu erledigen, und als Back-End, um die Analyse in den Zielcode zu synthetisieren. Eine Optimierung zwischen Frontend und Backend könnte einen effizienteren Zielcode erzeugen.[12]

Einige frühe Meilensteine ​​in der Entwicklung der Compiler-Technologie:

  • 1952: Ein Autocode-Compiler, der von Alick Glennie für den Manchester Mark I-Computer an der University of Manchester entwickelt wurde, wird von einigen als die erste kompilierte Programmiersprache angesehen.
  • 1952: Grace Hoppers Team bei Remington Rand schrieb den Compiler für die Programmiersprache A-0 (und prägte den Begriff the Compiler um es zu beschreiben),[13][14] obwohl der A-0-Compiler eher als Loader oder Linker fungierte als die moderne Vorstellung eines vollständigen Compilers.
  • 1954–1957: Ein Team unter der Leitung von John Backus bei IBM hat FORTRAN entwickelt, das normalerweise als die erste Hochsprache gilt. 1957 stellten sie einen FORTRAN-Compiler fertig, von dem allgemein anerkannt wird, dass er den ersten eindeutig vollständigen Compiler eingeführt hat.
  • 1959: Die Conference on Data Systems Language (CODASYL) hat die Entwicklung von COBOL initiiert. Das COBOL-Design basierte auf A-0 und FLOW-MATIC. In den frühen 1960er Jahren wurde COBOL auf mehreren Architekturen kompiliert.
  • 1958–1962: John McCarthy vom MIT entwarf LISP.[15] Die Symbolverarbeitungsfunktionen boten nützliche Funktionen für die Forschung zu künstlicher Intelligenz. Im Jahr 1962 wurden in der Veröffentlichung von LISP 1.5 einige Tools erwähnt: ein Interpreter von Stephen Russell und Daniel J. Edwards, ein Compiler und Assembler von Tim Hart und Mike Levin.[16]

Frühe Betriebssysteme und Software wurden in Assembler geschrieben. In den 1960er und frühen 1970er Jahren war die Verwendung von Hochsprachen für die Systemprogrammierung aufgrund von Ressourcenbeschränkungen noch umstritten. Mehrere Forschungs- und Industriebemühungen begannen jedoch mit der Verlagerung hin zu höheren Systemprogrammiersprachen, zum Beispiel BCPL, BLISS, B und C.

BCPL (Basic Combined Programming Language) wurde 1966 von Martin Richards an der University of Cambridge entwickelt und wurde ursprünglich als Compiler-Schreibwerkzeug entwickelt.[17] Mehrere Compiler wurden implementiert, Richards’ Buch bietet Einblicke in die Sprache und ihren Compiler.[18] BCPL war nicht nur eine einflussreiche Systemprogrammiersprache, die noch in der Forschung verwendet wird[19] sondern auch eine Grundlage für das Design von B- und C-Sprachen.

BLISS (Basic Language for Implementation of System Software) wurde vom Forschungsteam der Carnegie Mellon University (CMU) von WA Wulf für einen PDP-10-Computer der Digital Equipment Corporation (DEC) entwickelt. Ein Jahr später, 1970, entwickelte das CMU-Team den BLISS-11-Compiler.

An Multics (Multiplexed Information and Computing Service), einem Time-Sharing-Betriebssystemprojekt, waren das MIT, Bell Labs, General Electric (später Honeywell) beteiligt und wurde von Fernando Corbató vom MIT geleitet.[20] Multics wurde in der PL/I-Sprache geschrieben, die von IBM und der IBM User Group entwickelt wurde.[21] Das Ziel von IBM war es, geschäftliche, wissenschaftliche und Systemprogrammierungsanforderungen zu erfüllen. Es gab andere Sprachen, die in Betracht gezogen worden wären, aber PL/I bot die umfassendste Lösung, obwohl sie nicht implementiert war.[22] In den ersten Jahren des Multics-Projekts konnte eine Teilmenge der Sprache mit dem Early PL/I (EPL)-Compiler von Doug McIlory und Bob Morris von Bell Labs in Assembler kompiliert werden.[23] EPL unterstützte das Projekt, bis ein Bootstrapping-Compiler für das vollständige PL/I entwickelt werden konnte.[24]

Bell Labs verließ das Multics-Projekt 1969: “Im Laufe der Zeit wurde die Hoffnung durch Frustration ersetzt, da die Gruppenanstrengungen zunächst nicht zu einem wirtschaftlich sinnvollen System führten.”[25] Eine fortgesetzte Teilnahme würde die Kosten für die Projektunterstützung in die Höhe treiben. Also wandten sich die Forscher anderen Entwicklungsbemühungen zu. Eine Systemprogrammiersprache B basierend auf BCPL-Konzepten wurde von Dennis Ritchie und Ken Thompson geschrieben. Ritchie erstellte einen Boot-Strapping-Compiler für B und schrieb das Betriebssystem Unics (Uniplexed Information and Computing Service) für eine PDP-7 in B. Unics wurde schließlich Unix geschrieben.

Bell Labs begann mit der Entwicklung und Erweiterung von C basierend auf B und BCPL. Der BCPL-Compiler wurde von Bell Labs zu Multics transportiert und BCPL war eine bevorzugte Sprache bei Bell Labs.[26] Anfänglich wurde ein Front-End-Programm für den B-Compiler von Bell Labs verwendet, während ein C-Compiler entwickelt wurde. 1971 stellte eine neue PDP-11 die Ressource bereit, um Erweiterungen für B zu definieren und den Compiler neu zu schreiben. 1973 war das Design der C-Sprache im Wesentlichen abgeschlossen und der Unix-Kernel für eine PDP-11 wurde in C umgeschrieben. Steve Johnson begann mit der Entwicklung des Portable C Compiler (PCC), um die Neuausrichtung von C-Compilern auf neue Maschinen zu unterstützen.[27][28]

Die objektorientierte Programmierung (OOP) bot einige interessante Möglichkeiten für die Anwendungsentwicklung und -pflege. OOP-Konzepte gehen weiter zurück, waren aber Teil der LISP- und Simula-Sprachwissenschaft.[29] Bei Bell Labs interessierte sich die Entwicklung von C++ für OOP.[30] C++ wurde erstmals 1980 für die Systemprogrammierung verwendet. Das ursprüngliche Design nutzte die Programmierfähigkeiten von C-Sprachsystemen mit Simula-Konzepten. Objektorientierte Einrichtungen kamen 1983 hinzu.[31] Das Cfront-Programm implementierte ein C++-Frontend für den C84-Sprachcompiler. In den folgenden Jahren wurden mehrere C++-Compiler entwickelt, als C++ immer beliebter wurde.

In vielen Anwendungsdomänen hat sich die Idee, eine höhere Sprache zu verwenden, schnell durchgesetzt. Aufgrund der erweiterten Funktionalität, die von neueren Programmiersprachen unterstützt wird, und der zunehmenden Komplexität von Computerarchitekturen wurden Compiler komplexer.

DARPA (Defense Advanced Research Projects Agency) sponserte 1970 ein Compiler-Projekt mit dem CMU-Forschungsteam von Wulf. Das PQCC-Design des Production Quality Compiler-Compilers würde einen Production Quality Compiler (PQC) aus formalen Definitionen der Quellsprache und des Ziels erzeugen.[32] PQCC versuchte ohne großen Erfolg, den Begriff Compiler-Compiler über die traditionelle Bedeutung als Parser-Generator (zB Yacc) hinaus zu erweitern. PQCC könnte besser als Compiler-Generator bezeichnet werden.

Die PQCC-Forschung zum Codegenerierungsprozess zielte darauf ab, ein wirklich automatisches Compiler-Schreibsystem aufzubauen. Die Bemühungen entdeckten und gestalteten die Phasenstruktur des PQC. Der BLISS-11-Compiler lieferte die anfängliche Struktur.[33] Die Phasen umfassten Analysen (Front-End), Zwischenübersetzung in die virtuelle Maschine (Middle-End) und Übersetzung ins Ziel (Back-End). TCOL wurde für die PQCC-Forschung entwickelt, um sprachspezifische Konstrukte in der Zwischendarstellung zu handhaben.[34] Variationen von TCOL unterstützten verschiedene Sprachen. Das PQCC-Projekt untersuchte Techniken der automatisierten Compilerkonstruktion. Die Entwurfskonzepte erwiesen sich als nützlich bei der Optimierung von Compilern und Compilern für die objektorientierte Programmiersprache Ada.

Das Ada-Stoneman-Dokument formalisierte die Programmunterstützungsumgebung (APSE) zusammen mit dem Kernel (KAPSE) und Minimal (MAPSE). Ein Ada-Interpreter NYU/ED unterstützte die Entwicklungs- und Standardisierungsbemühungen mit dem American National Standards Institute (ANSI) und der International Standards Organization (ISO). Die anfängliche Entwicklung des Ada-Compilers durch die US-Militärdienste umfasste die Compiler in einer vollständig integrierten Designumgebung nach dem Vorbild des Stoneman-Dokuments. Armee und Marine arbeiteten am Ada Language System (ALS)-Projekt, das auf die DEC/VAX-Architektur abzielte, während die Air Force an der Ada Integrated Environment (AIE) für die IBM 370-Serie begann. Obwohl die Projekte nicht die gewünschten Ergebnisse lieferten, trugen sie doch zu den Gesamtanstrengungen bei der Entwicklung von Ada bei.[35]

Andere Bemühungen um einen Ada-Compiler begannen in Großbritannien an der University of York und in Deutschland an der Universität Karlsruhe. In den USA lieferte Verdix (später von Rational übernommen) das Verdix Ada Development System (VADS) an die Armee. VADS stellte eine Reihe von Entwicklungstools einschließlich eines Compilers zur Verfügung. Unix/VADS könnte auf einer Vielzahl von Unix-Plattformen wie DEC Ultrix und Sun 3/60 Solaris für Motorola 68020 in einer CECOM-Evaluierung der Armee gehostet werden.[36] Bald waren viele Ada-Compiler verfügbar, die die Ada-Validierungstests bestanden haben. Das GNU-Projekt der Free Software Foundation hat die GNU Compiler Collection (GCC) entwickelt, die eine Kernfähigkeit zur Unterstützung mehrerer Sprachen und Ziele bietet. Die Ada-Version GNAT ist einer der am weitesten verbreiteten Ada-Compiler. GNAT ist kostenlos, aber es gibt auch kommerziellen Support, zum Beispiel wurde AdaCore 1994 gegründet, um kommerzielle Softwarelösungen für Ada bereitzustellen. GNAT Pro enthält das GNU GCC-basierte GNAT mit einer Tool-Suite, um eine integrierte Entwicklungsumgebung bereitzustellen.

Hochsprachen haben die Compiler-Forschung und -Entwicklung weiter vorangetrieben. Schwerpunkte waren Optimierung und automatische Codegenerierung. Trends in Programmiersprachen und Entwicklungsumgebungen beeinflussten die Compilertechnologie. Weitere Compiler wurden in Sprachdistributionen (PERL, Java Development Kit) und als Bestandteil einer IDE (VADS, Eclipse, Ada Pro) aufgenommen. Die Wechselbeziehung und gegenseitige Abhängigkeit von Technologien wuchs. Das Aufkommen von Webdiensten förderte das Wachstum von Websprachen und Skriptsprachen. Skripte gehen auf die Anfänge von Command Line Interfaces (CLI) zurück, wo der Benutzer Befehle eingeben konnte, die vom System ausgeführt werden sollten. Benutzer Shell-Konzepte, die mit Sprachen entwickelt wurden, um Shell-Programme zu schreiben. Frühe Windows-Designs boten eine einfache Stapelprogrammierungsfunktion. Die konventionelle Transformation dieser Sprache verwendet einen Dolmetscher. Obwohl sie nicht weit verbreitet sind, wurden Bash- und Batch-Compiler geschrieben. In jüngerer Zeit wurden anspruchsvolle interpretierte Sprachen Teil des Entwickler-Toolkits. Moderne Skriptsprachen sind PHP, Python, Ruby und Lua. (Lua wird häufig in der Spieleentwicklung verwendet.) Alle haben Interpreter- und Compiler-Unterstützung.[37]

„Als das Compiler-Gebiet Ende der 50er Jahre begann, beschränkte sich sein Fokus auf die Übersetzung von Hochsprachenprogrammen in Maschinencode … Das Compiler-Gebiet wird zunehmend mit anderen Disziplinen wie Computerarchitektur, Programmiersprachen, formalen Methoden, Softwareentwicklung und Computersicherheit.”[38] Der Artikel “Compiler Research: The Next 50 Years” weist auf die Bedeutung von objektorientierten Sprachen und Java hin. Als zukünftige Forschungsziele wurden Sicherheit und Parallel Computing genannt.

Compileraufbau[edit]

Ein Compiler implementiert eine formale Transformation von einem High-Level-Quellprogramm zu einem Low-Level-Zielprogramm. Compiler-Design kann eine End-to-End-Lösung definieren oder eine definierte Untermenge angehen, die mit anderen Kompilierungswerkzeugen, zB Präprozessoren, Assemblern, Linkern, verbunden ist. Designanforderungen umfassen streng definierte Schnittstellen sowohl intern zwischen Compilerkomponenten als auch extern zwischen unterstützenden Toolsets.

In der Anfangszeit wurde der Ansatz für das Compiler-Design direkt von der Komplexität der zu verarbeitenden Computersprache, der Erfahrung der Person(en) und den verfügbaren Ressourcen beeinflusst. Ressourcenbeschränkungen führten dazu, dass der Quellcode mehr als einmal durchlaufen werden musste.

Ein Compiler für eine relativ einfache Sprache, die von einer Person geschrieben wurde, kann ein einzelnes, monolithisches Stück Software sein. Mit zunehmender Komplexität der Quellsprache kann das Design jedoch in mehrere voneinander abhängige Phasen aufgeteilt werden. Separate Phasen bieten Designverbesserungen, die die Entwicklung auf die Funktionen im Kompilierungsprozess konzentrieren.

One-Pass- oder Multi-Pass-Compiler[edit]

Das Klassifizieren von Compilern nach der Anzahl von Durchläufen hat seinen Hintergrund in den Hardware-Ressourcenbeschränkungen von Computern. Das Kompilieren erfordert viel Arbeit, und frühe Computer hatten nicht genug Speicher, um ein Programm zu enthalten, das all diese Arbeit erledigte. Daher wurden Compiler in kleinere Programme aufgeteilt, die jeweils die Quelle (oder eine Darstellung davon) durchgingen und einige der erforderlichen Analysen und Übersetzungen durchführten.

Die Fähigkeit, in einem einzigen Durchlauf zu kompilieren, wurde klassischerweise als Vorteil angesehen, da sie das Schreiben eines Compilers vereinfacht und Compiler mit einem Durchlauf Kompilierungen im Allgemeinen schneller ausführen als Compiler mit mehreren Durchläufen. Daher wurden, teilweise getrieben durch die Ressourcenbeschränkungen früherer Systeme, viele frühe Sprachen speziell so entwickelt, dass sie in einem einzigen Durchlauf kompiliert werden konnten (zB Pascal).

In einigen Fällen kann es beim Entwurf einer Sprachfunktion erforderlich sein, dass ein Compiler mehr als einen Durchgang über die Quelle durchführt. Betrachten Sie zum Beispiel eine Erklärung in Zeile 20 der Quelle, die die Übersetzung einer in Zeile 10 erscheinenden Aussage beeinflusst. In diesem Fall müssen im ersten Durchgang Informationen über Erklärungen gesammelt werden, die nach Aussagen erscheinen, die sie betreffen, wobei die eigentliche Übersetzung stattfindet translation bei einem weiteren Durchgang.

Der Nachteil des Kompilierens in einem einzigen Durchlauf besteht darin, dass es nicht möglich ist, viele der ausgeklügelten Optimierungen durchzuführen, die erforderlich sind, um qualitativ hochwertigen Code zu generieren. Es kann schwierig sein, genau zu zählen, wie viele Durchläufe ein optimierender Compiler macht. Beispielsweise können verschiedene Optimierungsphasen einen Ausdruck viele Male analysieren, einen anderen Ausdruck jedoch nur einmal analysieren.

Das Aufteilen eines Compilers in kleine Programme ist eine Technik, die von Forschern verwendet wird, die daran interessiert sind, nachweislich korrekte Compiler zu erstellen. Der Nachweis der Korrektheit einer Reihe kleiner Programme erfordert oft weniger Aufwand als der Nachweis der Korrektheit eines größeren, einzelnen gleichwertigen Programms.

Dreistufige Compilerstruktur[edit]

Unabhängig von der genauen Anzahl der Phasen im Compiler-Design können die Phasen einer von drei Stufen zugeordnet werden. Die Stufen umfassen ein Front-End, ein Middle-End und ein Back-End.

  • Das Frontend scannt die Eingabe und überprüft Syntax und Semantik gemäß einer bestimmten Quellsprache. Für statisch typisierte Sprachen führt es eine Typprüfung durch, indem es Typinformationen sammelt. Wenn das Eingabeprogramm syntaktisch inkorrekt ist oder einen Typfehler aufweist, generiert es Fehler- und/oder Warnmeldungen, die normalerweise die Stelle im Quellcode angeben, an der das Problem erkannt wurde; in einigen Fällen kann der eigentliche Fehler (viel) früher im Programm liegen. Aspekte des Frontends umfassen lexikalische Analyse, Syntaxanalyse und semantische Analyse. Das Frontend wandelt das Eingabeprogramm in eine Zwischenrepräsentation (IR) für die weitere Verarbeitung durch das Middleend um. Diese IR ist normalerweise eine untergeordnete Darstellung des Programms in Bezug auf den Quellcode.
  • Das mittleres Ende führt Optimierungen an der IR durch, die unabhängig von der CPU-Architektur sind, die anvisiert wird. Diese Unabhängigkeit von Quellcode/Maschinencode soll ermöglichen, dass generische Optimierungen zwischen Versionen des Compilers geteilt werden, die verschiedene Sprachen und Zielprozessoren unterstützen. Beispiele für Middle-End-Optimierungen sind das Entfernen von nutzlosem (Dead Code Elimination) oder unerreichbarem Code (Erreichbarkeitsanalyse), Entdeckung und Weitergabe von konstanten Werten (konstante Ausbreitung), Verlagerung der Berechnung an einen weniger häufig ausgeführten Ort (z. B. außerhalb einer Schleife) oder Spezialisierung der Berechnung basierend auf dem Kontext. Schließlich wird die “optimierte” IR erzeugt, die vom Back-End verwendet wird.
  • Das hinteres Ende nimmt die optimierte IR vom mittleren Ende. Es kann weitere Analysen, Transformationen und Optimierungen durchführen, die spezifisch für die Ziel-CPU-Architektur sind. Das Backend generiert den zielabhängigen Assemblercode und führt dabei die Registerzuordnung durch. Das Back-End führt eine Befehlsplanung durch, die Befehle neu anordnet, um parallele Ausführungseinheiten zu beschäftigen, indem Verzögerungsschlitze gefüllt werden. Obwohl die meisten Optimierungsprobleme NP-schwer sind, sind heuristische Techniken zu ihrer Lösung gut entwickelt und derzeit in Compilern in Produktionsqualität implementiert. Typischerweise ist die Ausgabe eines Back-Ends Maschinencode, der auf einen bestimmten Prozessor und ein bestimmtes Betriebssystem spezialisiert ist.

Dieser Front/Middle/Back-End-Ansatz ermöglicht es, Frontends für verschiedene Sprachen mit Backends für verschiedene CPUs zu kombinieren und gleichzeitig die Optimierungen des Middle-Ends zu teilen.[39] Praktische Beispiele für diesen Ansatz sind die GNU Compiler Collection, Clang (LLVM-basierter C/C++-Compiler),[40] und das Amsterdam Compiler Kit, das über mehrere Front-Ends, gemeinsame Optimierungen und mehrere Back-Ends verfügt.

Frontend[edit]

Lexer- und Parser-Beispiel für C. Ausgehend von der Zeichenfolge “if(net>0.0)total+=net*(1.0+tax/100.0);“, stellt der Scanner eine Sequenz von Token zusammen und kategorisiert jeden von ihnen, zum Beispiel als Kennung, reserviertes Wort, Zahlenbuchstabe, oder Operator. Letztere Sequenz wird vom Parser in einen Syntaxbaum umgewandelt, der dann von den restlichen Compilerphasen behandelt wird. Der Scanner und Parser handhabt die regulären bzw. korrekt kontextfreien Teile der Grammatik für C.

Das Frontend analysiert den Quellcode, um eine interne Darstellung des Programms zu erstellen, die als Zwischendarstellung (IR) bezeichnet wird. Es verwaltet auch die Symboltabelle, eine Datenstruktur, die jedes Symbol im Quellcode den zugehörigen Informationen wie Ort, Typ und Umfang zuordnet.

Während das Frontend eine einzelne monolithische Funktion oder ein einzelnes Programm sein kann, wie bei einem scannerlosen Parser, wurde es traditionell als mehrere Phasen implementiert und analysiert, die sequentiell oder gleichzeitig ausgeführt werden können. Diese Methode wird aufgrund ihrer Modularität und Trennung der Belange bevorzugt. Am häufigsten wird das Frontend heute in drei Phasen unterteilt: lexikalische Analyse (auch bekannt als Lexing oder Scanning), Syntaxanalyse (auch bekannt als Scanning oder Parsing) und semantische Analyse. Lexing und Parsen umfassen die syntaktische Analyse (Wortsyntax bzw. Phrasensyntax), und in einfachen Fällen können diese Module (der Lexer und der Parser) automatisch aus einer Grammatik für die Sprache generiert werden, obwohl diese in komplexeren Fällen eine manuelle Änderung erfordern . Die lexikalische Grammatik und die Phrasengrammatik sind normalerweise kontextfreie Grammatiken, was die Analyse erheblich vereinfacht, wobei die Kontextsensitivität in der semantischen Analysephase behandelt wird. Die semantische Analysephase ist in der Regel komplexer und von Hand geschrieben, kann aber mithilfe von Attributgrammatiken teilweise oder vollständig automatisiert werden. Diese Phasen selbst lassen sich weiter untergliedern: Lexing als Scannen und Auswerten, und Parsing als Aufbau eines konkreten Syntaxbaums (CST, Parse-Baum) und dann dessen Umwandlung in einen abstrakten Syntaxbaum (AST, Syntaxbaum). In einigen Fällen werden zusätzliche Phasen verwendet, insbesondere Linienrekonstruktion und Vorverarbeitung, aber diese sind selten.

Die Hauptphasen des Frontends umfassen die folgenden:

  • Linienrekonstruktion wandelt die eingegebene Zeichenfolge in eine kanonische Form um, die für den Parser bereit ist. Sprachen, die ihre Schlüsselwörter streichen oder beliebige Leerzeichen innerhalb von Bezeichnern zulassen, benötigen diese Phase. Die in den 1960er Jahren verwendeten von oben nach unten, rekursiv absteigenden, tabellengesteuerten Parser lesen die Quelle normalerweise zeichenweise und erforderten keine separate Tokenisierungsphase. Atlas Autocode und Imp (und einige Implementierungen von ALGOL und Coral 66) sind Beispiele für gestrichene Sprachen, deren Compiler a Linienrekonstruktion Phase.
  • Vorverarbeitung unterstützt Makroersetzung und bedingte Kompilierung. Typischerweise erfolgt die Vorverarbeitungsphase vor der syntaktischen oder semantischen Analyse; zB im Fall von C manipuliert der Präprozessor eher lexikalische Token als syntaktische Formen. Einige Sprachen wie Scheme unterstützen jedoch Makrosubstitutionen basierend auf syntaktischen Formen.
  • Lexikalische Analyse (auch bekannt als lexing oder Tokenisierung) zerlegt den Quellcodetext in eine Folge kleiner Teile namens lexikalische Token.[41] Diese Phase kann in zwei Phasen unterteilt werden: die Scannen, die den Eingabetext in syntaktische Einheiten namens . segmentiert Lexeme und weist ihnen eine Kategorie zu; und der auswertend, die Lexeme in einen verarbeiteten Wert umwandelt. Ein Token ist ein Paar bestehend aus a Token-Name und eine optionale Token-Wert.[42] Übliche Tokenkategorien können Bezeichner, Schlüsselwörter, Trennzeichen, Operatoren, Literale und Kommentare umfassen, obwohl der Satz von Tokenkategorien in verschiedenen Programmiersprachen variiert. Die Lexemsyntax ist normalerweise eine reguläre Sprache, sodass ein aus einem regulären Ausdruck konstruierter endlicher Automat verwendet werden kann, um sie zu erkennen. Die Software, die eine lexikalische Analyse durchführt, wird als lexikalischer Analysator bezeichnet. Dies ist möglicherweise kein separater Schritt – er kann mit dem Parsing-Schritt beim scannerlosen Parsing kombiniert werden, in diesem Fall wird das Parsing auf Zeichenebene und nicht auf Token-Ebene durchgeführt.
  • Syntaxanalyse (auch bekannt als Parsing) beinhaltet das Parsen der Tokensequenz, um die syntaktische Struktur des Programms zu identifizieren. Diese Phase erstellt typischerweise einen Parse-Baum, der die lineare Sequenz von Token durch eine Baumstruktur ersetzt, die gemäß den Regeln einer formalen Grammatik aufgebaut ist, die die Syntax der Sprache definiert. Der Parse-Baum wird oft durch spätere Phasen im Compiler analysiert, erweitert und transformiert.[43]
  • Semantische Analyse fügt dem Analysebaum semantische Informationen hinzu und erstellt die Symboltabelle. In dieser Phase werden semantische Prüfungen wie Typprüfung (Prüfen auf Typfehler) oder Objektbindung (Verknüpfen von Variablen- und Funktionsreferenzen mit ihren Definitionen) oder definitive Zuweisung (erfordert, dass alle lokalen Variablen vor der Verwendung initialisiert werden müssen), das Zurückweisen falscher Programme oder das Ausgeben von Warnungen. Die semantische Analyse erfordert normalerweise einen vollständigen Analysebaum, was bedeutet, dass diese Phase logisch auf die Analysephase folgt und logisch der Codegenerierungsphase vorausgeht, obwohl es oft möglich ist, mehrere Phasen in einem Durchlauf über den Code in einer Compilerimplementierung zusammenzufassen.

Mittleres Ende[edit]

Das mittlere Ende, auch bekannt als Optimierer, führt Optimierungen an der Zwischendarstellung durch, um die Leistung und die Qualität des erzeugten Maschinencodes zu verbessern.[44] Das mittlere Ende enthält die Optimierungen, die unabhängig von der angestrebten CPU-Architektur sind.

Die Hauptphasen des mittleren Endes umfassen die folgenden:

Die Compileranalyse ist die Voraussetzung für jede Compileroptimierung und sie arbeiten eng zusammen. Beispielsweise ist die Abhängigkeitsanalyse für die Schleifentransformation von entscheidender Bedeutung.

Der Umfang der Compileranalyse und -optimierungen variiert stark; ihr Umfang kann vom Arbeiten innerhalb eines Basisblocks bis hin zu ganzen Prozeduren oder sogar dem gesamten Programm reichen. Es gibt einen Kompromiss zwischen der Granularität der Optimierungen und den Kosten der Kompilierung. Beispielsweise sind Guckloch-Optimierungen während der Kompilierung schnell durchzuführen, betreffen jedoch nur ein kleines lokales Fragment des Codes und können unabhängig von dem Kontext durchgeführt werden, in dem das Codefragment erscheint. Im Gegensatz dazu benötigt die interprozedurale Optimierung mehr Kompilierzeit und Speicherplatz, ermöglicht aber Optimierungen, die nur durch die Berücksichtigung des Verhaltens mehrerer Funktionen gleichzeitig möglich sind.

Interprozedurale Analysen und Optimierungen sind in modernen kommerziellen Compilern von HP, IBM, SGI, Intel, Microsoft und Sun Microsystems üblich. Der freien Software GCC wurde lange Zeit das Fehlen leistungsfähiger interprozeduraler Optimierungen vorgeworfen, doch in dieser Hinsicht ändert sie sich. Ein weiterer Open-Source-Compiler mit vollständiger Analyse- und Optimierungsinfrastruktur ist Open64, der von vielen Organisationen für Forschungs- und kommerzielle Zwecke verwendet wird.

Aufgrund des zusätzlichen Zeit- und Platzbedarfs für Compileranalysen und -optimierungen überspringen einige Compiler diese standardmäßig. Benutzer müssen Kompilierungsoptionen verwenden, um dem Compiler explizit mitzuteilen, welche Optimierungen aktiviert werden sollen.

Back-End[edit]

Das Backend ist verantwortlich für die CPU-Architektur spezifischen Optimierungen und für die Codegenerierung[44].

Die Hauptphasen des Backends umfassen die folgenden:

  • Maschinenabhängige Optimierungen: Optimierungen, die von den Details der CPU-Architektur abhängen, auf die der Compiler abzielt.[45] Ein prominentes Beispiel sind Guckloch-Optimierungen, die kurze Sequenzen von Assembler-Befehlen in effizientere Befehle umschreiben.
  • Codegenerierung: Die transformierte Zwischensprache wird in die Ausgabesprache übersetzt, normalerweise die native Maschinensprache des Systems. Dies beinhaltet Ressourcen- und Speicherentscheidungen, wie z. B. die Entscheidung, welche Variablen in Register und Speicher passen, und die Auswahl und Planung geeigneter Maschinenbefehle zusammen mit ihren zugehörigen Adressierungsmodi (siehe auch Sethi-Ullman-Algorithmus). Debug-Daten müssen möglicherweise auch generiert werden, um das Debuggen zu erleichtern.

Compiler-Korrektheit[edit]

Compiler-Korrektheit ist der Zweig der Softwaretechnik, der sich damit befasst, zu zeigen, dass sich ein Compiler gemäß seiner Sprachspezifikation verhält.[citation needed] Zu den Techniken gehören die Entwicklung des Compilers unter Verwendung formaler Methoden und die Verwendung strenger Tests (oft als Compilervalidierung bezeichnet) an einem vorhandenen Compiler.

Kompilierte versus interpretierte Sprachen[edit]

Höhere Programmiersprachen erscheinen normalerweise mit einer Art der Übersetzung im Hinterkopf: entweder als kompilierte Sprache oder als interpretierte Sprache konzipiert. In der Praxis gibt es jedoch selten etwas an einer Sprache, die erfordert es kann ausschließlich kompiliert oder ausschließlich interpretiert werden, obwohl es möglich ist, Sprachen zu entwerfen, die zur Laufzeit auf Neuinterpretation angewiesen sind. Die Kategorisierung spiegelt normalerweise die beliebtesten oder am weitesten verbreiteten Implementierungen einer Sprache wider – zum Beispiel wird BASIC manchmal als interpretierte Sprache und C als kompilierte Sprache bezeichnet, obwohl es BASIC-Compiler und C-Interpreter gibt.

Die Interpretation ersetzt die Kompilierung nicht vollständig. Es verbirgt es nur vor dem Benutzer und macht es schrittweise. Auch wenn ein Interpreter selbst interpretiert werden kann, wird irgendwo unten im Stapel ein direkt ausgeführtes Programm benötigt (siehe Maschinensprache).

Außerdem können Compiler aus Optimierungsgründen Interpreter enthalten. Wenn beispielsweise ein Ausdruck während der Kompilierung ausgeführt und die Ergebnisse in das Ausgabeprogramm eingefügt werden können, dann wird verhindert, dass er bei jedem Programmlauf neu berechnet werden muss, was das endgültige Programm erheblich beschleunigen kann. Moderne Trends zur Just-in-Time-Kompilierung und Bytecode-Interpretation verwischen manchmal die traditionellen Kategorisierungen von Compilern und Interpretern noch weiter.

Einige Sprachspezifikationen weisen darauf hin, dass Implementierungen Muss eine Kompilierungsfunktion einschließen; zum Beispiel Common Lisp. Die Definition von Common Lisp enthält jedoch nichts, was ihre Interpretation verhindert. Andere Sprachen haben Funktionen, die in einem Interpreter sehr einfach zu implementieren sind, aber das Schreiben eines Compilers viel schwieriger machen; APL, SNOBOL4 und viele Skriptsprachen ermöglichen es Programmen beispielsweise, zur Laufzeit mit regulären Zeichenfolgenoperationen beliebigen Quellcode zu erstellen und diesen Code dann auszuführen, indem sie ihn an eine spezielle Auswertungsfunktion übergeben. Um diese Funktionen in einer kompilierten Sprache zu implementieren, müssen Programme normalerweise mit einer Laufzeitbibliothek geliefert werden, die eine Version des Compilers selbst enthält.

Eine Klassifizierung von Compilern erfolgt nach der Plattform, auf der ihr generierter Code ausgeführt wird. Dies ist bekannt als die Zielplattform auf.

EIN gebürtig oder bereitgestellt Compiler ist ein Compiler, dessen Ausgabe direkt auf demselben Computer- und Betriebssystemtyp ausgeführt werden soll, auf dem der Compiler selbst ausgeführt wird. Die Ausgabe eines Cross-Compilers ist für die Ausführung auf einer anderen Plattform ausgelegt. Cross-Compiler werden häufig bei der Entwicklung von Software für eingebettete Systeme verwendet, die keine Softwareentwicklungsumgebung unterstützen sollen.

Die Ausgabe eines Compilers, der Code für eine virtuelle Maschine (VM) erzeugt, kann auf derselben Plattform wie der Compiler, der sie erstellt hat, ausgeführt werden oder nicht. Aus diesem Grund werden solche Compiler normalerweise nicht als native oder Cross-Compiler klassifiziert.

Die untergeordnete Sprache, die das Ziel eines Compilers ist, kann selbst eine höhere Programmiersprache sein. C, von manchen als eine Art portable Assemblersprache angesehen, ist häufig die Zielsprache solcher Compiler. Cfront, der ursprüngliche Compiler für C++, verwendet beispielsweise C als Zielsprache. Der von einem solchen Compiler generierte C-Code ist normalerweise nicht dafür gedacht, von Menschen gelesen und gewartet werden zu können, daher werden Einrückungsstile und das Erstellen von hübschem C-Zwischencode ignoriert. Zu den Merkmalen von C, die es zu einer guten Zielsprache machen, gehören die #line -Direktive, die vom Compiler generiert werden kann, um das Debuggen des ursprünglichen Quellcodes zu unterstützen, und die breite Plattformunterstützung, die mit C-Compilern verfügbar ist.

Während ein gängiger Compilertyp Maschinencode ausgibt, gibt es viele andere Typen:

  • Source-to-Source-Compiler sind ein Compilertyp, der eine Hochsprache als Eingabe verwendet und eine Hochsprache ausgibt. Zum Beispiel nimmt ein automatisch parallelisierender Compiler häufig ein Hochsprachenprogramm als Eingabe auf, transformiert den Code dann und kommt mit parallelen Codeannotationen (z. B. OpenMP) oder Sprachkonstrukten (z. B. Fortrans DOALL Aussagen).
  • Bytecode-Compiler, die in die Assemblersprache einer theoretischen Maschine kompilieren, wie einige Prolog-Implementierungen
  • Just-in-Time-Compiler (JIT-Compiler) verschieben die Kompilierung bis zur Laufzeit. JIT-Compiler gibt es für viele moderne Sprachen, darunter Python, JavaScript, Smalltalk, Java, die Common Intermediate Language (CIL) von Microsoft .NET und andere. Ein JIT-Compiler läuft im Allgemeinen innerhalb eines Interpreters. Wenn der Interpreter erkennt, dass ein Codepfad “heiß” ist, was bedeutet, dass er häufig ausgeführt wird, wird der JIT-Compiler aufgerufen und kompiliert den “heißen” Code, um die Leistung zu erhöhen.
    • Für einige Sprachen, wie beispielsweise Java, werden Anwendungen zunächst unter Verwendung eines Bytecode-Compilers kompiliert und in einer maschinenunabhängigen Zwischendarstellung geliefert. Ein Bytecode-Interpreter führt den Bytecode aus, aber der JIT-Compiler übersetzt den Bytecode in Maschinencode, wenn eine höhere Leistung erforderlich ist.[46][non-primary source needed]
  • Hardware-Compiler (auch Synthese-Tools genannt) sind Compiler, deren Ausgabe eine Beschreibung der Hardware-Konfiguration anstelle einer Befehlsfolge ist.
  • Ein Monteur ist ein Programm, das von Menschen lesbare Assemblersprache in Maschinencode kompiliert, die eigentlichen Anweisungen, die von Hardware ausgeführt werden. Das inverse Programm, das Maschinencode in Assembler übersetzt, wird Disassembler genannt.
  • Ein Programm, das von einer Low-Level-Sprache in eine höhere übersetzt, ist ein Decompiler.[citation needed]
  • Ein Programm, das zwischen höheren Sprachen übersetzt, wird normalerweise als Sprachübersetzer, Source-to-Source-Compiler, Sprachkonverter oder Sprachumschreiber bezeichnet.[citation needed] Der letzte Begriff wird normalerweise für Übersetzungen verwendet, die keinen Sprachwechsel beinhalten.[50]
  • Ein Programm, das in ein Objektcodeformat übersetzt, das auf der Kompilierungsmaschine nicht unterstützt wird, wird Cross-Compiler genannt und wird üblicherweise verwendet, um Code für eingebettete Anwendungen vorzubereiten.[citation needed][clarification needed]
  • Ein Programm, das Objektcode zurück in denselben Typ von Objektcode umschreibt, während Optimierungen und Transformationen angewendet werden, ist ein binärer Recompiler.

Siehe auch[edit]

Verweise[edit]

  1. ^ PC Mag Staff (28. Februar 2019). “Enzyklopädie: Definition des Compilers”. PCMag.com. Abgerufen 28. Februar 2017.
  2. ^ ein b Compiler: Prinzipien, Techniken und Werkzeuge von Alfred V. Aho, Ravi Sethi, Jeffrey D. Ullman – Zweite Ausgabe, 2007
  3. ^ Sonne, Chennian; Le, Vu; Zhang, Qirun; Su, Zhendong (2016). “Auf dem Weg zum Verständnis von Compiler-Bugs in GCC und LLVM”. ACM.
  4. ^ Vorlesungsnotizen Compiler: Prinzipien, Techniken und Tools Jing-Shin Chang Department of Computer Science & Information Engineering National Chi-Nan University
  5. ^ Naur, P. et al. “Bericht über ALGOL 60”. Mitteilungen des ACM 3 (Mai 1960), 299–314.
  6. ^
  7. ^ Gries, David (2012). “Anhang 1: Backus-Naur-Formular”. Die Wissenschaft des Programmierens. Springer Wissenschaft & Wirtschaftsmedien. s. 304. ISBN 978-1461259831.
  8. ^ Iverson, Kenneth E. (1962). Eine Programmiersprache. John Wiley & Söhne. ISBN 978-0-471430-14-8.
  9. ^ Backus, John. “Die Geschichte von FORTRAN I, II und III” (PDF). Geschichte der Programmiersprachen. Softwarepreservation.org.
  10. ^ Porter Adams, Vicki (5. Oktober 1981). „Captain Grace M. Hopper: die Mutter von COBOL“. InfoWelt. 3 (20): 33. ISSN 0199-6649.
  11. ^ McCarthy, J.; Brayton, R.; Edwards, D.; Fox, P.; Hodes, L.; Luckham, D.; Maling, K.; Park, D.; Russell, S. (März 1960). “LISP I Programmierhandbuch” (PDF). Boston, Massachusetts: Gruppe für künstliche Intelligenz, MIT-Rechenzentrum und Forschungslabor.
  12. ^ Compiler-Prinzipien, Techniken & Tools 2. Auflage von Aho, Lam, Sethi, Ullman ISBN 0-321-48681-1
  13. ^ Hopper, Grace Murray (1952). „Die Ausbildung eines Computers“. Proceedings of the 1952 ACM National Meeting (Pittsburgh): 243–249. mach:10.1145/609784.609818. S2CID 10081016.
  14. ^ Ridgway, Richard K. (1952). “Routinen kompilieren”. Tagungsband des ACM National Meeting 1952 (Toronto): 1–5. mach:10.1145/800259.808980. S2CID 14878552.
  15. ^ Rekursive Funktionen symbolischer Ausdrücke und deren maschinelle Berechnung“, Mitteilungen der ACM, April 1960
  16. ^ McCarthy, John; Abrahams, Paul W.; Edwards, Daniel J.; Hart, Timothy P.; Levin, Michael I. (1965). Lisp 1.5 Programmierhandbuch. Die MIT-Presse. ISBN 9780262130110.
  17. ^ BCPL: Ein Werkzeug zum Compiler-Schreiben und zur Systemprogrammierung“M. Richards, Mathematisches Labor der Universität Cambridge, England 1969
  18. ^ BCPL: The Language and Its Compiler, M. Richards, Cambridge University Press (Erstveröffentlichung 31. Dezember 1981)
  19. ^ Das BCPL Cintsys und Cintpos Benutzerhandbuch, M. Richards, 2017
  20. ^ Corbató, FJ; Wyssotski, VA “Einführung und Überblick über das MULTICS-System”. 1965 gemeinsame Computerkonferenz im Herbst Fall. Multicians.org.
  21. ^ Bericht II des SHARE Advanced Language Development Committee, 25. Juni 1964
  22. ^ Multicians.org Artikel “The Choice of PL/I”, Herausgeber /tom Van Vleck
  23. ^ “PL/I als Werkzeug für die Systemprogrammierung”, FJ Corbato, Ausgabe von Datamation vom 6. Mai 1969
  24. ^ Der Multics PL/1 Compiler“, RA Freiburghouse, GE, Fall Joint Computer Conference 1969
  25. ^ Datamation-Spalte, 1969
  26. ^ Dennis M. Ritchie, “Die Entwicklung der C-Sprache“, ACM Second History of Programming Languages ​​Conference, April 1993
  27. ^ SC Johnson, “a Portable C Compiler: Theory and Practice”, 5. ACM POPL Symposium, Januar 1978
  28. ^ A. Snyder, Ein tragbarer Compiler für die Sprache C, MIT, 1974.
  29. ^ K. Nygarard, Universität Oslo, Norwegen, “Grundkonzepte der objektorientierten Programmierung“, SIGPLAN-Mitteilungen V21, 1986
  30. ^ B. Stroustrup: “Was ist objektorientierte Programmierung?” Tagungsband 14. ASU-Konferenz, 1986.
  31. ^ Bjarne Stroustrup, “An Overview of the C++ Programming Language”, Handbook of Object Technology (Herausgeber: Saba Zamir, ISBN 0-8493-3135-8)
  32. ^ Leverett, Cattell, Hobbs, Newcomer, Reiner, Schatz, Wulf: “An Overview of the Production Quality Compiler-Compiler Project”, CMU-CS-89-105, 1979
  33. ^ W. Wulf, K. Nori, “Verzögerte Bindung in PQCC-generierten Compilern“, CMU Research Showcase Report, CMU-CS-82-138, 1982
  34. ^ Joseph M. Newcomer, David Alex Lamb, Bruce W. Leverett, Michael Tighe, William A. Wulf – Carnegie-Mellon University und David Levine, Andrew H. Reinerit – Intermetrics: „TCOL Ada: Revised Report on An Intermediate Representation for the DOD Standard-Programmiersprache”, 1979
  35. ^ William A. Whitaker, “Ada – the project: the DoD High Order Working Group”, ACM SIGPLAN Notices (Band 28, Nr. 3, März 1991)
  36. ^ CECOM Center for Software Engineering Advanced Software Technology, “Final Report – Evaluation of the ACEC Benchmark Suite for Real-Time Applications”, AD-A231 968, 1990
  37. ^ P. Biggar, E. de Vries, D. Gregg, “A Practical Solution for Scripting Language Compilers”, Einreichung bei Science of Computer Programming, 2009
  38. ^ M. Hall, D. Padua, K. Pingali, “Compiler Research: The Next 50 Years”, ACM Communications 2009 Vol 54 #2
  39. ^ Cooper und Torczon 2012, p. 8
  40. ^ Lattner, Chris (2017). “LLVM”. In Braun, Amy; Wilson, Gregor (Hrsg.). Die Architektur von Open-Source-Anwendungen. Archiviert vom Original vom 2. Dezember 2016. Abgerufen 28. Februar 2017.
  41. ^ Aho, Lam, Sethi, Ullman 2007, p. 5-6, 109-189
  42. ^ Aho, Lam, Sethi, Ullman 2007, p. 111
  43. ^ Aho, Lam, Sethi, Ullman 2007, p. 8, 191-300
  44. ^ ein b Blindell, Gabriel Hjort (3. Juni 2016). Instruktionsauswahl: Prinzipien, Methoden und Anwendungen. Schweiz. ISBN 9783319340197. OCLC 951745657.
  45. ^ Cooper und Toczon (2012), p. 540
  46. ^ Aycock, John (2003). „Eine kurze Geschichte von Just-in-Time“. ACM-Computer. Überleben. 35 (2. Juni): 93–113. mach:10.1145/857076.857077. S2CID 15345671.[non-primary source needed]
  47. ^ Swartz, Jordan S.; Betz, Vaugh; Rose, Jonathan (22.–25. Februar 1998). “Ein schneller Routing-gesteuerter Router für FPGAs” (PDF). FPGA ’98 Proceedings of the 1998 ACM/SIGDA Sixth International Symposium on Field Programmable Gate Arrays. Monterey, Kalifornien: ACM: 140–149. mach:10.1145/275107.275134. ISBN 978-0897919784. S2CID 7128364. Archiviert (PDF) vom Original vom 9. August 2017.
  48. ^ Xilinx-Mitarbeiter (2009). “XST-Synthese-Übersicht”. Xilinx, Inc. Archiviert aus dem Original vom 2. November 2016. Abgerufen 28. Februar 2017.[non-primary source needed]
  49. ^ Altera-Mitarbeiter (2017). “Spectra-Q™-Motor”. Altera.com. Archiviert von das Original am 10. Oktober 2016. Abgerufen 28. Februar 2017.[non-primary source needed]
  50. ^ “Tutorial zum Sprachübersetzer” (PDF). Washington-Universität.

Weiterführende Literatur[edit]

Externe Links[edit]