Читая об ассемблере, я часто сталкиваюсь с людьми, которые пишут, что они нажимают определенный регистр процессора и вставляют его позже, чтобы восстановить его предыдущее состояние.
- Как вы можете протолкнуть регистр? Куда это подталкивают? Зачем это нужно?
- Сводится ли это к инструкции одного процессора или это более сложно?
assembly
x86
stack
terminology
Ars emble
источник
источник
b
,w
,l
, или ,q
чтобы обозначить размер памяти манипулируют. Пример:pushl %eax
иpopl %eax
%eax
он всегда имеет размер 32 бита.Ответы:
проталкивание значения (не обязательно сохраненного в регистре) означает запись его в стек.
выталкивание означает восстановление всего, что находится наверху стека, в регистр. Это основные инструкции:
источник
r/m
не просто регистрация, так что вы можетеpush dword [esi]
. Или дажеpop dword [esp]
загрузить, а затем сохранить то же значение обратно по тому же адресу. ( github.com/HJLebbink/asm-dude/wiki/POP ). Я упоминаю об этом только потому, что вы говорите «не обязательно регистр».pop
в области памяти:pop [0xdeadbeef]
Вот как вы нажимаете регистр. Я предполагаю, что речь идет о x86.
Он помещается в стек. Значение
ESP
регистра уменьшается до размера отправляемого значения по мере того, как стек растет вниз в системах x86.Это нужно для сохранения ценностей. Общее использование
A
push
- это отдельная инструкция в x86, которая внутренне выполняет две функции.ESP
регистр на размер введенного значения.ESP
регистра.источник
Куда это подталкивают?
esp - 4
. Точнее:esp
вычитается на 4esp
pop
меняет это.ABI System V сообщает Linux
rsp
указать разумное расположение стека при запуске программы: какое состояние регистра по умолчанию при запуске программы (asm, linux)? что вы обычно должны использовать.Как вы можете протолкнуть регистр?
Минимальный пример GNU GAS:
Вышеупомянутое на GitHub с запускаемыми утверждениями .
Зачем это нужно?
Это правда , что эти инструкции могут быть легко реализованы с помощью
mov
,add
иsub
.Причина их существования заключается в том, что эти комбинации инструкций настолько часты, что Intel решила предоставить их нам.
Причина, по которой эти комбинации так часты, заключается в том, что они позволяют легко сохранять и временно восстанавливать значения регистров в памяти, чтобы они не были перезаписаны.
Чтобы разобраться в проблеме, попробуйте вручную скомпилировать код C.
Основная трудность состоит в том, чтобы решить, где будет храниться каждая переменная.
В идеале все переменные должны помещаться в регистры, что является самой быстрой памятью для доступа (в настоящее время примерно в 100 раз быстрее, чем ОЗУ).
Но, конечно, мы можем легко иметь больше переменных, чем регистров, особенно для аргументов вложенных функций, поэтому единственное решение - запись в память.
Мы могли бы писать по любому адресу памяти, но поскольку локальные переменные и аргументы вызовов и возвращений функций вписываются в хороший шаблон стека, который предотвращает фрагментацию памяти , это лучший способ справиться с этим. Сравните это с безумием написания распределителя кучи.
Затем мы позволяем компиляторам оптимизировать распределение регистров для нас, поскольку это NP-полная и одна из самых сложных частей написания компилятора. Эта проблема называется распределением регистров и изоморфна раскраске графа .
Когда распределитель компилятора вынужден хранить вещи в памяти, а не только в регистрах, это называется разливом .
Сводится ли это к инструкции одного процессора или это более сложно?
Все , что мы знаем наверняка, что Intel документирует
push
и вpop
инструкции, так что они одна команда в этом смысле.Внутренне он может быть расширен до нескольких микрокодов, один для изменения,
esp
а другой для ввода-вывода памяти и занимает несколько циклов.Но также возможно, что сингл
push
будет быстрее, чем эквивалентная комбинация других инструкций, поскольку он более конкретен.В основном это недокументировано:
push
иpop
требуют одной микрооперации.источник
push
/pop
декодировать в упс. Благодаря счетчикам производительности возможно экспериментальное тестирование, и Агнер Фог это сделал и опубликовал таблицы с инструкциями . Процессоры Pentium-M и более поздних версий имеют единичный упорpush
/pop
благодаря механизму стека (см. Pdf-файл Microarch Agner). Сюда входят новейшие процессоры AMD, благодаря соглашению Intel / AMD о совместном использовании патентов.mov
загрузками). Для разлитых неконстантных переменных циклические обходы переадресации хранилища приводят к большой дополнительной задержке (дополнительные ~ 5 с по сравнению с пересылкой напрямую, а инструкции хранилища не из дешевых).ocperf.py
сценарий оболочки, чтобы получить простые символические имена для счетчиков.Выталкивание и извлечение регистров за кулисами эквивалентно следующему:
Обратите внимание, что это синтаксис At & t x86-64.
При использовании в паре это позволяет сохранить регистр в стеке и восстановить его позже. Есть и другие варианты использования.
источник
lea rsp, [rsp±8]
вместоadd
/,sub
чтобы лучше имитировать эффектpush
/pop
на флагах.Почти все процессоры используют стек. Программный стек представляет собой технологию LIFO с аппаратной поддержкой управления.
Стек - это объем программной (ОЗУ) памяти, обычно выделяемой в верхней части кучи памяти ЦП и увеличивающейся (при выполнении команды PUSH указатель стека уменьшается) в противоположном направлении. Стандартный термин для вставки в стек - PUSH, а для удаления из стека - POP .
Стек управляется через предназначенный для стека регистр ЦП, также называемый указателем стека, поэтому, когда ЦП выполняет POP или PUSH, указатель стека будет загружать / сохранять регистр или константу в память стека, и указатель стека будет автоматически уменьшаться x или увеличиваться в соответствии с количеством нажатых слов или вставлен в (из) стек.
С помощью инструкций ассемблера мы можем сохранить в стек:
источник