2008-11-04 11 views
44

Es ist eine Weile her, seit ich letzten Arm Assembler und ich bin ein wenig eingerostet auf die Details. Wenn ich eine C-Funktion vom Arm aus anrufe, muss ich mich nur um das Speichern von r0-r3 und lr kümmern, oder? Wenn die C-Funktion andere Register verwendet, ist sie dafür verantwortlich, diese auf dem Stack zu speichern und wiederherzustellen? Mit anderen Worten, der Compiler würde Code erzeugen, um dies für C-Funktionen zu tun. Zum Beispiel, wenn ich r10 in einer Assembler-Funktion verwende, muss ich seinen Wert nicht auf den Stack oder in den Speicher schieben und ihn nach einem C-Aufruf wieder freigeben/wiederherstellen, oder?ARM zu C Aufruf Konvention, Register zu speichern

Dies ist für arm-eabi-gcc 4.3.0.

Ich weiß, ich konnte die ganze EABI lesen, aber dann ist RTFM Abkürzung für was ist SO, oder? :-)

+1

Hier ist ein externer Link, der hilfreich sein kann. [APCS Intro] (http://www.heyrick.co.uk/assembler/apcsintro.html), insbesondere einige [verschiedene Namen] (http://sourceware.org/ml/binutils/2000-06/msg00240.html) für 'Register' verwenden. –

Antwort

59

Es hängt von der ABI für die Plattform ab, für die Sie kompilieren. Unter Linux gibt es zwei ARM-ABIs. der alte und der neue. AFAIK, die neue (EABI) ist in der Tat ARM AAPCS. Die vollständigen EABI-Definitionen sind derzeit here on ARM's infocenter.

Von the AAPCS, §5.1.1:

  • r0-r3 sind die Argumente und Scratch-Register; r0-r1 sind auch das Ergebnis registriert
  • r4-r8 werden Register-Rufenen speichern
  • r9 könnte 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 Angerufener-Sichere-Register muss vom Angerufenen gespeichert werden (im Gegensatz zu einem Anrufer-Speicherregister, in dem der Anrufer das Register speichert); also Wenn Dies ist der ABI, den Sie verwenden, müssen Sie R10 nicht speichern, bevor Sie eine andere Funktion aufrufen (die andere Funktion ist für das Speichern verantwortlich).

Edit: Welcher Compiler Sie verwenden, macht keinen Unterschied; gcc kann insbesondere für mehrere verschiedene ABIs konfiguriert werden und kann sogar in der Befehlszeile geändert werden. Der erzeugte Prolog/Epilog-Code ist nicht so nützlich, da er für jede Funktion und zugeschnitten ist, kann der Compiler andere Möglichkeiten zum Speichern eines Registers verwenden (zum Beispiel das Speichern in der Mitte einer Funktion).

+0

Danke, das scheint ein paar Glocken zu läuten. Ich denke, das erste "r0-r4" in deiner Liste ist ein Tippfehler, oder? +1 (und wahrscheinlich beste Antwort, es sei denn, es gibt eine radikale Wende) – richq

+0

Ja, es war ein Tippfehler (und nicht der einzige, aber ich reparierte die anderen vor dem ersten Schlagen - oder so hoffe ich). – CesarB

+1

"Von dieser Seite können Sie die gesamte ABI-Spezifikation und ihre unterstützenden Dokumente und den Beispielcode als ZIP-Archiv herunterladen." Zip-Archiv: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0036b/bsabi.zip – jww

21

um fehlende Informationen zu NEON Register aufaddieren:

Von the AAPCS, §5.1.1 Kernregister:

  • r0-r3 sind die Argumente und Scratch-Register; r0-r1 sind auch das Ergebnis registriert
  • r4-r8 werden Register-Rufenen speichern
  • r9 könnte 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

Vom AAPCS, §5.1.2.1 VFP registrieren Nutzungskonventionen:

  • s16-s31 (d8-d15, q4-q7) muss
  • s0-s15 (d0-d7 bewahrt werden, q0-q3) und d16-d31 (Q8-Q15) brauchen nicht

Original-Beitrag erhalten werden:
arm-to-c-calling-convention-neon-registers-to-save

4

Die Antworten von Cesarb und Pavel lieferten Zitate von AAPCS, aber offene Fragen bleiben offen. Speichert der Angerufene R9? Was ist mit R12? Was ist mit R14? Darüber hinaus waren die Antworten sehr allgemein gehalten und nicht spezifisch für die Arm-eabi-Toolchain wie gewünscht. Hier ist ein praktischer Ansatz, um herauszufinden, welches Register aufgerufen wird und welches nicht.

Der folgende C-Code enthält einen Inline-Assembly-Block, der angibt, die Register r0-r12 und r14 zu ändern. Der Compiler generiert den Code zum Speichern der von der ABI benötigten Register.

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

Verwenden der Befehlszeile arm-eabi-gcc-4.7 -O2 -S -o - foo.c und die Schalter für die jeweilige Plattform (wie zum Beispiel -mcpu=arm7tdmi) hinzuzufügen. Der Befehl wird den generierten Assembly-Code auf STDOUT ausgeben. 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 speichert nicht r0-r3, r12. Dass es r14 (alias lr) wiederherstellt, ist rein zufällig, da ich aus Erfahrung weiß, dass der Exit-Code auch das gespeicherte lr in r0 laden kann und dann ein "bx r0" anstelle von "bx lr" macht. Entweder durch die -mcpu=arm7tdmi -mno-thumb-interwork Hinzufügen oder durch -mcpu=cortex-m4 -mthumb verwenden wir etwas anderen Assembler-Code zu erhalten, die 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} 

