11 Stimmen

Wie funktioniert das Bootstrapping für gcc?

Ich war auf der Suche nach dem pypy-Projekt (Python in Python), und begann das Nachdenken über die Frage, was läuft die äußere Schicht von Python? Sicherlich, so vermutete ich, kann es nicht sein, wie das alte Sprichwort sagt, "Schildkröten den ganzen Weg nach unten"! Schließlich ist Python keine gültige x86-Assembly!

Bald erinnerte ich mich an das Konzept des Bootstrappings und sah nach, wie Compiler-Bootstrapping funktioniert. "Ok", dachte ich, "also kann es entweder in einer anderen Sprache geschrieben oder von Hand aus Assembler kompiliert werden". Ich bin mir sicher, dass C-Compiler im Interesse der Leistung einfach aus Assembler aufgebaut sind.

Das ist alles gut und schön, aber es bleibt die Frage, wie der Computer an die Baugruppendatei kommt!

Nehmen wir an, ich kaufe eine neue CPU, auf der nichts drauf ist. Bei der ersten Inbetriebnahme möchte ich ein Betriebssystem installieren, auf dem C läuft. Wo läuft der C-Compiler? Gibt es einen Miniatur-C-Compiler im BIOS?

Kann mir das jemand erklären?

12voto

Nehmen wir an, ich kaufe eine neue CPU, auf der nichts drauf ist. Bei der ersten Inbetriebnahme möchte ich ein Betriebssystem installieren, auf dem C läuft. Wie läuft der C-Compiler? Gibt es einen Miniatur-C-Compiler im BIOS?

Ich verstehe, was Sie fragen... was würde passieren, wenn wir keinen C-Compiler hätten und von vorne anfangen müssten?

Die Antwort ist, dass Sie mit der Montage oder der Hardware beginnen müssen. Das heißt, man kann einen Compiler entweder in Software oder in Hardware bauen. Wenn es keine Compiler auf der Welt gäbe, könnte man es heutzutage wahrscheinlich schneller mit Assembler machen; aber ich glaube, früher waren Compiler tatsächlich spezielle Hardware. Die Wikipedia-Artikel ist etwas kurz und bestätigt mich in dieser Hinsicht nicht, aber das macht nichts.

Die nächste Frage ist wohl: Was passiert heute? Nun, die Compilerautoren sind seit Jahren damit beschäftigt, portables C zu schreiben, also sollte der Compiler in der Lage sein, sich selbst zu kompilieren. Es lohnt sich, auf einer sehr hohen Ebene zu diskutieren, was Kompilierung ist. Im Grunde genommen nimmt man eine Reihe von Anweisungen und erzeugt daraus eine Baugruppe. Das war's. Nun, es ist eigentlich komplizierter als das - man kann alle möglichen Dinge mit Lexern und Parsern machen und ich verstehe nur einen kleinen Teil davon, aber im Wesentlichen geht es darum, C in Assembler abzubilden.

Im Normalfall erzeugt der Compiler Assemblercode, der zu Ihrer Plattform passt, aber das muss er nicht. Er kann Assemblercode für jede beliebige Plattform erzeugen, vorausgesetzt, er weiß, wie das geht. Der erste Schritt, um C auf Ihrer Plattform zum Laufen zu bringen, besteht also darin, ein Ziel in einem vorhandenen Compiler zu erstellen, Anweisungen hinzuzufügen und den Basiscode zum Laufen zu bringen.

Sobald dies geschehen ist, können Sie nun theoretisch Crosskompilierung von einer Plattform zur anderen. Die nächsten Schritte sind: Erstellung eines Kernels, eines Bootloaders und einiger grundlegender Dienstprogramme für die jeweilige Plattform.

