Frage

Es ist schon eine Weile her, seit ich das letzte Mal Arm Assembler codiert und ich bin ein wenig rostig auf die Details. Wenn ich eine C-Funktion von Arm nennen, ich muß nur Sorge über das Speichern r0-r3 und lr, nicht wahr?

Wenn die C-Funktion alle anderen Register verwendet, ist es verantwortlich für die auf dem Stapel speichern und sie wieder herstellt? Mit anderen Worten, würde der Compiler Code generiert dies für C-Funktionen zu tun.

Zum Beispiel, wenn ich r10 in einer Assembler-Funktion zu verwenden, ich muß nicht seinen Wert auf dem Stapel schieben, oder den Speicher und Pop / wiederherstellen nach einem C-Aufruf, kann ich tun?

Dies ist für Arm-EABI-gcc 4.3.0.

War es hilfreich?

Lösung

Es hängt von der ABI für die Plattform, die Sie für kompilieren. Unter Linux gibt es zwei ARM ABIs; die alte und die neue. AFAIK, die neue (EABI) ist in AAPCS der Tatsache ARM. Die vollständigen EABI Definitionen leben zur Zeit hier auf ARM Infothek .

der AAPCS, §5.1.1 :

  • r0-r3 sind die Argumente und Scratch-Register; r0-r1 sind auch die Ergebnisregister
  • r4-r8 sind Rufenen-save-Register
  • r9 kann ein Angerufener-Sicherungsregister oder nicht (bei einigen Varianten von AAPCS es ist ein spezielles Register)
  • r10-r11 sind Rufenen-save-Register
  • r12-r15 sind spezielle Register

Ein Rufenen-Sicherungsregister muss durch die Angerufenen (im Gegensatz zu einem Anrufer-Sicherungsregister, bei dem der Anrufer speichert das Register) gespeichert werden; so, , wenn das ist die ABI Sie verwenden, Sie müssen nicht r10 speichern, bevor eine andere Funktion aufrufen (die andere Funktion ist verantwortlich für das Speichern von es).

Edit: Welche Compiler Sie macht keinen Unterschied, verwenden; gcc kann insbesondere für verschiedene ABIs konfiguriert werden, und es kann sogar in der Befehlszeile geändert werden. Mit Blick auf den Prolog / Epilog-Code ist es erzeugt nicht so nützlich, da es für jede Funktion zugeschnitten ist und kann der Compiler verwenden, um andere Möglichkeiten des Sparens ein Register (zum Beispiel, ist es in der Mitte eine rettende Funktion).

Andere Tipps

Um fehlende Informationen über NEON Register aufaddieren:

der AAPCS , § 5.1.1 Kernregister:

  • r0-r3 sind die Argumente und Scratch-Register; r0-r1 sind auch die Ergebnisregister
  • r4-r8 sind Rufenen-save-Register
  • r9 kann ein Angerufener-Sicherungsregister oder nicht (bei einigen Varianten von AAPCS es ist ein spezielles Register)
  • r10-r11 sind Rufenen-save-Register
  • r12-r15 sind spezielle Register

Von der AAPCS, §5.1.2.1 VFP registrieren Nutzungskonventionen:

  • s16-s31 (d8-d15, q4-q7) muss erhalten bleiben
  • s0-s15 (d0-d7, q0-q3) und d16-d31 (Q8-Q15) müssen nicht beibehalten
  • werden

Original-Beitrag:
Arm-zu-c-calling-convention-Neon- Register-zu-speichern

Für 64-Bit-ARM, A64 (von Procedure Call-Standard für die ARM-64-Bit-Architektur)

Es ist einunddreißig, 64-bit, Allzweck- (integer) registriert sichtbar für den A64-Befehlssatz; Diese sind mit r0-r30 . In einem 64-Bit-Kontext werden diese Register normalerweise bezeichnet mit den Namen x0-x30 ; in einem 32-Bit-Kontext werden die Register unter Verwendung von angegeben w0-w30 . Zusätzlich wird ein Stapelzeigerregister, SP , kann mit einer begrenzten Anzahl von Befehlen verwendet werden.

  • SP Der Stapelzeiger
  • r30 LR Der Link-Register
  • r29 FP Der Rahmenzeiger
  • r19 ... r28 Callee-Register gespeichert
  • r18 Die Plattform registrieren, falls erforderlich; andernfalls wird ein temporäres Register.
  • r17 IP1 Die zweiten innere Prozedur-Aufruf temporäre Register (kann verwendet werden, durch Aufruf Furniere und PLT-Code); zu anderen Zeiten kann als verwendet werden Temporärregister.
  • r16 IP0 Das erste intra-Prozedur-Aufruf Scratch-Register (kann durch Aufruf verwendet werden Furniere und PLT-Code); zu anderen Zeiten kann als verwendet werden Temporärregister.
  • r9 ... r15 Temporäre Register
  • r8 Indirekte Ergebnis Register
  • r0 ... r7 Parameter / Ergebnisregister

Die ersten acht Register, r0-r7 , wird verwendet, um Argumentwerte in ein Unterprogramm übergeben und Ergebniswerte aus einer Funktion zurückzukehren. Sie können auch Zwischenwerte innerhalb einer Routine (aber in der Regel nur zwischen Subroutinenaufrufe) zu halten, verwendet werden.

Register R16 (IP0) und R17 (IP1) kann durch einen Linker als ein Scratch-Register zwischen einer Routine und jedem Unterprogramm ruft verwendet werden. Sie können auch in einer Routine verwendet werden, um Zwischenwerte zwischen Subroutinenaufrufe zu halten.

