2016-11-05 1 views
0

Für ARM MontageArmmontage richtige Art und Weise POP Verbindungsregister und PC in einem Unterprogramm zu drücken und ein Unterprogramm in einem Unterprogramm-Aufruf

Ich habe in meinen Subroutinen wie folgt vorgehen:

SubRoutine: 
    PUSH {r1,r2,lr} 
    //code that changes r1 and r2 
    POP {r1,r2,lr} 
    bx lr 

dies ist der richtige Weg, um von einem Unterprogramm zurückzukehren und mit Code in Ihrer Hauptfunktion fortzufahren? Ich habe um zu sehen, dass die Menschen das folgende tun:

SubRoutine: 
    PUSH {r1,r2,lr} 
    //code that changes r1 and r2 
    POP {r1,r2,pc} 
    bx lr 

Aber ich weiß nicht, warum Sie den PC POP würde, wenn Sie den LR gedrückt. Welcher ist der richtige Weg und warum?

Auch, wenn Sie ein Unterprogramm in einem Unterprogramm aufrufen, tun Sie wie folgt vor:

SubRoutine: 
    PUSH {r1,r2,lr} 

    //code that changes r1 and r2 
    PUSH {lr} 
    bl AnotherRoutine (where bx lr will be used to return from it) 
    POP {lr} 

    POP {r1,r2,pc} 
    bx lr 

oder haben Sie es tun, wie folgt statt:

SubRoutine: 
    PUSH {r1,r2,lr} 

    //code that changes r1 and r2 
    PUSH {lr} 
    bl AnotherRoutine(where bx lr will be used to return from it) 
    POP {pc} 

    POP {r1,r2,pc} 
    bx lr 
+0

Wenn Sie sich die Mühe gemacht hätten, das Handbuch zu lesen, würden Sie wissen, dass 'pop' mit' pc' in der Liste ein Interworking-Zweig seit ARMv5T ist. Die 'bxlr' nach ihr wird nie erreicht. – EOF

+0

"Ich weiß nicht, warum du den PC POPSTEN würdest, wenn du den LR gedrängt hast." ---- hört sich so an, als ob dir hier etwas Grundlegendes fehlt. Sie drücken einen Wert __ aus dem LR__ und dann setzen Sie diesen Wert __in den PC__ (und führen Sie so die Verzweigung zu dieser Adresse). Sie müssen einige Grundlagen auf asm Programmierung lesen, es gibt viele Sachen im Netz ... –

+0

gut das ist, was ich über Klärung für – SeesSound

Antwort

2

gibt es drei Fälle, in denen Sie Sollte sich bewusst sein.

  1. Blatt: void foo(void) {};
  2. Endaufruf: int foo(void) { return bar(); };
  3. Intermediate: int foo(void) { int i; i = bar() + 4; return i; };

Es gibt viele Möglichkeiten, diese Anrufe zu implementieren. Im Folgenden sind einige Beispiele und sind nicht die einzige Möglichkeit, Epilog und Prolog in ARM-Assembler zu implementieren.


LEAF funtions

Viele Funktionen sind die Blatt Typ und erfordern keine Speicherung der lr. Sie verwenden einfach die bx lr, um zurückzukehren. Zum Beispiel

SubRoutine: 
    PUSH {r1,r2} 
    //code that changes r1 and r2 
    POP {r1,r2} 
    bx lr 

Auch ist es typisch, dass R1 und R2 verwendet Parameter zu übergeben und eine Unterroutine ist frei, sie zu nutzen/zerstören. ARM calling conventions Dies ist der Fall, wenn Sie die C-Funktion von Assembler aufrufen. So würde normalerweise niemand r1 und r2 speichern, aber da es Assembler ist, können Sie tun, was immer Sie wollen (auch wenn es eine schlechte Idee ist). Also das Beispiel ist eigentlich nur bx lr, wenn Sie dem Standard folgen.


Endaufruf

Wenn Ihre Funktion ein Blatt ist mit Ausnahme einer letzten Aufruf einer anderen Funktion Sie folgende Abkürzung verwenden können,

Sub_w_tail: 
// Save callee-saved regs (for whatever calling convention you need) 
// Leave LR as is. 
// ... do stuff 
B tail_call 

Die LR wird gespeichert durch den Anrufer zu Sub_w_tail und Sie springen einfach direkt zu tail_call, die an den ursprünglichen Anrufer zurückkehrt.


Intermediate Funktion

Dies ist der komplexeste. Hier ist eine mögliche Folge,

SubRoutine: 
    PUSH {r1,r2,lr} 

    //code that changes r1 and r2 
    bl AnotherRoutine (where bx lr will be used to return from it) 

    // more code 
    POP {r1,r2,pc} // returns to caller of 'SubRoutine' 

Einige Details einer älteren Aufrufkonvention sind in der ARM Link and frame registers Frage. Sie können diese Konvention verwenden. Es gibt viele verschiedene Möglichkeiten, um den Epilog und Prolog in ARM-Assembler durchzuführen.


Der letzte ist ziemlich komplex; oder zumindest mühsam zu programmieren. Es ist viel besser, einen Compiler bestimmen zu lassen, welche Register zu verwenden sind und was auf den Stack zu legen ist. In der Regel müssen Sie jedoch nur wissen, wie die erste (LEAF-Funktion) geschrieben wird, wenn Sie Assembler schreiben. Es ist am produktivsten, nur eine optimierte Unterroutine zu programmieren, die von einer höheren Programmiersprache in Assembler aufgerufen wird. Es ist nützlich zu wissen, wie alle von ihnen arbeiten, um kompilierten Code zu verstehen. Sie sollten auch Inline Assembler in Erwägung ziehen, damit Sie sich nicht mit diesen Nuancen befassen müssen.

+0

Die Tail-Call-Methode kann sehr nützlich sein, wenn Sie einen Assembler * Furnier * oder * shim * machen, der irgendeine Art von Buchhaltung vor dem Aufruf einer * echten * Routine tut. Zum Beispiel einige 'malloc' Debug-Code, etc. Allerdings, wenn Sie' void * malloc (Größe) kompilieren {return real_malloc (size); }; 'Sie können das Gleiche bekommen. Es könnte sinnvoller sein, wenn Shared Libraries oder andere Speicherbereiche aktiv sind. –

Verwandte Themen