Was bedeuten eckige klammern assembler

Der GNU C-Compiler für ARM RISC Prozessoren erlaubt, Assemblerbefehlscode in C Programmen einzubetten. Diese coole Funktion kann für manuelle Optimierung zeitkritischer Programmabschnitte benutzt werden oder um spezifische Prozessoranweisung zu verwenden, die in C nicht zur Verfügung stehen.

Es wird angenommen, dass Sie mit Schreiben von ARM Assembler-Programmen vertraut sind, denn dies ist keine Einführung in Assembler-Programmierung. Ebensowenig ist es als Einführung in die C Programmierung geeignet.

Das Dokument basiert auf GCC Version 4, sollte aber auch mit früheren Versionen verwendbar sein.

GCC asm Befehl

Lassen Sie uns mit einem einfachen Beispiel beginnen. Der folgende Befehl kann in Ihrem C Programm wie jeder andere Befehl verwendet werden.

/* Beispiel NOP */
asm("mov r0,r0");

Er kopiert den Inhalt von Register r0 in das Register r0. Das heißt, der Befehl tut nicht viel mehr als gar nichts. Er ist auch als NOP (No OPeration) Befehl bekannt und wird gewöhnlich für sehr kurze Delays verwendet.

Halt! Bevor Sie unmittelbar daran gehen, dieses Beispiel in Ihrem Programm zu verwenden, fahren Sie mit dem Lesen fort um zu erfahren, warum er möglicherweise nicht so wie erwartet funktionieren wird.

Mit Inline-Assembler können Sie die gleiche Assemblerbefehlgedächtnislehre verwenden, die Sie für Schreiben reinen ARMversammlungscode verwenden würden. Und Sie können mehr als eine Assemblerbefehl in eine einzelne Inline-asm Statement schreiben. Um sie lesbarer zu bilden, können Sie jede Anweisung auf eine unterschiedliche Linie setzen.

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);

Die spezielle Reihenfolge des Zeilenvorschubes und der Tabulatorzeichen hält die Versammlungsteilnehmerauflistung, nett zu schauen. Es kann eine Spitze zum ersten Mal scheinen, die ungerade ist, aber die ist die Weise, die, der Compiler seinen eigenen Versammlungsteilnehmercode bei der Zusammenstellung von c-Befehln verursacht.

Bis jetzt sind die Assemblerbefehlen die vielen selben, die sie in den reinen Assemblersprachenprogrammen erscheinen würden. Jedoch werden Register und Konstanten in einer unterschiedlichen Art spezifiziert, wenn sie auf c-Ausdrücke sich beziehen. Die allgemeine Form einer Inline-Assemblersteilnehmeraussage ist

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);

Der Anschluss zwischen Assemblersprache und c-Rechengrößen wird von einem wahlweise freigestellten zweiten und dritten Teil der asm-Befehl, der Liste des Ausganges und der Eingangsrechengrößen zur Verfügung gestellt. Wir erklären das dritte wahlweise freigestellte Teil, die Liste von Clobbers, später.

Das folgende Beispiel des Drehens der Spitzen führt c-Variablen zur Assemblersprache. Es nimmt den Wert von einer Ganzzahlvariable, dreht Recht die Spitzen durch eine und speichert die Ganzzahlvariable des Resultats sofort.

/* Beispiel rotierende Bits */
asm("mov %[result], %[value], ror #1" : [result] "=r" (y) : [value] "r" (x));

Jede asm-Befehl wird durch Doppelpunkte in bis vier Teile unterteilt:

  1. Die Assemblerbefehlen, definiert in einem einzelnen Schnurdruckfehler:
    "Bewegungen % [Resultat], % [Wert], ror #1"
  2. Eine wahlweise freigestellte Liste der Ausgangsrechengrößen, getrennt durch Kommas. Jede Eintragung besteht aus einem symbolischen Namen, der in den eckigen Klammern eingeschlossen wird, gefolgt von einer Begrenzungsschnur, gefolgt vom Wechselstrom-Ausdruck, der in Klammern eingeschlossen wird. Unser Beispiel benutzt gerade einen Eintrag:
    [Resultat] "=r" (Y)
  3. Ein Komma trennte Liste der Eingangsrechengrößen, die die gleiche Syntax wie die Liste der Ausgangsrechengrößen verwendet. Wieder ist dieses wahlweise freigestellter und unser Beispielgebrauch eine Rechengröße nur:
    [Wert] "r" (X)
  4. Wahlweise freigestellte Liste der verprügelten Register, ausgelassen in unserem Beispiel.

