Яка функція push / pop інструкцій, що використовуються в регістрах у збірці x86?


100

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

  • Як можна натиснути реєстр? Куди його штовхають? Навіщо це потрібно?
  • Це зводиться до однієї інструкції процесора чи вона більш складна?

3
Попередження: усі поточні відповіді наводяться у синтаксисі збірки Intel; поштовх популярності в AT & T синтаксис, наприклад , використовує пост-FIX , як b, w, l, або , qщоб позначити розмір пам'яті маніпулюють. Приклад: pushl %eaxіpopl %eax
Хокен

5
@hawken У більшості асемблерів, здатних проковтнути синтаксис AT&T (зокрема, газ), розмір постфікса може бути опущений, якщо розмір операнда можна визначити з розміру операнда. Це стосується наведених вами прикладів, як %eaxзавжди 32-бітний розмір.
Гюнтер Пієз,

Відповіді:


153

натискання значення (не обов'язково зберігається в реєстрі) означає запис його в стек.

вискакування означає відновлення того, що знаходиться поверх стека, в реєстрі. Це основні вказівки:

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

5
Явний операнд для push і pop - це r/mне просто зареєструватися, щоб ви могли push dword [esi]. Або навіть pop dword [esp]завантажити, а потім зберегти те саме значення назад за тією ж адресою. ( github.com/HJLebbink/asm-dude/wiki/POP ). Я згадую це лише тому, що ви говорите "не обов'язково реєстр".
Пітер Кордес,

2
Ви також можете popпотрапити в область пам’яті:pop [0xdeadbeef]
SS Anne

Привіт, яка різниця між push / pop та pushq / popq? Я на macos / intel
SteakOverflow

45

Ось як ви натискаєте реєстр. Я припускаю, що мова йде про 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реєстру.

@vavan щойно надіслав запит на його виправлення
jgh fun-run

1
@vavan: Я зробив редагування.
Nate Eldredge

38

Куди його штовхають?

esp - 4. Точніше:

  • esp віднімається від 4
  • значення підштовхується до esp

pop змінює це.

Система 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 вирішила надати їх нам.

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

Щоб зрозуміти проблему, спробуйте скласти трохи коду С вручну.

Основною складністю є рішення, де буде зберігатися кожна змінна.

В ідеалі всі змінні вписувалися б у регістри, що є найшвидшим доступом до пам'яті (в даний час приблизно в 100 разів швидше, ніж оперативної пам'яті).

Але, звичайно, ми можемо мати більше змінних, ніж регістри, спеціально для аргументів вкладених функцій, тому єдиним рішенням є запис в пам’ять.

Ми могли б писати на будь-яку адресу пам’яті, але оскільки локальні змінні та аргументи викликів та повернень функції вписуються в гарний шаблон стека, який запобігає фрагментації пам’яті , це найкращий спосіб вирішити це. Порівняйте це з божевільністю написання розподілювача купи.

Потім ми дозволяємо компіляторам оптимізувати розподіл регістрів для нас, оскільки це NP завершено і є однією з найскладніших частин написання компілятора. Ця проблема називається розподілом регістрів , і вона ізоморфна розфарбуванню графіків .

Коли розподільник компілятора змушений зберігати речі в пам'яті, а не просто регістри, це відоме як розлив .

Це зводиться до однієї інструкції процесора чи вона більш складна?

Все, що ми точно знаємо, це те, що Intel документує a pushта popінструкцію, тому вони є однією інструкцією в цьому сенсі.

Внутрішньо він може бути розширений до декількох мікрокодів, один для модифікації, espа другий для введення-виведення в пам'ять, і триває кілька циклів.

Але також можливо, що сингл pushшвидший, ніж еквівалентна комбінація інших інструкцій, оскільки він є більш конкретним.

Це переважно не (дер) задокументовано:


4
Вам не потрібно здогадуватися про те, як push/ popдекодувати в uops. Завдяки лічильникам продуктивності можливо експериментальне тестування, і Агнер Фог зробив це і опублікував таблиці інструкцій . Процесори Pentium-M та пізніші мають одинарний uop push/ popзавдяки механізму стека (див. Pdf мікроарха Агнера). Сюди входять останні процесори AMD, завдяки угоді про патент Intel / AMD.
Пітер Кордес

@PeterCordes чудово! Тож лічильники продуктивності задокументовані компанією Intel для підрахунку мікрооперацій?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Крім того, локальні змінні, пролиті з реєстрів, як правило, все ще залишаються гарячими в кеші L1, якщо будь-яка з них фактично використовується. Але читання з реєстру фактично безкоштовне, нульова затримка. Тож це нескінченно швидше, ніж кеш-пам’ять L1, залежно від того, як ви хочете визначити терміни. Для місцевих жителів, доступних лише для читання, основна вартість - просто додаткові завантаження (іноді операнди пам'яті, іноді з окремими movзавантаженнями). Для розлитих змінних, які не є const, перевезення в зворотному напрямку магазину є великою додатковою затримкою (додаткові ~ 5c проти прямої пересилання, а інструкції магазину недешеві).
Пітер Кордес

Так, на декількох різних етапах конвеєра існують лічильники загальної кількості uops (видавати / виконувати / виводити), отже, ви можете рахувати домен із сплавленим або неплавленим доменом. Дивіться цю відповідь, наприклад. Якби я переписував цю відповідь зараз, я б використовував ocperf.pyсценарій обгортки, щоб отримати прості символічні назви лічильників.
Пітер Кордес

23

Реєстрації виштовхування та вискакування є за кадром еквівалентом цього:

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.

Використовується як пара, це дозволяє зберегти реєстр у стеку та відновити його пізніше. Є й інші способи використання.


4
Так, ці послідовності правильно імітують push / pop. (крім push / pop не впливає на прапори).
Пітер Кордес,

2
Вам краще використовувати lea rsp, [rsp±8]замість add/, subщоб краще імітувати вплив push/ popна прапори.
Руслан

12

Майже всі процесори використовують стек. Стек програм - це техніка LIFO з апаратним підтримкою управління.

Стек - це обсяг програмної (ОЗП) пам'яті, який зазвичай виділяється у верхній частині купи пам'яті процесора і зростає (за вказівкою PUSH вказівник стека зменшується) у протилежному напрямку. Стандартним терміном для вставки в стек є PUSH, а для вилучення з стека - POP .

Керування стеком здійснюється через призначений для стека регістр ЦП, який також називається покажчиком стека, тому, коли процесор виконує POP або PUSH, вказівник стека завантажує / зберігає регістр або константу в пам’яті стека, і вказівник стека автоматично зменшується xor збільшується відповідно до кількості ссунутих слів або завантажений у (із) стека.

За допомогою інструкцій до асемблера ми можемо зберігати для стеку:

  1. Регістри ЦП, а також константи.
  2. Повернути адреси функцій або процедур
  3. Функції / процедури вхідних / вихідних змінних
  4. Локальні змінні функції / процедури.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.