Яка мета реєстру покажчика кадру EBP?


94

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

Я розумію, чому покажчик кадру може полегшити налагодження коду, і це може знадобитися, якщо alloca()викликається у межах функції. Однак у x86 є дуже мало регістрів, і використовувати два з них для утримання місця розташування кадру стека, коли одного буде достатньо, для мене немає сенсу. Чому упущення покажчика кадру вважається поганою ідеєю навіть у оптимізованих / випущених версіях?


19
Якщо ви думаєте, що у x86 дуже мало реєстрів, ви повинні перевірити 6502 :)
Sedat Kapanoglu


1
Користь від нього також може отримати C99 VLA.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功


1
Чи не вказівник кадру робить покажчик стека зайвим? . TL; DR: 1. нетривіальне вирівнювання стека 2. розподіл стеків ( alloca) 3. простота виконання програми: обробка винятків, пісочниця, GC
Олександр Малахов

Відповіді:


102

Покажчик кадру - це довідковий покажчик, що дозволяє налагоджувачу знати, де знаходиться локальна змінна чи аргумент, з єдиним постійним зміщенням. Хоча значення ESP змінюється в ході виконання, EBP залишається тим самим, що дозволяє досягти тієї самої змінної при тому ж зміщенні (наприклад, перший параметр завжди буде на рівні EBP + 8, тоді як компенсації ESP можуть істотно змінитися, оскільки ви будете натискати / вискакує речі)

Чому компілятори не відкидають покажчик кадру? Оскільки за допомогою покажчика кадру, налагоджувач може з'ясувати, де локальні змінні та аргументи використовують таблицю символів, оскільки вони гарантовано постійно зміщуються до EBP. В іншому випадку існує непростий спосіб зрозуміти, де локальна змінна знаходиться в будь-якій точці коду.

Як згадував Грег, він також допомагає розмотати стек для налагоджувача, оскільки EBP надає зворотний зв'язаний список фреймів стека, тому дозволяє налагоджувачу визначити розмір кадру стека (локальні змінні + аргументи) функції.

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


10
Компілятор, ймовірно, знає, що робить ESP. Інші пункти дійсні, однак, +1
erikkallen

8
Сучасні відладчики можуть робити зворотні стеки навіть у складеному коді -fomit-frame-pointer. Цей параметр є типовим для останніх gcc.
Пітер Кордес

2
@SedatKapanoglu: Розділ даних записує необхідну інформацію: yosefk.com/blog/…
Пітер Кордес,

3
@SedatKapanoglu: цей .eh_frame_hdrрозділ також використовується для виключень під час виконання. Ви знайдете його (з objdump -h) у більшості двійкових файлів в системі Linux, це приблизно 16 к за /bin/bash, порівняно з 572 Б для GNU /bin/true, 108 к за ffmpeg. Існує опція gcc відключити її генерування, але це "звичайний" розділ даних, а не розділ налагодження, який stripвидаляється за замовчуванням. В іншому випадку ви не змогли виконати функцію бібліотеки, яка не мала символів налагодження. Цей розділ може бути більшим, ніж push/mov/popінструкції, які він замінює, але він має майже нульову вартість виконання (наприклад, загальний кеш).
Пітер Кордес

3
Що стосується "такого, як перший параметр завжди буде в EBP-4": Чи не перший параметр на EBP + 8 (на x86)?
Айдін К.

31

Просто додав два мої центи до вже хороших відповідей.

Це частина гарної мовної архітектури - мати ланцюжок стекових фреймів. ВР вказує на поточний кадр, де зберігаються підпрограми-локальні змінні. (Місцеві жителі мають від'ємне відшкодування, а аргументи - за рахунок позитивного компенсації.)

Ідея, що це заважає використовувати ідеально хороший реєстр для оптимізації, викликає питання: коли і де оптимізація насправді варта?

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

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

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


Дякую @Mike, я знайшов вашу відповідь дуже корисною.
sixtyfootersdude

2
Усунення вказівника кадру також економить вам пару інструкцій на кожен функціональний виклик, що є невеликою оптимізацією. До речі, ваше вживання "напрошує" неправильне; ви маєте на увазі "ставить питання".
аугурар

@augurar: виправлено. Дякую. Я трохи граматично нарікаю сам :)
Майк Данлаве

3
@augurar Мова розвивається: "Попрошує питання" тепер просто означає "ставить питання". Будучи рецептуристською ниткою для застарілого використання, нічого не додає.
користувач3364825

9

Це, безумовно, залежить від компілятора. Я бачив оптимізований код, випромінюваний компіляторами x86, який вільно використовує регістр EBP як реєстр загального призначення. (Я не пригадую, з яким компілятором я це помітив.)

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


Більшість компіляторів за замовчуванням, -fomit-frame-pointerколи ввімкнено оптимізацію. (коли АБІ дозволяє це). GCC, clang, ICC та MSVC - це все, IIRC, навіть коли націлено на 32-бітні Windows. Так, моя відповідь на тему Чому для пошуку параметрів на стеці краще використовувати ebp, ніж регістр esp? показує, що навіть 32-розрядна Windows може опускати покажчик кадру. 32-бітний x86 Linux напевно може і робить. І звичайно, 64-бітні ABI дозволили опускати покажчик кадру з самого початку.
Пітер Кордес

4

Однак у x86 є дуже мало регістрів

Це справедливо лише в тому сенсі, що опкоди можуть адресувати лише 8 регістрів. Сам процесор насправді матиме набагато більше реєстрів, ніж це, і використовувати перейменування реєстру, конвеєрне, спекулятивне виконання та інші мовленнєві слова процесора, щоб обійти цю межу. У Вікіпедії є хороший вступний параграф щодо того, що може зробити процесор x86 для подолання ліміту реєстру: http://en.wikipedia.org/wiki/X86#Current_implementations .


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

1
Так, але саме тому пропускання покажчика кадру в оптимізованих складах нині не так важливо.
Майкл

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

1

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

Скільки ви економите, чистий реєстр? Чи варто того?


Більше регістрів обмежено кодуванням інструкцій. x86-64 використовує біти в байті префікса REX для розширення частини інструкцій, що визначає регістр, від 3 до 4 біт для src та регістрів dest. Якби було місце, x86-64, ймовірно, пішов би до 32 архітектурних регістрів, хоча збереження / відновлення багатьох із контекстних комутаторів починає складатись. 15 - це величезний крок у порівнянні з 7, але 31 - значно менший покращення у більшості випадків. (не рахуючи покажчик стека як загальноприйнятого.) Робота швидкого натискання / попсовки чудово підходить для більше, ніж просто кадри стека. Однак це не компроміс з # рег.
Пітер Кордес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.