Куда это подталкивают?
esp - 4
. Точнее:
esp
вычитается на 4
- значение помещается в
esp
pop
меняет это.
ABI System V сообщает Linux rsp
указать разумное расположение стека при запуске программы: какое состояние регистра по умолчанию при запуске программы (asm, linux)? что вы обычно должны использовать.
Как вы можете протолкнуть регистр?
Минимальный пример 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.
*/
Вышеупомянутое на GitHub с запускаемыми утверждениями .
Зачем это нужно?
Это правда , что эти инструкции могут быть легко реализованы с помощью mov
, add
и sub
.
Причина их существования заключается в том, что эти комбинации инструкций настолько часты, что Intel решила предоставить их нам.
Причина, по которой эти комбинации так часты, заключается в том, что они позволяют легко сохранять и временно восстанавливать значения регистров в памяти, чтобы они не были перезаписаны.
Чтобы разобраться в проблеме, попробуйте вручную скомпилировать код C.
Основная трудность состоит в том, чтобы решить, где будет храниться каждая переменная.
В идеале все переменные должны помещаться в регистры, что является самой быстрой памятью для доступа (в настоящее время примерно в 100 раз быстрее, чем ОЗУ).
Но, конечно, мы можем легко иметь больше переменных, чем регистров, особенно для аргументов вложенных функций, поэтому единственное решение - запись в память.
Мы могли бы писать по любому адресу памяти, но поскольку локальные переменные и аргументы вызовов и возвращений функций вписываются в хороший шаблон стека, который предотвращает фрагментацию памяти , это лучший способ справиться с этим. Сравните это с безумием написания распределителя кучи.
Затем мы позволяем компиляторам оптимизировать распределение регистров для нас, поскольку это NP-полная и одна из самых сложных частей написания компилятора. Эта проблема называется распределением регистров и изоморфна раскраске графа .
Когда распределитель компилятора вынужден хранить вещи в памяти, а не только в регистрах, это называется разливом .
Сводится ли это к инструкции одного процессора или это более сложно?
Все , что мы знаем наверняка, что Intel документирует push
и в pop
инструкции, так что они одна команда в этом смысле.
Внутренне он может быть расширен до нескольких микрокодов, один для изменения, esp
а другой для ввода-вывода памяти и занимает несколько циклов.
Но также возможно, что сингл push
будет быстрее, чем эквивалентная комбинация других инструкций, поскольку он более конкретен.
В основном это недокументировано:
b
,w
,l
, или ,q
чтобы обозначить размер памяти манипулируют. Пример:pushl %eax
иpopl %eax