Die Rolle des Registers r18 Plattform spezifisch ist. Wenn eine ABI Plattform eines eigenen Mehrzweckregisters benötigen hat interVerfahrensZustand zu tragen (zB der Thread-Kontext), dann sollte es auf dieses Register zu diesem Zweck verwendet werden. Wenn die Plattform ABI keine solchen Anforderungen hat, dann sollte es r18 als zusätzliche temporäre Register verwenden. Die Plattform ABI-Spezifikation die Verwendung für dieses Register dokumentieren müssen.

SIMD

Die ARM 64-Bit-Architektur hat auch ein weiteres zweiunddreißig Register, v0-v31 , die von SIMD verwendet werden kann, und Floating-Point-Operationen. Der genaue Name des Registers ändert sich die Größe des Zugriffs angibt.

Hinweis: Im Gegensatz zu AArch32, in AArch64 die 128-Bit- und 64-Bit-Ansichten eines SIMD und Floating-Point-Register nicht überlappt mehrere Register in einer engeren Ansicht, so q1 , d1 und s1 beziehen sich alle auf den gleichen Eintrag in der Registerbank.

Die ersten acht Register, v0-v7 , wird verwendet, um Argumentwerte in ein Unterprogramm übergeben und Ergebniswerte aus einer Funktion zurückzukehren. Sie können auch Zwischenwerte verwendet werden, in einer Routine zu halten (aber in der Regel nur zwischen Subroutinenaufrufe).

Register v8-v15 muss von einem Angerufenen über Subroutinenaufrufe erhalten bleiben; die übrigen Register ( v0-v7, v16-v31 ) müssen nicht beibehalten werden (oder sollte vom Anrufer erhalten bleiben). Darüber hinaus sind nur die unteren 64-Bit jedes gespeicherten Wert in v8-v15 gewahrt werden müssen; es liegt in der Verantwortung des Anrufers größere Werte zu erhalten.

Die Antworten von CesarB und Pavel bereitgestellt Zitate aus AAPCS, aber offene Fragen bleiben. Hat der Angerufene speichern r9? Was ist r12? Was ist r14? Darüber hinaus waren die Antworten sehr allgemein und nicht spezifisch für die Arm-EABI Toolchain wie gewünscht. Hier ist ein praktischer Ansatz, um herauszufinden, welche Register sind Rufenen-gespeichert und welche nicht.

Der folgende C-Code enthält einen Inline Montageblock, die Register R0-R12 und R14 zu ändern, behauptet. Der Compiler den Code generieren, um die Register durch das ABI erforderlich speichern.

void foo() {
  asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14");
}

Verwenden Sie die Befehlszeile arm-eabi-gcc-4.7 -O2 -S -o - foo.c und fügen Sie den Schalter für Ihre Plattform (wie -mcpu=arm7tdmi zum Beispiel). Der Befehl wird die erzeugte Assembler-Code auf STDOUT aus. Es kann wie folgt aussehen:

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    bx  lr

Beachten Sie, dass der Compiler erzeugte Code speichert und stellt r4-r11. Der Compiler nicht speichern r0-r3, r12. Dass es r14 (alias lr) ist rein zufällig, wie ich aus eigener Erfahrung weiß, dass der Exit-Code kann auch lädt die gespeicherte lr in r0 und dann ein „bx r0“ anstelle von „bx lr“ wieder her. Entweder durch die -mcpu=arm7tdmi -mno-thumb-interwork Hinzufügen oder durch -mcpu=cortex-m4 -mthumb Verwendung erhalten wir etwas anderen Assembler-Code, der wie folgt aussieht:

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}

Auch hier r4-r11 werden gespeichert und wiederhergestellt. Aber r14 (alias lr) nicht gestellt.

Um es zusammenzufassen:

  • r0-r3 nicht Angerufenen-gespeichert
  • r4-r11 sind Rufenen-gespeichert
  • r12 (alias ip) ist nicht Angerufenen-gespeichert
  • r13 (alias sp) ist Rufenen-gespeichert
  • r14 (alias lr) ist nicht Angerufenen-gespeichert
  • r15 (alias pc) ist der Programmzähler und wird auf den Wert von lr vor dem Funktionsaufruf
  • gesetzt

Das gilt zumindest für die Arm-EABI-gcc-Standard ist. Es gibt Befehlszeilenschalter (insbesondere den -mabi Schalter), die die Ergebnisse beeinflussen kann.

Es gibt auch Unterschied zumindest auf Cortex M3-Architektur für Funktionsaufruf und unterbrechen.

Wenn ein Interrupt auftritt, wird es automatisch Push-R0-R3, R12, LR, PC auf Stapel und wenn Rückkehr Form IRQ automatischer POP machen. Wenn Sie andere Register in IRQ-Routine verwenden, müssen Sie Push / Pop sie auf Stapel manuell.

Ich glaube nicht, diese automatische PUSH und POP für einen Funktionsaufruf (Sprungbefehl) gemacht. Wenn Konvention sagt R0-R3 nur als Argument verwendet werden kann, führen oder Register kratzen, so gibt es keine Notwendigkeit, sie zu speichern, bevor Anruffunktion, weil es keinen Wert später nach Funktion Rückkehr verwendet werden soll. Aber gleich wie in einem Interrupt haben Sie alle anderen CPU-Register speichern, wenn Sie sie in Ihrer Funktion verwenden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top