Wie in dem Anfangs-NOP Beispiel gezeigt können schleppende Teile der asm Statement ausgelassen werden, wenn unbenutzt. Die Inline-asm Statementn, die nur Assemblerbefehl enthalten, sind alias grundlegende Inline-Assembler, während die Befehln, die wahlweise freigestellte Teile enthalten, ausgedehnte Inline-Assembler genannt werden. Wenn ein unbenutztes Teil von einem gefolgt wird, das verwendet wird, muss es leer gelassen werden. Das folgende Beispiel stellt das Programmstatusregister der ARM-CPU ein. Es verwendet einen Eingang, aber keine Ausgangsrechengröße.

asm ("msr cpsr, % [ps]": : [ps] "r" (Status));

Sogar kann das Codeteil leer gelassen werden, obwohl eine leere Schnur angefordert wird. Die folgende Befehl verursacht einen speziellen Clobber, um den Compiler zu erklären, Inhalt dieses Gedächtnisses kann geändert haben. Wieder wird die Clobberliste später erklärt, wenn wir einen Blick zur Codeoptimierung nehmen.

asm (""::: "Gedächtnis");

Sie können Räume, Neuzeilen und sogar c-Anmerkungen zur Zunahmelesbarkeit einsetzen.

asm("mov    %[result], %[value], ror #1"

           : [result]"=r" (y) /* Resultat. */
           : [value]"r"   (x) /* Wert. */
           : /* Keine clobbers */
    );

Im Codeabschnitt werden Rechengrößen durch ein Prozentzeichen bezogen, das vom in Verbindung stehenden symbolischen Namen gefolgt wird, der in den eckigen Klammern eingeschlossen wird. Er bezieht sich die auf Eintragung in einer der Rechengrößenlisten, die den gleichen symbolischen Namen enthält. Vom drehenden Spitzenbeispiel:

% [Resultat] bezieht sich auf Ausgangsrechengröße, das C variables y und
% [Wert] bezieht sich die auf Eingangsrechengröße, das C variables X.

Namen der symbolischen Rechengröße benutzen einen unterschiedlichen Namensplatz. Die Mittel, das dort ist keine Relation zu jeder möglicher anderen Symboltabelle. Zu sie setzen einfach: Sie können einen Namen wählen, ohne mach's gut, ob der gleiche Name bereits in Ihrem c-Code existiert. Jedoch müssen einzigartige Symbole innerhalb jeder asm Statement verwendet werden.

Wenn Sie bereits zu einigen Funktionsinline-Versammlungsteilnehmeraussagen schauten, die von anderen Autoren geschrieben wurden, können Sie einen bedeutenden Unterschied beachtet haben. Tatsächlich stützt der GCC-Compiler symbolische Namen seit Version 3.1. Für frühere Freilassungen muss das drehende Spitzenbeispiel wie geschrieben werden

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
0

Rechengrößen werden durch ein Prozentzeichen bezogen, das von einer einzelnen Stelle gefolgt wird, in der %0 auf die ersten %1 zur zweiten Rechengröße sich und so weiter bezieht. Dieses Format wird noch durch die spätesten GCC-Freigaben gestützt, aber ziemlich fehleranfällig und schwierig beizubehalten. Nach der Einfügung einer neuen Ausgangsrechengröße, stellen Sie sich, das vor, das Sie viele Assemblerbefehlen geschrieben haben, in denen Rechengrößen manuell geumnumeriert werden müssen.

Wenn dieses ganzes Material noch ungerades wenig schaut, sorgen Sie sich nicht. Neben der geheimnisvollen Clobberliste haben Sie das starke Gefühl, dass noch etwas fehlt, recht? In der Tat sprachen wir nicht über die Begrenzungsschnüre in den Rechengrößenlisten. Ich möchte um Ihre Geduld bitten. Es gibt etwas, das zum Höhepunkt im Folgenden Kapitel wichtiger ist.

C-Codeoptimierung

Es gibt zwei mögliche Gründe, warum Sie Assemblersprache verwenden möchten. Zuerst, ist, dieses C ist begrenzt, wenn wir näeher an der Hardware erhalten. Z.B. gibt es keine c-Befehl für das Prozessorstatusregister direkt ändern. Der zweite Grund ist, - optimierten Code in hohem Grade zu verursachen. Kein Zweifel, der Codeoptimierer GNU-C erledigt eine gute Arbeit, aber die Resultate sind von handcrafted Versammlungsteilnehmercode weit entfernt.