Dann können Sie versuchen, den Compiler für diese Plattform zu kompilieren (sobald Sie ein funktionierendes Userland und alles, was Sie für den Build-Prozess benötigen, haben). Wenn das gelingt, haben Sie grundlegende Dienstprogramme, einen funktionierenden Kernel, Userland und ein Compilersystem. Sie sind jetzt auf einem guten Weg.

Beachten Sie, dass Sie im Zuge der Portierung des Compilers wahrscheinlich auch einen Assembler und Linker für diese Plattform schreiben mussten. Um die Beschreibung einfach zu halten, habe ich sie weggelassen.

Falls dies von Interesse ist, Linux von Grund auf neu ist eine interessante Lektüre. Es wird nicht erklärt, wie man ein neues Ziel von Grund auf erstellt (was deutlich nicht trivial ist) - es wird davon ausgegangen, dass man für ein bestehendes bekanntes Ziel baut, aber es wird gezeigt, wie man das Wesentliche kreuzkompiliert und mit dem Aufbau des Systems beginnt.

Python wird nicht wirklich zu einer Baugruppe zusammengesetzt. Zunächst einmal verfolgt das laufende Python-Programm die Anzahl der Verweise auf Objekte, etwas, das eine CPU nicht für Sie tun wird. Das Konzept des anweisungsbasierten Codes ist jedoch auch der Kern von Python. Probieren Sie das mal aus:

>>> def hello(x, y, z, q):
...     print "Hello, world"
...     q()
...     return x+y+z
... 
>>> import dis
dis.dis(hello)

  2           0 LOAD_CONST               1 ('Hello, world')
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  3           5 LOAD_FAST                3 (q)
              8 CALL_FUNCTION            0
             11 POP_TOP             

  4          12 LOAD_FAST                0 (x)
             15 LOAD_FAST                1 (y)
             18 BINARY_ADD          
             19 LOAD_FAST                2 (z)
             22 BINARY_ADD          
             23 RETURN_VALUE

Dort können Sie sehen, wie Python den von Ihnen eingegebenen Code auffasst. Dies ist Python-Bytecode, d.h. die Assemblersprache von Python. Sie hat sozusagen ihren eigenen "Befehlssatz" für die Implementierung der Sprache. Dies ist das Konzept einer virtuellen Maschine.

Java hat genau die gleiche Idee. Ich nahm eine Klassenfunktion und führte javap -c class um dies zu erhalten:

invalid.site.ningefingers.main:();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
public static void main(java.lang.String[]);
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iconst_0
   3:   istore_1
   4:   iload_1
   5:   aload_0
   6:   arraylength
   7:   if_icmpge   57
   10:  getstatic   #2; 
   13:  new #3; 
   16:  dup
   17:  invokespecial   #4; 
   20:  ldc #5; 
   22:  invokevirtual   #6; 
   25:  iload_1
   26:  invokevirtual   #7; 
   //.......
}

Ich nehme an, Sie haben es verstanden. Dies sind die Assemblersprachen der Python und Java Welten, d.h. wie der Python Interpreter und der Java Compiler jeweils denken.

Es lohnt sich auch, folgende Informationen zu lesen JonesForth . Dies ist sowohl ein funktionierender Interpreter als auch ein Tutorial und ich kann es nicht genug empfehlen, um darüber nachzudenken, "wie Dinge ausgeführt werden" und wie man eine einfache, leichte Sprache schreibt.

6voto

Im Interesse der Leistung bin ich sicher, dass C-Compiler einfach aus Assembler aufgebaut sind.

C-Compiler sind heutzutage (fast?) vollständig in C geschrieben (oder in höheren Sprachen - Clang ist zum Beispiel C++). Compiler gewinnen wenig bis gar nichts durch die Einbeziehung von handgeschriebenem Assembler-Code. Die Dinge, die die meiste Zeit in Anspruch nehmen, sind so langsam wie sie sind, weil sie sehr schwierige Probleme lösen, wobei "schwierig" "große Rechenkomplexität" bedeutet - das Umschreiben in Assembler bringt höchstens einen konstanten Geschwindigkeitszuwachs, aber das spielt auf dieser Ebene keine Rolle mehr.

