Warum produzieren Compiler Assembler-Code?

19

Die Assemblersprache wird vom Assembler in die Maschinensprache konvertiert. Warum sollte ein Compiler eine Hochsprache in eine Assembly konvertieren? Kann es nicht direkt von der Hochsprache in Maschinencode konvertiert werden?

CODERSAM
quelle

Antworten:

22

Ein weiterer Grund für Compiler, Assemblys anstatt des richtigen Maschinencodes zu erstellen, sind:

  • Die symbolischen Adressen, die von Assemblern anstelle von fest codierten Maschinenadressen verwendet werden, erleichtern die Code-Verlagerung erheblich.
  • Das Verknüpfen von Code kann Sicherheitsüberprüfungen wie die Typprüfung umfassen. Dies ist einfacher mit symbolischen Namen zu tun.
  • Kleine Änderungen im Maschinencode sind leichter zu berücksichtigen, wenn der Assembler und nicht der Codegenerator geändert werden.
Martin Berger
quelle
Warum ist die Assemblersprache so effizient, obwohl sie auch in Englisch geschrieben ist und wie der Prozessor sie versteht?
CODERSAM
3
@CODERSAM Assembly ist eine formale Sprache, keine natürliche Sprache. Es ist sehr nah an der Maschinensprache. Die Übersetzung führt also nicht zu Ineffizienzen.
Martin Berger
Was bedeutet es, wenn Sie "der Maschinensprache sehr nahe" sagen? Ich bin wirklich verwirrt damit!
CODERSAM
2
@CODERSAM Die genaue Bedeutung ist kompliziert, aber so etwas wie Homomorphismus in der Algebra. Wenn Sie übersetzen, sagen Sie "add eax, # 2", was x86-Assembly ist, können Sie es sofort in d7f5 (oder was auch immer der Op-Code sein mag) übersetzen, ohne den Kontext zu betrachten, ohne weitere Dinge hinzuzufügen. Assembly hat keine Abstraktion.
Martin Berger
1
"Assembly hat keine Abstraktion" - Ich würde sagen, Labelnamen sind bereits eine Abstraktion (von Offsets). Auch der Kontext spielt eine Rolle: zB add eax,2kann nach 83 c0 02oder nach übersetzt werden 66 83 c0 02, je nach zuletzt aufgetretener Direktive wie use16.
Ruslan
15

Ein Compiler konvertiert normalerweise Code auf hoher Ebene direkt in Maschinensprache, kann jedoch modular aufgebaut werden, sodass ein Back-End Maschinencode und der andere Assemblycode (wie GCC) ausgibt. Die Codegenerierungsphase erzeugt "Code", der eine interne Darstellung des Maschinencodes ist, der dann in ein verwendbares Format wie Maschinensprache oder Assembler-Code konvertiert werden muss.

Yuval Filmus
quelle
Wenn die Quelle möglicherweise Assembly-Code enthält, muss außerdem ein Mechanismus verfügbar sein, mit dem diese Inline-Assembly ohnehin übersetzt werden kann.
Paul A. Clayton
Warum ist die Assemblersprache so effizient, obwohl sie auch in Englisch geschrieben ist und wie der Prozessor sie versteht?
CODERSAM
1
Assemblersprache ist eine "englische" Beschreibung des Maschinencodes.
Yuval Filmus
11