Das Thema des Kapitels wird häufig übersehen: Wenn man Assemblersprachencode, indem man Inline-Assemblersteilnehmeraussagen verwendet, dieser Code wird auch verarbeitet durch den Codeoptimierer des c-Compilers hinzufügt. Lassen Sie uns das Teil einer Compilerliste überprüfen, die von unserem drehenden Spitzenbeispiel erzeugt worden sein kann:

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
1

Das Compiler vorgewählte Register r3 für Spitzenumdrehung. Sie könnte irgendein anderes Register oder zwei Register, eins vorgewählt haben für jede c-Variable. Sie kann den Wert möglicherweise nicht ausdrücklich laden oder das Resultat speichern. Ist hier eine andere Auflistung, erzeugt durch einen anderen Compiler, den Version mit unterschiedlichem Wahlen kompilieren:

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
2

Der Compiler wählte ein einzigartiges Register für jede Rechengröße, unter Verwendung des Wertes vor, der bereits in r4 und im Führen des Resultats zum folgenden Code in r2 cachiert wurde. Erhielten Sie die Abbildung?

Häufig wird es schlechter. Der Compiler kann sogar sich entscheiden, Ihren Versammlungsteilnehmercode nicht überhaupt einzuschließen. Diese Entscheidungen sind ein Teil der Optimierungsstrategie des Compilers und hängen vom Zusammenhang ab, in dem Ihre Assemblerbefehlen verwendet werden. Z.B. wenn Sie nie irgendwelche der Ausgangsrechengrößen im restlichen Teil des c-Programms verwenden, entfernt der Optimierer höchstwahrscheinlich Ihre Inline-Assemblersteilnehmeraussage. Das NOP Beispiel, das wir kann uns darstellten solch ein Anwärter zuerst, außerdem sein, weil zum Compiler dieses unbrauchbares obenliegendes ist und Ablauf des Programms verlangsamen.

Die Lösung ist, das löschbare Attribut der asm-Befehl hinzuzufügen, um den Kompilator anzuweisen, Ihren Versammlungsteilnehmercode von der Codeoptimierung auszuschließen. Erinnern Sie sich, das, das Sie gewarnt worden sind, das Anfangsbeispiel zu verwenden. Ist hier die korrigierte Version:

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
3

Aber es gibt mehr Mühe, die uns wartet. Ein hoch entwickelter Optimierer ordnet den Code neu. Das folgende c-Stückchen war nach einigen letzten Änderungen zurückgelassen worden:

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
4

Der Optimierer erkennt, das, welches die zwei Stufensprünge keine Auswirkung auf die bedingte Befehl haben. Außerdem weiß er, das, das einen Wert durch 2 erhöht, kostet nur eine ARM-Anweisung. So ordnet er den Code zu neu

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
5

und außer einer ARM-Anweisung. Infolgedessen: Es gibt keine Garantie, die, die der kompilierte Code die Reihenfolge der Befehln behält, die im Quellencode gegeben werden.

Dieses kann eine große Auswirkung auf Ihren Code haben, da wir jetzt demonstrieren. Der folgende Code beabsichtigt, c mit b, von zu multiplizieren, welches oder beide kann durch ein Unterbrechungsprogramm geändert werden. Sperrenunterbrechungen, bevor sie auf die Variablen zurückgreifen und re-enable sie aussieht danach wie eine gute Idee.

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
6

Leider kann der Optimierer sich entscheiden, die Vermehrung zuerst zu tun und beide Inline-Assemblerbefehlen dann durchzuführen oder umgekehrt. Dieses bildet unseren Versammlungscode unbrauchbar.

Wir können dieses mithilfe der Clobberliste lösen, die jetzt erklärt wird. Die Clobberliste vom Beispiel oben

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
7

