2017-10-17 4 views
3

Wir studieren den MIPS-Assembler (ich denke, dass diese Frage allgemein für die Assembly gelten kann), und der Lehrer stellte uns den Frame-Zeiger vor.Was sind die Vorteile eines Frame Pointers?

addiu $sp, $sp, -8 ; alloc 2 words in the stack 
sw $s0, 4($sp)  ; save caller function $s0 value in the stack 
sw $ra, ($sp)  ; save the return address for the callee function 

Und in der Funktion Epilog:

Wenn ich eine Funktion Prolog habe ich direkt den Stapelzeiger tun verwendet

move $v0, $0   ; set 0 as return value 
lw $s0, 4($sp)  ; pick up caller $s0 value from the stack 
lw $ra, ($sp)  ; pick up return address to return to the caller 
addiu $sp, $sp, 8 ; dealloc the stack words I used 
jr $ra    ; return back to caller 

Der Lehrer sagte, dass Rahmenzeiger verwendet, ist nützlich für uns Menschen, wenn wir Funktionen in der Montage schreiben:

addiu $sp, $sp, -12 ; alloc 3 words in the stack 
sw $fp, 8($sp)  ; save caller frame pointer in the stack 
addiu $fp, $sp, 8 ; set $fp to the uppermost address of the activation frame 
sw $ra, -4($fp)  ; saving like the first example, but relative 
sw $s0, -8($fp)  ; to the frame pointer 

Der Lehrer sagte auch, dass der Stapelzeiger manchmal anderen Speicherplatz zuweist und dass der Aktivierungsrahmen innerhalb der Funktion schwieriger ist, da wir darauf achten müssen. Mit Rahmenzeiger haben wir einen statischen Zeiger auf den Aktivierungsrahmen.

Ja, aber werde ich jemals müssen die Aktivierung innerhalb der Funktion verwenden, da es nur die gespeicherten Daten der Anruferfunktion enthält?

Ich denke, es macht nur Dinge schwieriger zu implementieren. Gibt es ein echtes praktisches Beispiel, bei dem der Rahmenzeiger für den Programmierer von großem Vorteil ist?

+0

für viele Befehlssätze müssen Sie nicht einen Frame-Zeiger verwenden, im besten Fall erleichtert es den Debuggern des Compilers Autoren, die nicht wirklich eine gültige Entschuldigung ist, scheint mir eine Verschwendung eines Registers. GCC, etc. können Sie wählen, nicht einen Rahmenzeiger zu verwenden, nicht Code zu erstellen, der es verwendet, um diesen Overhead zu speichern. einige Standardeinstellungen mit einigen Standardeinstellungen ohne. Es macht die Offsets innerhalb der für diese Funktion festgelegten Funktion, Variable X ist immer fp-N für diese Funktion. Sie können X sp + M für die ganze Funktion machen, oder wenn die Funktion geht, können Sie sp nach Bedarf verschieben, um Platz zu sparen. –

+0

Am Ende des Tages, was auch immer der Lehrer sagt, geht, für diese Klasse, die Klasse bestehen, zweifeln Sie alles und (wieder-) entdecken Sie es für sich selbst, um zu sehen, ob Sie es glauben oder nicht. War es nur etwas, um dem Lehrer die Benotung von Hausaufgaben zu erleichtern, oder wurde mir diese Programmierregel tatsächlich "besser" in gewisser Hinsicht gelehrt? Ich stimme zu, es macht es einfacher für Menschen, aber gleichzeitig Compiler oder Mensch, wenn Sie sp bewegen, um die gesamte Nutzung für die Funktion zu decken, ist es genauso einfach, ohne einen Rahmenzeiger. –

+1

Versuchen Sie Folgendes: Definieren Sie eine C-Struktur in Ihrem Kopf mit 5 oder mehr Feldern. Weisen Sie Platz dafür im Stapel zu. Initialisiere es. Übergeben Sie seine Mitglieder an andere Funktionen als Parameter auf dem Stapel. Mach es mit und ohne Rahmenzeiger. –

Antwort

2

Sie benötigen unbedingt einen Rahmenzeiger, wenn Sie auf dem Stapel variable Mengen an Speicherplatz dynamisch zuweisen. Funktionen, die Arrays variabler Länge und/oder alloca in C verwenden, sind Beispiele für Funktionen, die einen Frame-Pointer benötigen. Da der Mengenstapel, den die Funktion verwendet, eine Variable ist, können Sie keine konstanten Offsets vom Stapelzeiger verwenden, um auf Variablen zuzugreifen, und Sie müssen die Variablenlängenzuordnungen rückgängig machen, wenn die Funktion zurückkehrt. Die Verwendung eines Rahmenzeigers löst beide Probleme. Sie können ihn verwenden, um Stack-Variablen mit konstanten Offsets zu adressieren und den Stack-Zeiger auf den Wert zurückzusetzen, den er am Anfang der Funktion hatte.