In der Vergangenheit haben einige namhafte Compiler Maschinencode direkt ausgegeben. Es gibt jedoch einige Schwierigkeiten dabei. Im Allgemeinen ist es für jemanden, der versucht zu bestätigen, dass ein Compiler ordnungsgemäß funktioniert, einfacher, die Ausgabe des Assemblycodes zu überprüfen als den Maschinencode. Außerdem ist es möglich (und historisch üblich), einen C- oder Pascal-Compiler mit einem Durchgang zu verwenden, um eine Assembler-Datei zu erstellen, die dann mit einem Assembler mit zwei Durchgängen verarbeitet werden kann. Das direkte Generieren von Code erfordert entweder die Verwendung eines C- oder Pascal-Compilers mit zwei Durchläufen oder die Verwendung eines Compilers mit einem Durchlauf, gefolgt von einer Methode zum Zurück-Patchen von Vorwärtssprungadressen [wenn eine Laufzeitumgebung die Größe eines gestarteten Programms in einem bereitstellt fester platz, Ein Compiler könnte eine Liste von Patches am Ende des Codes schreiben und den Startcode veranlassen, diese Patches zur Laufzeit anzuwenden. Ein solcher Ansatz würde die ausführbare Größe um etwa vier Bytes pro Patch-Punkt erhöhen, aber die Geschwindigkeit der Programmerstellung verbessern.

Wenn der Compiler schnell ausgeführt werden soll, kann die direkte Codegenerierung gut funktionieren. Bei den meisten Projekten sind die Kosten für das Generieren und Assemblieren des Assembler-Codes heutzutage jedoch kein großes Problem. Die Tatsache, dass Compiler Code in einer Form produzieren, die gut mit Code anderer Compiler interagieren kann, ist im Allgemeinen ein hinreichender Vorteil, um die Erhöhung der Kompilierungszeiten zu rechtfertigen.

Superkatze
quelle
1

Sogar Plattformen, die denselben Befehlssatz verwenden, können unterschiedliche verschiebbare Objektdateiformate aufweisen. Ich kann an "a.out" (frühes UNIX), OMF, MZ (MS-DOS EXE), NE (16-Bit Windows), COFF (UNIX System V), Mach-O (OS X und iOS) und denken ELF (Linux und andere) sowie Varianten davon wie XCOFF (AIX), ECOFF (SGI) und COFF-basierte Portable Executable (PE) unter 32-Bit-Windows. Ein Compiler, der Assemblersprache erzeugt, muss nicht viel über Objektdateiformate wissen, sodass Assembler und Linker dieses Wissen in einem separaten Prozess zusammenfassen können.

Siehe auch Unterschied zwischen OMF und COFF bei Stapelüberlauf.

Damian Yerrick
quelle
1

Normalerweise arbeiten Compiler intern mit Folgen von Anweisungen. Jeder Befehl wird durch eine Datenstruktur dargestellt, die den Operationsnamen, die Operanden usw. darstellt. Wenn die Operanden Adressen sind, sind diese Adressen normalerweise symbolische Referenzen, keine konkreten Werte.

Die Ausgabe von Assembler ist relativ einfach. Es geht so ziemlich darum, die interne Datenstruktur des Compilers in eine Textdatei in einem bestimmten Format zu kopieren. Die Assembler-Ausgabe ist auch relativ einfach zu lesen, was nützlich ist, wenn Sie überprüfen müssen, was der Compiler tut.

Die Ausgabe von Binärobjektdateien ist erheblich aufwändiger. Der Compiler-Schreiber muss wissen, wie alle Anweisungen codiert sind (was auf manchen CPUS-Systemen alles andere als trivial sein kann). Er muss einige symbolische Verweise auf relative Programmzähleradressen und andere in eine Form von Metadaten in der Binärobjektdatei konvertieren . Sie müssen alles in einem Format ausschreiben, das sehr systemspezifisch ist.

Ja, Sie können absolut einen Compiler erstellen, der binäre Objekte direkt ausgibt, ohne den Assembler als Zwischenschritt ausschreiben zu müssen. Die Frage bei der Softwareentwicklung ist, ob die Reduzierung der Kompilierungszeit die zusätzliche Entwicklungs- und Wartungsarbeit wert ist.

Der mir vertraute Compiler (freepascal) kann Assembler auf allen Plattformen ausgeben, aber nur binäre Objekte direkt auf einer Teilmenge von Plattformen ausgeben.

Peter Green
quelle
1

Ein Compiler sollte in der Lage sein, zusätzlich zu dem normalen verschiebbaren Code eine Assembler-Ausgabe zu erzeugen, was dem Programmierer zugute kommt.

Einmal habe ich den Fehler in einem C-Programm, das unter Unix System V auf einem LSI-11-Computer ausgeführt wird, einfach nicht gefunden. Nichts schien zu funktionieren. Schließlich ließ ich verzweifelt den protable C-Compiler eine Assembler-Version seiner Übersetzung ausscheiden. Ich hatte endlich den Bug gefunden! Der Compiler hat mehr Register zugewiesen, als auf der Maschine vorhanden waren! (Der Compiler hat die Register R0 bis R8 auf einer Maschine mit nur den Registern R0 bis R7 zugewiesen.) Ich habe es geschafft, den Fehler im Compiler zu umgehen, und mein Programm hat funktioniert.

Ein weiterer Vorteil der Assembler-Ausgabe ist die Verwendung von "Standard" -Bibliotheken, die andere Parameterübergabeprotokolle verwenden. Spätere C-Compiler ermöglichen es mir, das Protokoll mit einem Parameter festzulegen ("Pascal" würde den Compiler veranlassen, die Parameter in der angegebenen Reihenfolge hinzuzufügen, im Gegensatz zum C-Standard zum Umkehren der Reihenfolge).

Ein weiterer Vorteil ist, dass der Programmierer sehen kann, was für eine entsetzliche Arbeit sein Compiler leistet. Eine einfache C-Anweisung benötigt ungefähr 44 Maschinenbefehle. Werte werden aus dem Speicher geladen und dann schnell verworfen. etc, etc, etc ...

Ich persönlich glaube, dass es wirklich dumm ist, einen Compiler anstelle eines verschiebbaren Objektmoduls zu haben. Während der Kompilierung Ihres Programms sammelt der Compiler viele Informationen über Ihr Programm. In der Regel werden alle diese Informationen in einer sogenannten Symboltabelle gespeichert. Nach dem Ausscheiden des Assembler-Codes wirft es alle diese Informationstabelle. Der Assembler untersucht dann den ausgeschiedenen Code und sammelt einige der Informationen, die der Compiler bereits hatte, erneut. Assembler wissen jedoch nichts über If-Anweisungen von For-Anweisungen oder While-Anweisungen. All diese Informationen fehlen also. Anschließend erstellt der Assembler das verschiebbare Objektmodul, das der Compiler nicht erstellt hat.

Warum???

Robert Pearson
quelle