Wieder r4-r11 werden gespeichert und wiederhergestellt. Aber r14 (alias lr) wird nicht wiederhergestellt.

Fassen wir zusammen:

  • r0-r3 nicht Rufenen gespeicherten
  • r4-r11 sind Rufenen gespeicherte
  • r12 (alias ip) sind, ist nicht Rufenen gespeicherten
  • r13 (Alias ​​sp) wird auf Abruf gespeichert
  • r14 (alias lr) ist nicht callee-gespeichert
  • R15 (alias PC) wird der Programmzähler und wird auf den Wert von LR vor dem Funktionsaufruf eingestellt

Dies gilt zumindest für den Arm-EABI-GCCs Verzuges des. Es gibt Befehlszeilenschalter (insbesondere der Schalter -mabi), die die Ergebnisse beeinflussen können.

+1

Ihre Analyse ist * incorrect *; das 'lr' wird ** als' pc' für einen schnelleren Weg zurückgeholt **. Die Antwort auf Ihre 'r9' Frage ist in der [APCS] (http://www.cl.cam.ac.uk/~fms27/teaching/2001-02/arm-project/02-sort/apcs.txt) . Es heißt in diesem Dokument * statische Basis * und der Abschnitt * Reentrant vs Non-Reentrant Code * ist relativ. Das ** APCS ** unterstützt mehrere Konfigurationen, aber 'gcc' ist generell * reentry * ohne * stack limits *. Besonders, * Es gibt spezielle Rollen für 'sb/r9' und' sl/r10' in einigen Varianten des APCS. In anderen Varianten können sie als auf Abruf gespeicherte Register verwendet werden * –

+0

Siehe [ARM Link und Frame Pointer] (http://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer) für Details zu 'pc' und' lr'. 'r12' wird auch als 'ip' bezeichnet und kann während eines * Prolog * und * Epilog * verwendet werden. Es ist ein * flüchtiges * Register. Dies ist wichtig für Routinen, die den Call Stack/die Frames analysieren. –

+0

Inwiefern ist meine Analyse bezüglich 'lr' falsch? Ich glaube, du hast mich falsch verstanden. Jedenfalls präsentierte ich den zweiten Assembly Code Snippet, da der erste aussah, als wäre "lr" als Angerufener gespeichert worden. Aber ich denke, das ist es nicht. Ja, im zweiten Ausschnitt wird "lr" als "pc" angezeigt, um schneller zurückzukommen, und das habe ich nicht erklärt, aber der Punkt, an dem das zweite Snippet präsentiert wurde, war, dass "lr" nicht gespeichert wird. – Sven

13

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) auf dem sichtbaren Register A64 Befehlssatz; diese sind mit r0-r30 gekennzeichnet. In einem 64-Bit-Kontext werden diese Register normalerweise unter Verwendung der Namen x0-x30; In einem 32-Bit-Kontext werden die Register unter Verwendung von w0-w30 spezifiziert. Zusätzlich kann ein Stapelzeigerregister, SP, mit einer beschränkten Anzahl von Anweisungen verwendet werden.

  • SP Der Stapelzeiger
  • r30 LR Der Link-Register
  • r29 FP Der Rahmenzeiger
  • r19 ... r28 Callee gespeicherten Register
  • r18 Das Plattform-Register, falls erforderlich; sonst ein temporäres Register.
  • r17 IP1 Das zweite Intra-Prozedur-Aufruf temporäre Register (kann per Anruf Furniere und PLT-Code verwendet werden); zu anderen Zeiten kann als temporäres Register verwendet werden.
  • r16 IP0 Das erste Intra-Prozedur-Anruf-Scratch-Register (kann per Anruf Veneers und PLT-Code verwendet werden); zu anderen Zeiten kann als temporäres Register verwendet werden.
  • r9 ... r15 Temporäre Register
  • r8 Indirekte Folge Standortregister
  • r0 ... r7 Parameter/Ergebnis-Register

Die ersten acht Register, r0-r7 sind Wird verwendet, um Argumentwerte in eine Subroutine zu übergeben und Ergebniswerte von einer Funktion zurückzugeben. Sie können auch verwendet werden, um Zwischenwerte innerhalb einer Routine zu halten (aber im Allgemeinen nur zwischen Unterprogrammaufrufen).

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 innerhalb einer Routine verwendet werden, um Zwischenwerte zwischen Unterprogrammaufrufen zu halten.

Die Rolle des Registers r18 ist plattformspezifisch. Wenn eine Plattform ABI ein dediziertes Allzweckregister benötigt, um den interprozeduralen Zustand (zum Beispiel den Thread-Kontext) zu tragen, dann sollte sie dieses Register für diesen Zweck verwenden. Wenn die Plattform ABI keine solchen Anforderungen hat, sollte sie r18 als zusätzliches temporäres Register verwenden. Die Plattform-ABI-Spezifikation muss die Verwendung für dieses Register dokumentieren.

SIMD

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

Anmerkung: Anders als in AArch32, in AArch64 die 128-Bit und 64-Bit-Ansichten eines SIMD und Floating-Point-Register nicht mehrere Register in einer schmaleren Ansicht überlappen, so q1, d1 und s1 beziehen sich alle zum selben Eintrag in der Registerbank.

Die ersten acht Register, v0-v7, werden verwendet Argumentwerte in einem Unterprogramm zu übergeben und die Ergebniswerte aus einer Funktion zurückzukehren. Sie können auch verwendet werden, um Zwischenwerte innerhalb einer Routine zu halten (aber im Allgemeinen nur zwischen Unterprogrammaufrufen).

Register v8-v15 müssen von einem Aufrufer über Subroutinenaufrufe beibehalten werden; Die verbleibenden Register (v0-v7, v16-v31) müssen nicht beibehalten werden (oder vom Anrufer erhalten werden). Außerdem müssen nur die unteren 64 Bits jedes Wertes, die in v8-v15 gespeichert sind, erhalten werden; Es liegt in der Verantwortung des Anrufers, größere Werte zu bewahren.

0

Es gibt auch einen Unterschied zumindest bei Cortex M3 Architektur für Funktionsaufruf und Interrupt.

Wenn ein Interrupt auftritt, wird automatisch R0-R3, R12, LR, PC auf Stack und bei Rückkehr IRQ automatisch POP drücken. Wenn Sie andere Register in der IRQ-Routine verwenden, müssen Sie sie manuell auf Stack schieben.

Ich glaube nicht, dass diese automatische PUSH und POP für einen Funktionsaufruf (Sprunganweisung) gemacht wird.Wenn die Konvention sagt, dass R0-R3 nur als Argument, Ergebnis oder Scratch-Register verwendet werden kann, müssen sie nicht vor dem Funktionsaufruf gespeichert werden, da nach der Funktionsrückgabe kein Wert mehr verwendet werden sollte. Aber genauso wie bei einem Interrupt müssen Sie alle anderen CPU-Register speichern, wenn Sie sie in Ihrer Funktion verwenden.