informiert den Compiler, dass der Versammlungscode Register r12 ändert und die Bedingungsschlüsselmarkierungsfahnen aktualisiert. BTW. unter Verwendung eines fest verdrahteten Registers verhindert gewöhnlich beste Optimierungsresultate. Im Allgemeinen sollten Sie eine Variable führen und lassen den Kompilator das ausreichende Register wählen. Neben Registerbezeichnungen und cm für das Bedingungregister, ist Gedächtnis ein gültiges Schlüsselwort auch. Es erklärt dem Compiler, dass die Assemblerbefehl Speicherstellen ändern kann. Nach der Durchführung der Assemblerbefehlen dieses zwingt den Kompilator, alle cachierten Werte vorher zu speichern und sie neu zu laden. Nach der Durchführung einer asm Statement mit einem Gedächtnis Clobber und es muss die Reihenfolge behalten, weil der Inhalt aller Variablen unvorhersehbar ist.

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
8

Alle cachierten Werte ungültig zu erklären kann suboptimal sein. Wechselweise können Sie eine blinde Rechengröße addieren, um eine künstliche Abhängigkeit zu verursachen:

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
9

Dieser Code täuscht, variables b in der ersten asm Statement zu ändern vor und den Inhalt variables c in der zweiten zu benutzen. Dieses konserviert die Reihenfolge unserer drei Befehln, ohne andere cachierte Variablen ungültig zu erklären.

Es ist wesentlich, zu verstehen, wie der Optimierer Inline-Assemblersteilnehmeraussagen beeinflußt. Wenn etwas neblig bleibt, besser lesen Sie dieses Teil neu, vor das folgende Thema weitergehen.

Eingangs- und Ausgangsrechengrößen

Wir erlernten, dieser jeder Eingang und Ausgangsrechengröße werden beschrieben durch einen symbolischen Namen, der in der eckigen Klammer eingeschlossen wurde, gefolgt von einer Begrenzungsschnur, die der Reihe nach vom Wechselstrom-Ausdruck in Klammern gefolgt wird.

Was sind diese Begrenzungen und warum wir benötigen sie? Sie wissen vermutlich, dass jeder Assemblerbefehl nur spezifische Rechengrößenarten annimmt. Z.B. erwartet der Verzweigungsbefehl eine Zieladresse, um an zu springen. Jedoch ist nicht jede Speicheradresse gültig, weil das abschließende opcode ein 24 Bit annimmt, das nur versetzt wird. Im Gegenteil erwartet die Niederlassungs- und Austauschanweisung ein Register, das eine 32-Bitzieladresse enthält. In beiden Fällen überschritt die Rechengröße von C zum Inline-Assemblersteilnehmer kann der gleiche c-Funktionszeiger sein. So wenn er Konstanten, Zeiger oder Variablen zu den Inline-Assemblersaussagen führt, muss der Inline-Assemblersteilnehmer wissen, wie sie im Versammlungscode dargestellt werden sollten.

Für ARM-Prozessoren liefert GCC 4 die folgenden Begrenzungen.

