Wo wird es weitergeschoben?
esp - 4
. Etwas präziser:
esp
wird von 4 abgezogen
- Der Wert wird auf verschoben
esp
pop
kehrt dies um.
Das System V ABI weist Linux an, rsp
auf einen sinnvollen Stapelspeicherort hinzuweisen, wenn das Programm gestartet wird: Wie lautet der Standardregisterstatus beim Programmstart (asm, linux)? Welches ist, was Sie normalerweise verwenden sollten.
Wie kann man ein Register schieben?
Beispiel für ein minimales GNU-GAS:
.data
/* .long takes 4 bytes each. */
val1:
/* Store bytes 0x 01 00 00 00 here. */
.long 1
val2:
/* 0x 02 00 00 00 */
.long 2
.text
/* Make esp point to the address of val2.
* Unusual, but totally possible. */
mov $val2, %esp
/* eax = 3 */
mov $3, %ea
push %eax
/*
Outcome:
- esp == val1
- val1 == 3
esp was changed to point to val1,
and then val1 was modified.
*/
pop %ebx
/*
Outcome:
- esp == &val2
- ebx == 3
Inverses push: ebx gets the value of val1 (first)
and then esp is increased back to point to val2.
*/
Das obige auf GitHub mit ausführbaren Assertions .
Warum wird das benötigt?
Es ist wahr, dass diese Anweisungen leicht über und implementiert werden mov
können .add
sub
Der Grund dafür ist, dass diese Kombinationen von Anweisungen so häufig sind, dass Intel beschlossen hat, sie für uns bereitzustellen.
Der Grund, warum diese Kombinationen so häufig sind, besteht darin, dass sie das Speichern und Wiederherstellen der Werte von Registern im Speicher erleichtern, damit sie nicht überschrieben werden.
Versuchen Sie, C-Code von Hand zu kompilieren, um das Problem zu verstehen.
Eine große Schwierigkeit besteht darin, zu entscheiden, wo jede Variable gespeichert wird.
Im Idealfall passen alle Variablen in Register, auf die am schnellsten zugegriffen werden kann (derzeit etwa 100-mal schneller als RAM).
Aber natürlich können wir leicht mehr Variablen als Register haben, insbesondere für die Argumente verschachtelter Funktionen. Die einzige Lösung besteht darin, in den Speicher zu schreiben.
Wir könnten in jede Speicheradresse schreiben, aber da die lokalen Variablen und Argumente von Funktionsaufrufen und -rückgaben in ein schönes Stapelmuster passen, wird eine Speicherfragmentierung verhindert , ist dies der beste Weg, damit umzugehen. Vergleichen Sie das mit dem Wahnsinn, einen Heap-Allokator zu schreiben.
Dann lassen wir Compiler die Registerzuordnung für uns optimieren, da dies NP vollständig ist und einer der schwierigsten Teile beim Schreiben eines Compilers ist. Dieses Problem wird als Registerzuordnung bezeichnet und ist isomorph zur Diagrammfärbung .
Wenn der Allokator des Compilers gezwungen ist, Dinge im Speicher zu speichern, anstatt nur Register, wird dies als Überlauf bezeichnet .
Kommt es auf einen einzelnen Prozessorbefehl an oder ist es komplexer?
Wir wissen nur, dass Intel a push
und a dokumentiertpop
Anweisung , also sind sie eine Anweisung in diesem Sinne.
Intern könnte es auf mehrere Mikrocodes erweitert werden, einen zum Ändern esp
und einen zum Ausführen der Speicher-E / A, und mehrere Zyklen dauern.
Es ist aber auch möglich, dass eine einzelne push
schneller ist als eine äquivalente Kombination anderer Anweisungen, da sie spezifischer ist.
Dies ist meist un (der) dokumentiert:
b
,w
,l
oderq
die Größe des Speichers manipuliert zu bezeichnen. Beispiel:pushl %eax
undpopl %eax