Це все про реєстр BP / EBP / RBP на платформах Intel. Цей регістр за замовчуванням має сегмент стека (для доступу до сегмента стека не потрібен спеціальний префікс).
EBP є найкращим вибором регістру для доступу до структур даних, змінних та динамічно розподіленого робочого простору в стеці. EBP часто використовується для доступу до елементів у стеку відносно фіксованої точки в стеку, а не щодо поточної TOS. Зазвичай він ідентифікує базову адресу поточного кадру стека, встановленого для поточної процедури. Коли EBP використовується як базовий регістр при розрахунку зсуву, зсув обчислюється автоматично в поточному сегменті стека (тобто сегменті, вибраному на даний момент SS). Оскільки SS не потрібно чітко вказувати, кодування інструкцій у таких випадках є більш ефективним. EBP також може використовуватися для індексування на сегменти, адресовані через інші регістри сегментів.
(джерело - http://css.csail.mit.edu/6.858/2017/readings/i386/s02_03.htm )
Оскільки на більшості 32-розрядних платформ сегмент даних та сегмент стека однакові, ця асоціація EBP / RBP зі стеком більше не є проблемою. Так само і на 64-розрядних платформах: Архітектура x86-64, представлена AMD у 2003 році, значною мірою відмовилася від підтримки сегментації в 64-розрядному режимі: чотири регістри сегментів: CS, SS, DS та ES змушені до 0 Ці обставини 32-розрядних та 64-розрядних платформ x86 по суті означають, що регістр EBP / RBP може використовуватися без будь-якого префіксу в інструкціях процесора, що здійснюють доступ до пам'яті.
Отож варіант компілятора, про який ви писали, дозволяє використовувати BP / EBP / RBP для інших засобів, наприклад, для зберігання локальної змінної.
Під "Це уникає вказівок щодо збереження, налаштування та відновлення покажчиків на фрейми" мається на увазі уникання наступного коду при введенні кожної функції:
push ebp
mov ebp, esp
або enter
інструкція, яка була дуже корисна для процесорів Intel 80286 та 80386.
Крім того, перед поверненням функції використовується наступний код:
mov esp, ebp
pop ebp
або leave
інструкція.
Інструменти налагодження можуть сканувати дані стеку та використовувати ці дані переданого реєстру EBP під час пошуку call sites
, тобто для відображення імен функції та аргументів у тому порядку, в якому вони були ієрархічно викликані.
Програмісти можуть мати запитання щодо стекових кадрів не в широкому плані (що це одна сутність у стеці, яка обслуговує лише один виклик функції та зберігає адресу повернення, аргументи та локальні змінні), а у вузькому сенсі - коли термін stack frames
згадується в контекст параметрів компілятора. З точки зору компілятора, кадр стека - це лише код входу та виходу для підпрограми , який штовхає прив'язку до стеку - який також може бути використаний для налагодження та обробки винятків. Інструменти налагодження можуть сканувати дані стеку та використовувати ці прив'язки для зворотного трасування, одночасно знаходячись call sites
у стеці, тобто для відображення імен функції в порядку, який вони називали ієрархічно.
Ось чому для програміста дуже важливо зрозуміти, що таке фрейм стека з точки зору параметрів компілятора - адже компілятор може контролювати, генерувати цей код чи ні.
У деяких випадках компілятор може опустити кадр стека (код входу та виходу для підпрограми), і доступ до змінних буде здійснюватися безпосередньо через покажчик стека (SP / ESP / RSP), а не через зручний базовий покажчик (BP / ESP / RSP). Умови для того, щоб компілятор опускав фрейми стека для деяких функцій, можуть бути різними, наприклад: (1) функція - це функція листа (тобто кінцева сутність, яка не викликає інших функцій); (2) не використовуються винятки; (3) жодні процедури не викликаються із вихідними параметрами у стеку; (4) функція не має параметрів.
Опускання кадрів стека (код входу та виходу для процедури) може зробити код меншим і швидшим, але також може негативно вплинути на здатність налагоджувачів робити зворотне відстеження даних у стеці та відображати їх програмісту. Це параметри компілятора, які визначають, за яких умов повинна задовольняти функція, щоб компілятор призначив їй код входу та виходу з фрейму стека. Наприклад, компілятор може мати варіанти додавання такого коду входу та виходу до функцій у таких випадках: (a) завжди, (b) ніколи, (c) за потреби (вказуючи умови).
Повернення від загального до особливостей: якщо ви будете використовувати -fomit-frame-pointer
опцію компілятора GCC, ви можете виграти як код входу, так і вихід для підпрограми, а також наявність додаткового реєстру (якщо це вже не ввімкнено за замовчуванням самостійно або неявно іншим варіантів, в цьому випадку ви вже отримуєте вигоду від виграшу від використання реєстру EBP / RBP, і ніякого додаткового виграшу не буде отримано, якщо явно вказати цей параметр, якщо він уже неявно включений). Однак зауважте, що в 16-розрядному та 32-розрядному режимах регістр BP не має можливості отримати доступ до його 8-розрядних частин, як це має AX (AL та AH).
Оскільки ця опція, окрім того, що дозволяє компілятору використовувати EBP як регістр загального призначення при оптимізації, також запобігає генерації коду виходу та входу для кадру стека, що ускладнює налагодження - ось чому в документації GCC явно зазначено (незвично підкреслюється жирним шрифтом style), що увімкнення цієї опції унеможливлює налагодження на деяких машинах
Також пам’ятайте, що інші параметри компілятора, пов’язані з налагодженням або оптимізацією, можуть неявно вмикати -fomit-frame-pointer
або вимикати опцію.
Я не знайшов офіційної інформації на gcc.gnu.org про те, як впливають інші параметри -fomit-frame-pointer
на платформах x86 , https://gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Optimize-Options.html зазначає лише наступне:
-O також включає -fomit-frame-pointer на машинах, де це не заважає налагодженню.
Тож із документації як такої не зрозуміло, чи -fomit-frame-pointer
буде вона ввімкнена, якщо ви просто скомпілюєте один -O
варіант на платформі x86. Це може бути перевірено емпіричним шляхом, але в цьому випадку розробники GCC не зобов'язуються не змінювати поведінку цього варіанту в майбутньому без попередження.
Однак Пітер Кордес зазначив у коментарях, що є різниця в налаштуваннях за замовчуванням -fomit-frame-pointer
між платформами x86-16 та x86-32 / 64.
Цей параметр - -fomit-frame-pointer
- також має відношення до компілятора Intel C ++ 15.0 , а не лише до GCC:
Для компілятора Intel ця опція має псевдонім /Oy
.
Ось що про це писала компанія Intel:
Ці параметри визначають, чи використовується EBP як регістр загального призначення при оптимізації. Параметри -fomit-frame-pointer та / Oy дозволяють це використовувати. Параметри -fno-omit-frame-pointer та / Oy- заборонити це.
Деякі налагоджувачі очікують, що EBP буде використовуватися як вказівник кадру стека, і не може створити зворотну трасування стека, якщо це не так. Параметри -fno-omit-frame-pointer та / Oy- спрямовують компілятор на створення коду, який підтримує та використовує EBP як вказівник кадру стека для всіх функцій, так що налагоджувач все ще може створити зворотну трасування стека, не роблячи наступного:
Для -fno-omit-frame-pointer: вимкнення оптимізацій за допомогою -O0 For / Oy-: вимкнення / O1, / O2 або / O3-оптимізація Параметр -fno-omit-frame-pointer встановлюється, коли ви вказуєте параметр - O0 або опція -g. Параметр -fomit-frame-pointer встановлюється, коли ви вказуєте параметр -O1, -O2 або -O3.
Параметр / Oy встановлюється, коли ви вказуєте параметр / O1, / O2 або / O3. Option / Oy- встановлюється, коли ви вказуєте параметр / Od.
Використання параметра -fno-omit-frame-pointer або / Oy- зменшує кількість доступних регістрів загального призначення на 1 і може привести до дещо менш ефективного коду.
ПРИМІТКА Для систем Linux *: На даний момент існує проблема з обробкою винятків GCC 3.2. Тому компілятор Intel ігнорує цю опцію, коли GCC 3.2 інстальовано для C ++ та ввімкнено обробку винятків (за замовчуванням).
Зверніть увагу, що наведена вище цитата стосується лише компілятора Intel C ++ 15, а не GCC.