Какова функция инструкций push / pop, используемых для регистров в сборке x86? - PullRequest
67 голосов
/ 03 января 2011

Читая об ассемблере, я часто сталкиваюсь с людьми, которые пишут, что они нажимают определенный регистр процессора и pop позже, чтобы восстановить его предыдущее состояние.

  • Как вы можете нажать регистр?Куда это толкнуло?Зачем это нужно?
  • Это сводится к одной инструкции процессора или является более сложным?

Ответы [ 5 ]

112 голосов
/ 03 января 2011

нажатие значение (не обязательно хранится в регистре) означает запись его в стек.

выталкивание означает восстановление всего, что находится на вершине стека в регистр.Это основные инструкции:

push 0xdeadbeef      ; push a value to the stack
pop eax              ; eax is now 0xdeadbeef

; swap contents of registers
push eax
mov eax, ebx
pop ebx
36 голосов
/ 03 января 2011

Вот как вы нажимаете регистр. Я предполагаю, что мы говорим о x86.

push ebx
push eax

Он помещается в стек. Значение регистра ESP уменьшается до размера отправляемого значения по мере увеличения стека в системах x86.

Необходимо сохранить значения. Общее использование

push eax           ;   preserve the value of eax
call some_method   ;   some method is called which will put return value in eax
mov  edx, eax      ;    move the return value to edx
pop  eax           ;    restore original eax

A push - это отдельная инструкция в x86, которая выполняет две внутренние функции.

  1. Сохранить переданное значение по текущему адресу регистра ESP.
  2. Уменьшить регистр ESP до размера передаваемого значения.
27 голосов

Куда это нажимается?

esp - 4.Точнее:

  • esp вычитается из 4
  • , значение увеличивается до esp

pop, и наоборот.

System V ABI сообщает 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 вручную.

Основная трудность - решить, где будет храниться каждая переменная.

В идеале все переменные должнывписывается в регистры, которые являются самой быстрой памятью для доступа (в настоящее время примерно на 100x быстрее , чем RAM).

Но, конечно, мы можем легко иметь больше переменных, чем регистров, особенно для аргументоввложенные функции, поэтому единственным решением является запись в память.

Мы могли бы записать любой адрес памяти, но, поскольку локальные переменные и аргументы вызовов и возвращений функций вписываются в хороший шаблон стека, который предотвращает фрагментация памяти , это лучший способ справиться с этим.Сравните это с безумием написания распределителя кучи.

Затем мы позволим компиляторам оптимизировать распределение регистров для нас, так как это завершает NP, и является одной из самых сложных частей написания компилятора.Эта проблема называется распределение регистров , и она изоморфна раскраске графов .

Когда распределитель компилятора вынужден хранить вещи в памяти вместо просто регистров, чтоизвестен как разлив .

Это сводится к одной инструкции процессора или это более сложный?

Все, что мы знаем навернякав том, что Intel документирует инструкции push и pop, поэтому они являются одной инструкцией в этом смысле.

Внутренне, она может быть расширена до нескольких микрокодов, один для изменения esp и один для выполненияпамяти ввода-вывода и занимают несколько циклов.

Но также возможно, что один push быстрее, чем эквивалентная комбинация других инструкций, поскольку он более конкретен.

Этов большинстве случаев документально подтверждено:

16 голосов
/ 06 сентября 2016

Регистры проталкивания и выталкивания находятся за кулисами, эквивалентными этому:

push reg   <= same as =>      sub  $8,%rsp        # subtract 8 from rsp
                              mov  reg,(%rsp)     # store, using rsp as the address

pop  reg    <= same as=>      mov  (%rsp),reg     # load, using rsp as the address
                              add  $8,%rsp        # add 8 to the rsp

Обратите внимание, что это x86-64 Синтаксис At & t.

Используется как пара, это позволяет сохранить регистрв стеке и восстановить его позже.Есть и другие применения.

11 голосов
/ 03 января 2011

Почти все процессоры используют стек.Стек программ - это метод LIFO с аппаратно поддерживаемым управлением.

Стек - это объем памяти программы (ОЗУ), обычно выделяемый в верхней части кучи памяти ЦП и растущий (по инструкции PUSH указатель стека)уменьшается) в противоположном направлении.Стандартный термин для вставки в стек - PUSH , а для удаления из стека - POP .

Управление стеком осуществляется с помощью предназначенного для стека регистра ЦП, также называемого указателем стека, поэтому при выполнении ЦП POP или PUSH указатель стека будет загружать / сохранять регистр или константув память стека, и указатель стека будет автоматически уменьшен на x или увеличен в соответствии с количеством слов, вставленных или вставленных в (из) стека.

С помощью инструкций ассемблера мы можем сохранить в стеке:

  1. Регистры ЦП, а также константы.
  2. Адрес возврата для функций или процедур
  3. Переменные функций / процедур ввода / вывода
  4. Функции / процедуры локальных переменных.
...