Это все о реестре 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) функция не имеет параметров.
Пропуск кадров стека (код входа и выхода для процедуры) может сделать код меньше и быстрее, но также может отрицательно повлиять на способность отладчиков отслеживать данные в стеке и отображать их программисту. Это параметры компилятора, которые определяют, при каких условиях функция должна удовлетворять, чтобы компилятор присудил ей код входа и выхода из кадра стека. Например, у компилятора могут быть опции для добавления такого кода входа и выхода к функциям в следующих случаях: (а) всегда, (б) никогда, (в) при необходимости (с указанием условий).
Возвращаясь от общих деталей к частностям: если вы будете использовать параметр -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 ++ Compiler 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. Для / Oy-: отключение / O1, / O2 или / O3 оптимизации. Параметр -fno-omit-frame-pointer устанавливается, когда вы указываете параметр - O0 или параметр -g. Параметр -fomit-frame-pointer устанавливается, если вы указываете параметр -O1, -O2 или -O3.
Параметр / Oy устанавливается при указании параметра / O1, / O2 или / O3. Параметр / Oy- устанавливается при указании параметра / Od.
Использование параметра -fno-omit-frame-pointer или / Oy- уменьшает количество доступных регистров общего назначения на 1 и может привести к несколько менее эффективному коду.
ПРИМЕЧАНИЕ Для систем Linux *: в настоящее время существует проблема с обработкой исключений GCC 3.2. Поэтому компилятор Intel игнорирует эту опцию, если GCC 3.2 установлен для C ++ и включена обработка исключений (по умолчанию).
Имейте в виду, что приведенная выше цитата актуальна только для компилятора Intel C ++ 15, а не для GCC.