BegrenzungVerbrauch im ARM-ZustandVerbrauch im DaumenzustandfGleitkomma registriert f0. f7Nicht vorhandenhNicht vorhandenRegistriert r8. .r15GSofortig Gleitkomma-KonstanteNicht vorhandenHSelben ein G, aber verneintNicht vorhandenISofortig Wert in datenverarbeitenden Anweisungen
z.B. ORR R0, R0, #operandKonstante in der Strecke 0. 255
z.B. SWI RechengrößeJBewegungskonstanten -4095. 4095
z.B. LDR R1, [PC, #operand]Konstante in der Strecke -255. -1
z.B. VORR0, R0, #operandKSelben wie I, aber umgewandeltSelben wie I, aber verschobenLSelben wie I, aber verneintKonstante in der Strecke -7. 7
z.B. VORR0, R1, #operandLSelben wie rRegistriert r0. .r7
z.B. DRÜCKEN Sie RechengrößeMKonstante in der Strecke 0. 32 oder eine Energie von 2
z.B. BEWEGUNGEN R2, R1, ROR #operandKonstante, die eine Mehrfachverbindungsstelle von 4 in der Strecke 0. ist. 1020
z.B. ADDIEREN Sie R0, SP, #operandmIrgendeine gültige SpeicheradresseNNicht vorhandenKonstante in der Strecke 0. 31
z.B. LSL R0, R1, #operandONicht vorhandenKonstante, die eine Mehrfachverbindungsstelle von 4 in der Strecke -508. ist. 508
z.B. ADDIEREN Sie SP, #operandrMehrzweckregister r0. r15
z.B. VORoperand1, operand2, operand3Nicht vorhandenwVektorgleitkomma registriert s0. s31Nicht vorhandenXIrgendeine Rechengröße

Begrenzungsbuchstaben können durch einen einzelnen Begrenzungsmodifizierfaktor vorangestellt werden. Begrenzungen ohne einen Modifizierfaktor spezifizieren Read-only-Rechengrößen. Modifizierfaktoren sind:

ModifizierfaktorSpezifiziert=Write-only Rechengröße, normalerweise verwendet für alle Ausgangsrechengrößen+Lese-Schreibrechengröße, muss als Ausgangsrechengröße verzeichnet werdenu.Ein Register, das für nur Ausgang benutzt werden sollte

Ausgangsrechengrößen müssen write-only sein und das c-Ausdruckresultat muss ein lvalue sein, also bedeutet es, dass die Rechengrößen auf der linken Seite von Anweisungen gültig sein müssen. Der c-Compiler ist in der Lage, dieses zu überprüfen.

Eingangsrechengrößen sind, Sie schätzten ihn, schreibgeschützt. Anmerkung, die der c-Compiler ist nicht in der Lage zu überprüfen, ob die Rechengrößen von der angemessenen Art für die Art des Betriebes verwendet in den Assemblerbefehlen sind. Die meisten Probleme werden während des späten Versammlungsstadiums ermittelt, das für seine sonderbaren Fehlermeldungen weithin bekannt ist. Selbst wenn es behauptet, ein internes Compilerproblem gefunden zu haben, das die Autoren sofort berichtet werden sollte, verbessern Sie Überprüfung Ihr Inline-Assemblersteilnehmercode zuerst.

Eine strenge Richtlinie ist: Schreiben Sie nie überhaupt zu einer Eingangsrechengröße. Aber was, wenn Sie die gleiche Rechengröße für Eingang und Ausgang benötigen? Der Begrenzungsmodifizierfaktor + tut den Trick wie in dem folgenden Beispiel gezeigt:

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
0

Dieses ist unserem drehenden Spitzenbeispiel ähnlich, das oben dargestellt wird. Es dreht den Inhalt des variablen Wertes rechts durch ein Bit. Gegenüber im vorhergehenden Beispiel wird das Resultat nicht in einer anderen Variable gespeichert. Stattdessen wird der ursprüngliche Inhalt der Eingangsvariable geändert.

Der Modifizierfaktor + kann möglicherweise nicht durch frühere Freilassungen des Compilers gestützt werden. Glücklicherweise bieten sie eine andere Lösung an, die noch mit der spätesten Compilerversion arbeitet. Für Eingangsoperatoren ist es möglich, eine einzelne Stelle in der Begrenzungsschnur zu benutzen. Unter Verwendung der Stelle erklärt n dem Kompilator, das gleiche Register wie für die n-th Rechengröße, beginnend mit null zu benutzen. Ist hier ein Beispiel:

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
1

Begrenzung "0" erklärt dem Kompilator, um das gleiche Eingangsregister zu benutzen, das für die erste Ausgangsrechengröße benutzt wird.

Merken Sie jedoch, dieses dieses nicht automatisch andeutet den Rückfall. Der Compiler kann die gleichen Register für Eingang und Ausgang wählen, selbst wenn nicht erklärt, so zu tun. Sie können an die erste Umwandlungsliste des drehenden Spitzenbeispiels mit zwei Variablen dich erinnern, in denen der Compiler das gleiche Register r3 für beide Variablen benutzte. Die asm Statement

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
2

erzeugte diesen Code:

asm(
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0\n\t"
"mov     r0, r0"
);
1

Dieses ist nicht ein Problem in den meisten Fällen, aber kann tödlich sein, wenn der Ausgangsoperator durch den Versammlungsteilnehmercode geändert wird, bevor der Eingangsoperator verwendet wird. In den Situationen, in denen Ihr Code von den verschiedenen Registern abhängt, die für Eingangs- und Ausgangsrechengrößen benutzt werden, müssen Sie u. Begrenzungsmodifizierfaktor Ihrer Ausgangsrechengröße hinzufügen. Der folgende Code zeigt dieses Problem.

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
4

Ein Wert wird von einer Tabelle gelesen und dann wird ein anderer Wert zu einer anderen Position in diese Tabelle geschrieben. Wenn der Compiler das gleiche Register für Eingang und Ausgang gewählt haben würde, dann würde der Ausgangswert auf der ersten Assemblerbefehl zerstört worden sein. Glücklicherweise u. weist Modifizierfaktor den Kompilator, an kein Register für den Ausgangswert vorzuwählen, der für irgendwelche der Eingangsrechengrößen verwendet wird.

Weitere Rezepte

Inline-Assemblersteilnehmer als Präprozessormakro

Um Ihre Assemblerspracheteile wiederzuverwenden, ist es nützlich sie als Makro zu definieren und sie in sich zu setzen Include-Dateien. Unter Verwendung solchen Include-Dateien kann Compilerwarnungen produzieren, wenn sie in den Modulen verwendet werden, die im strengen ANSI-Modus kompiliert werden. Um das zu vermeiden, können Sie __asm anstelle von asm und __volatile schreiben anstelle vom flüchtigen Stoff. Diese sind gleichwertiger angenommener Name. Ist hier ein Makro, das einen langen Wert von wenig umwandelt, das zu großem endian oder umgekehrt endian ist:

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
5

C-Stummelfunktionen

Makrodefinitionen umfassen den gleichen Versammlungsteilnehmercode, wann immer sie bezogen werden. Dieses kann möglicherweise nicht für größere Programme annehmbar sein. In diesem Fall können Sie Wechselstrom-Stummelfunktion definieren. Ist hier das Bytetauschenverfahren wieder, dieses mal, das als Wechselstrom-Funktion eingeführt wird.

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
6

Symbolische Namen von C Variablen ersetzen

Durch Rückstellung verwendet GCC die gleichen symbolischen Namen von Funktionen oder von Variablen in C und im Versammlungsteilnehmercode. Sie können einen anderen Namen für den Versammlungsteilnehmercode spezifizieren, indem Sie eine spezielle Form der asm-Befehl verwenden:

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
7

Diese Befehl weist den Kompilator an, den Taktgeber des symbolischen Namens eher als Wert zu benutzen. Dieses ist sinnvoll nur für globale Variablen. Lokale Variablen (aka Selbstvariablen) haben nicht symbolische Namen im Versammlungsteilnehmercode.

Symbolische Namen von C Funktionen ersetzen

Um den Namen einer Funktion zu ändern, benötigen Sie eine Prototyperklärung, weil der Compiler nicht das asm-Schlüsselwort in der Funktionsdefinition annimmt:

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
8

Das Benennen der Funktion Calc () verursacht Assemblerbefehlen, die Funktion zu benennen BERECHNEN.

Zwingen des Verbrauches der spezifischen Register

Eine lokale Variable kann in einem Register gehalten werden. Sie können den Inline-Assemblersteilnehmer anweisen, ein spezifisches Register für es zu benutzen.

asm (Code: Ausgangsrechengrößenliste: Eingangsrechengrößenliste: Clobberliste);
9

Die Assemblerbefehl, "eor r3, r3, r3", löscht den variablen Kostenzähler. Seien gewarnt Sie, diese diese Probe ist schlecht in den meisten Situationen, weil sie den Optimierer des Compilers behindert. Außerdem hebt GCC nicht vollständig das spezifizierte Register auf. Wenn der Optimierer erkennt, dass die Variable nicht irgendwie länger bezogen wird, kann das Register wiederverwendet werden. Aber der Compiler ist nicht in der Lage, zu überprüfen, ob dieser Registerverbrauch mit irgendeinem vorbestimmten Register widerspricht. Wenn Sie zu viele Register auf diese Art aufheben, kann der Compiler aus Registern heraus während des Codeerzeugung sogar laufen.

Unter Verwendung der Register vorübergehend

Wenn Sie Register benutzen, die nicht als Rechengrößen geführt worden waren, müssen Sie den Compiler über dieses informieren. Der folgende Code justiert einen Wert auf eine Mehrfachverbindungsstelle von vier. Er verwendet r3 wie ein Kratzerregister und informiert den Kompilator über dieses, indem er r3 in der Clobberliste spezifiziert. Außerdem werden die CPU-Statusmarkierungsfahnen durch die ands Anweisung geändert und folglich war cm den Clobbers hinzugefügt worden.

/* Beispiel rotierende Bits */
asm("mov %[result], %[value], ror #1" : [result] "=r" (y) : [value] "r" (x));
0

Wieder ist harter Kodierungregisterverbrauch immer schlechte Kodierungart. Bessere Werkzeugwechselstrom-Stummelfunktion und verwenden eine lokale Variable für temporäre Werte.

Register-Verbrauch

Es ist immer eine gute Idee, den Umwandlungslisteausgang des c-Compilers zu analysieren und den erzeugten Code zu studieren. Die folgende Tabelle des typischen Registerverbrauches des Compilers ist vermutlich nützlich, den Code zu verstehen.

RegisterAlt. NameVerbrauchr0a1Erstes Funktionsargument
Zahlfunktionsresultat
Kratzerregisterr1a2Zweites Funktionsargument
Kratzerregisterr2a3Drittes Funktionsargument
Kratzerregisterr3a4Viertes Funktionsargument
Kratzerregisterr4v1Registervariabler5v2Registervariabler6v3Registervariabler7v4Registervariabler8v5Registervariabler9v6
rfpRegistervariable
Realer Rahmenzeigerr10SLStapelbegrenzungr11fpArgumentzeigerr12IPTemporärer Arbeitsplatzr13SPStapelzeigerr14LRVerbindungsregister
Arbeitsplatzr15PCBefehlszähler

Allgemeine Gefahren

Anweisungsfolge

Entwickler erwarten häufig, das eine Befehlsfolge Überreste im abschließenden Code, wie im Quellencode spezifiziert. Diese Annahme ist falsch und führt häufig stark ein, um Wanzen zu finden. Wirklich werden asm Statementn durch den Optimierer genauso wie andere c-Befehln verarbeitet. Sie können neu geordnet werden, wenn Abhängigkeiten dieses erlauben.

Das Kapitel "c-Codeoptimierung" bespricht die Details und bietet Lösungen an.

Durchführung im Daumenstatus

Seien Sie, das bewusst, abhängig von gegeben kompilieren Wahlen, der Compiler kann zum Daumenzustand schalten. Unter Verwendung des Inline-Assemblersteilnehmers mit Anweisungen, die nicht im Daumenzustand vorhanden sind, ergibt mysteriöses kompilieren Störungen.

Versammlungscodegröße

In den meisten Fällen stellt der Compiler richtig die Größe der Assemblerbefehl fest, aber er kann durch Versammlungsteilnehmermakro konfus werden. Besser vermeiden Sie sie.

Falls Sie konfus sind: Dieses ist über Assemblersprachenmakro, nicht c-Präprozessormakro. Es ist fein, die letzteren zu benutzen.

Aufkleber

Innerhalb der Assemblerbefehl können Sie Aufkleber als Sprungziele benutzen. Jedoch dürfen Sie nicht von einer Assemblerbefehl in andere springen. Der Optimierer weiß nichts über jene Niederlassungen und kann schlechten Code erzeugen.

Für eine vollständigere Diskussion über Inline-Assemblersverbrauch, sehen Sie das GCC-Benutzerhandbuch. Die späteste Version des GCC-Handbuches ist immer hier vorhanden:
http://gcc.gnu.org/onlinedocs/

Copyright

Wie Sie sicher wissen (oder auch nicht) ist jede originäre Arbeit durch ein Copyright geschützt, auch wenn dies nicht ausdrücklich erwähnt ist. Die Vorgängerversion dieses Dokuments wurde häufig kopiert und veröffentlicht, was im Interesse des Autors ist. bei einigen Kopien entsteht allerdings der Eindruck, der Herausgeber (nicht ich) sei der Autor (grrr...). Daher habe ich mich entschlossen, dieses Dokument unter den Schutz der GNU FDL zu stellen:

Was sind Assembler Befehle?

Eine Assemblersprache ist eine lesbare Art, Programme in Maschinen- sprache zu formulieren. Ein Assembler ist ein Programm, das Befehls- folgen von der Assemblersprache in die Maschinensprache übersetzt.

Wie funktioniert ein Assembler?

Ein Assembler übersetzt in Assemblersprache verfassten Code direkt in Binärcode, wobei der Code manuell oder maschinell erstellt sein kann. Manche Compiler wandeln Programmcode beispielsweise zunächst in Assemblercode um und rufen anschließend einen Assembler auf.

Was ist ein Quelloperand?

Der Quelloperand kann ein Register, eine Speicherstelle oder ein Wert sein, der Zieloperand kann ein Register oder eine Speicherstelle sein (wie immer dürfen nicht sowohl Ziel- wie auch Quelloperand eine Speicherstelle sein).

Was ist ein Register Assembler?

Ein Register ist ein winziges Stückchen Hardware innerhalb des Prozessors, das beim 80386 und höher bis zu 32 Bits, also 32 Ziffern im Bereich 0 und 1 speichern kann.