Auf MIPS wäre es auch sinnvoll, einen Rahmenzeiger als Optimierung in einer Funktion zu verwenden, die nur Stapelzuweisungen mit fester Größe verwendet, wenn die gesamte Stapelzuordnung mehr als 32 KB beträgt. Die eingeschränkten Adressierungsmodi, die von MIPS unterstützt werden, ermöglichen nur einen 16-Bit-Vorzeichen-erweiterten Offset relativ zu dem Stapelzeiger oder einem anderen Register. Da der Stapelzeiger auf den Boden des Stapels zeigt, können nur nicht negative Offsets mit dem Stapelzeiger verwendet werden, und so können nur 32k auf dem Stapel in einer einzigen Anweisung adressiert werden. Durch Verwendung des Rahmenzeigers und durch Zeigen auf die Mitte des Stapelrahmens (anstelle des oberen Rands des Rahmens) kann er verwendet werden, um bis zu 64k des Stapels in einer einzigen Anweisung zu adressieren.

Andernfalls ist der Rahmenzeiger etwas, das nur dem Programmierer, nicht dem Programm, zugute kommt. Wenn alle Funktionen in Ihrem Programm einen Standardstapelrahmen mit einem Rahmenzeiger verwenden, bilden der Rahmenzeiger und alle gespeicherten Rahmenzeigerwerte, die im Stapel gespeichert sind, eine verkettete Liste von Stapelrahmen. Diese verkettete Liste kann leicht transversiert werden, um eine Rückverfolgung von Funktionsaufrufen beim Debuggen zu erzeugen. Mit einem geeigneten modernen Debugger ist es jedoch auch möglich, Metadaten (Abwicklungsinformationen) in ausführbare Dateien einzubetten, die der Debugger verwenden kann, um die Stapelrahmen zu durchlaufen, selbst wenn kein Rahmenzeiger verwendet wird. Dies können moderne Compiler automatisch tun, aber in der Assembler-Sprache kann es ziemlich mühsam sein, alle notwendigen Extra-Anweisungen zu integrieren, damit es funktioniert.Wenn der Stapelzeiger während einer Funktion mehrere Male durch Zuweisungen und Freigaben mit fester Größe wechseln kann, kann es mühsam sein, den Standort einer Variablen relativ zu dem Stapelzeiger an einem beliebigen Punkt im Programm zu verfolgen. Während es an einem bestimmten Ort immer einen festen Versatz hätte, würde sich dies je nach Standort ändern. Die Bestimmung dieses Offsets kann knifflig und fehleranfällig sein. Die Verwendung des a-Frame-Zeigers würde jeder Variablen einen Offset relativ zu dem Frame-Zeiger geben, der sich niemals ändert, da sich der Frame-Zeigerwert nicht ändert.

Beachten Sie, dass wenn Sie glauben, Sie müssen einen Rahmenzeiger verwenden, weil einer der letzten zwei Gründe Sie berücksichtigen müssen, warum Sie in Assembly in erster Linie programmieren. Dies sind Situationen, in denen ein moderner Compiler keinen Frame-Pointer verwenden müsste, um einen besseren Code zu erzeugen.

1

Frame Zeigerauslassung ist ein standard optimization option, dass C und C++ - Compiler implementieren. Der Vorteil ist, dass ein Register für andere Zwecke freigegeben werden kann. Es ist jedoch sehr oft deaktiviert, es macht das Debuggen eines Programmabsturzes übermäßig schwierig. Selbst grundlegende Dinge wie das Erzeugen eines Stack-Trace werden sehr schwierig. Sie wissen nicht mehr, wo sich der Rahmen der Elternfunktion befindet, also wissen Sie nicht, wo Sie nach der Absenderadresse suchen sollen. Das Überprüfen des Zustands lokaler Variablen wird ähnlich schmerzhaft. Dies ist nicht nur während des Debuggens der App wichtig, sondern es kommt zurück, wenn das Programm in der Produktion abstürzt.

Debug-Metadaten müssen wissen, wo Sie suchen müssen, Sie müssen die Größe des Rahmens kennen. Im Allgemeinen ist das Erstellen von Code debuggbar und diagnostizierbar mehr wichtig, als es einfach zu schreiben. Der typische Programmierer verbringt mehr Zeit mit dem Debuggen und Testen als mit Schreiben.

Verwandte Themen