Außerdem wollen die meisten Compiler eine hohe Portabilität, so dass architekturspezifische Tricks im Front- und Middle-End nicht in Frage kommen (und im Back-End sind sie auch nicht erwünscht, da sie die Cross-Compilation stören könnten).

Nehmen wir an, ich kaufe eine neue CPU, auf der nichts drauf ist. Bei der ersten Inbetriebnahme möchte ich ein Betriebssystem installieren, auf dem C läuft. Wo läuft der C-Compiler? Gibt es einen Miniatur-C-Compiler im BIOS?

Wenn Sie ein Betriebssystem installieren, wird (normalerweise) kein C-Compiler ausgeführt. Die Installations-CD ist voll mit fertig kompilierten Binärdateien für diese Architektur. Wenn ein C-Compiler enthalten ist (wie es bei vielen Linux-Distributionen der Fall ist), ist auch dieser bereits kompiliert. Und die Distributionen, die Sie dazu bringen, Ihren eigenen Kernel usw. zu bauen, haben auch mindestens eine ausführbare Datei enthalten - den Compiler. Das heißt, es sei denn, Sie müssen Ihren eigenen Kernel auf einer bestehenden Installation von irgendetwas mit einem C-Compiler kompilieren.

Wenn Sie mit "neuer CPU" eine neue Architektur meinen, die nicht abwärtskompatibel zu irgendetwas ist, das bereits unterstützt wird, können Self-Hosting-Compiler dem üblichen Portierungsverfahren folgen: Zuerst schreiben Sie ein Backend für das neue Ziel, dann kompilieren Sie selbst dafür, und plötzlich haben Sie einen ausgereiften Compiler mit einem kampferprobten (einen ganzen Compiler kompilierten) nativen Backend auf der neuen Plattform.

2voto

Matthew Slattery Punkte 43123

Wenn Sie einen neuen Rechner mit einem vorinstallierten Betriebssystem kaufen, muss dieser nicht einmal einen Compiler enthalten, da der gesamte ausführbare Code auf einem anderen Rechner kompiliert wurde, und zwar von demjenigen, der das Betriebssystem bereitstellt - Ihr Rechner muss nichts selbst kompilieren.

Wie kommt man zu diesem Punkt, wenn man eine völlig neue CPU-Architektur hat? In diesem Fall würden Sie wahrscheinlich damit beginnen, ein neues Codegenerierungs-Backend für Ihre neue CPU-Architektur (das "Ziel") für einen vorhandenen C-Compiler zu schreiben, der auf einer anderen Plattform (dem "Host") läuft - ein kompilerübergreifend .

Sobald Ihr Cross-Compiler (der auf dem Host läuft) gut genug funktioniert, um einen korrekten Compiler (und die erforderlichen Bibliotheken usw.) zu erzeugen, der auf dem Zielsystem läuft, können Sie den Compiler mit sich selbst auf der Zielplattform kompilieren und erhalten so einen zielspezifischen Compiler, der auf dem Zielsystem läuft und Code erzeugt, der auf dem Zielsystem läuft.

Bei einer neuen Sprache ist es das gleiche Prinzip: Sie müssen Code in einer bestehenden Sprache schreiben, für die Sie eine Toolchain haben, die Ihre neue Sprache in etwas kompiliert, mit dem Sie arbeiten können (nennen wir das den "Bootstrap-Compiler"). Sobald dies gut genug funktioniert, können Sie einen Compiler in Ihrer neuen Sprache schreiben (den "echten Compiler") und dann den echten Compiler mit dem Bootstrap-Compiler kompilieren. An diesem Punkt schreiben Sie den Compiler für Ihre neue Sprache in der neuen Sprache selbst, und man sagt, dass Ihre Sprache "selbsthostend" ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X