Я попросил Google дать мне значение gcc
параметра -fomit-frame-pointer
, который перенаправляет меня к приведенному ниже утверждению.
-fomit-frame-pointer
Не храните указатель фрейма в регистре для функций, которым он не нужен. Это позволяет избежать инструкций по сохранению, настройке и восстановлению указателей кадров; он также делает дополнительный регистр доступным для многих функций. Это также делает невозможной отладку на некоторых машинах.
Насколько мне известно о каждой функции, запись активации будет создана в стеке памяти процесса для хранения всех локальных переменных и некоторой дополнительной информации. Я надеюсь, что этот указатель кадра означает адрес записи активации функции.
В этом случае, каковы типы функций, для которых не нужно хранить указатель кадра в регистре? Если я получу эту информацию, я попытаюсь разработать новую функцию на ее основе (если возможно), потому что, если указатель кадра не хранится в регистрах, некоторые инструкции будут опущены в двоичном формате. Это действительно заметно улучшит производительность в приложении, в котором много функций.
источник
Release
иDebug
на самом деле очень полезно, принять этот вариант в качестве примера.Release
-build?Ответы:
Большинству небольших функций не нужен указатель кадра - он МОЖЕТ понадобиться более крупным функциям.
На самом деле речь идет о том, насколько хорошо компилятор отслеживает, как используется стек и где что-то находится в стеке (локальные переменные, аргументы, переданные текущей функции, и аргументы, подготовленные для функции, которая будет вызвана). Я не думаю, что легко охарактеризовать функции, которым нужен или не нужен указатель кадра (технически НИКАКАЯ функция НЕ ДОЛЖНА иметь указатель кадра - это скорее случай «если компилятор сочтет необходимым уменьшить сложность другой код »).
Я не думаю, что вам следует «пытаться сделать функции без указателя кадра» как часть вашей стратегии кодирования - как я уже сказал, простые функции в них не нуждаются, поэтому используйте
-fomit-frame-pointer
, и вы получите еще один доступный регистр для распределителя регистров и сохраните 1-3 инструкции по входу / выходу в functions. Если вашей функции нужен указатель кадра, это потому, что компилятор решает, что это лучший вариант, чем не использовать указатель кадра. Это не цель - иметь функции без указателя фрейма, это цель - иметь код, который работает правильно и быстро.Обратите внимание, что «отсутствие указателя кадра» должно дать лучшую производительность, но это не какая-то волшебная палочка, которая дает огромные улучшения - особенно на x86-64, у которого для начала уже есть 16 регистров. В 32-битной системе x86, поскольку она имеет только 8 регистров, один из которых является указателем стека, а другой занимает место в качестве указателя кадра, это означает, что занято 25% пространства регистров. Изменить это значение на 12,5% - это большое улучшение. Конечно, компиляция для 64-битной версии тоже очень поможет.
источник
alloca
который перемещает указатель стека на переменную величину. Отсутствие указателя кадра значительно усложняет отладку. Локальные переменные труднее найти, а трассировки стека гораздо сложнее восстановить без помощи указателя кадра. Кроме того, доступ к параметрам может стать более дорогостоящим, поскольку они находятся далеко от вершины стека и могут потребовать более дорогих режимов адресации.alloca
[кто использует? - Я на 99% уверен, что никогда не писал код, который используетalloca
] илиvariable size local arrays
[который является современной формойalloca
], тогда компилятор МОЖЕТ все же решить, что использование указателя кадра - лучший вариант - потому что компиляторы написаны так, чтобы не слепо следовать предоставлены варианты, но предоставят вам лучший выбор.alloca
: они выбрасываются, как только вы покидаете область, в которой они объявлены, тогда какalloca
пространство освобождается только тогда, когда вы выходите из функции.alloca
Я думаю, это делает VLA намного проще, чем .-fomit-frame-pointer
по умолчанию включен для x86-64.alloca
, пространство ed) неизвестен во время компиляции . Обычно компилятор будет использовать указатель фрейма для получения адреса локальных переменных, если размер фрейма стека не меняется, он может найти их по фиксированному смещению от указателя стека.Это все о реестре BP / EBP / RBP на платформах Intel. По умолчанию в этом регистре используется сегмент стека (не требуется специальный префикс для доступа к сегменту стека).
(источник - 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 для других целей, например, для хранения локальной переменной.
Под «Это позволяет избежать инструкций по сохранению, настройке и восстановлению указателей кадров» подразумевается отказ от следующего кода при вводе каждой функции:
или
enter
инструкция, которая очень пригодилась на процессорах Intel 80286 и 80386.Также перед возвратом функции используется следующий код:
или
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 заявляет только следующее:Таким образом, из документации как таковой неясно,
-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:
Имейте в виду, что приведенная выше цитата актуальна только для компилятора Intel C ++ 15, а не для GCC.
источник
gcc -m16
существует, но это странный частный случай, который в основном создает 32-битный код, который работает в 16-битном режиме, используя повсюду префиксы. Также обратите внимание, что-fomit-frame-pointer
он был включен по умолчанию в течение многих лет на x86-m32
и дольше, чем на x86-64 (-m64
).