Попытка понять параметр gcc -fomit-frame-pointer - программирование
Подтвердить что ты не робот

Попытка понять параметр gcc -fomit-frame-pointer

Я попросил Google дать мне значение опции gcc -fomit-frame-pointer, которая перенаправляет меня на следующую инструкцию.

-fomit-frame-pointer
        Don't keep the frame pointer in a register for functions that don't 
need one. This avoids the instructions to save, set up and restore frame 
pointers; it also makes an extra register available in many functions. It 
also makes debugging impossible on some machines.

В соответствии с моими знаниями для каждой функции в стеке памяти процесса будет создана запись активации, чтобы сохранить все локальные переменные и некоторую дополнительную информацию. Я надеюсь, что этот указатель кадра означает адрес записи активации функции.

В этом случае, каков тип функций, для которых ему не нужно сохранять указатель рамки в регистре? Если я получу эту информацию, я попытаюсь создать новую функцию на основе этого (если это возможно), потому что если указатель фрейма не хранится в регистрах, некоторые инструкции будут опущены в двоичном формате. Это действительно улучшит производительность в приложении, где есть много функций.

4b9b3361

Ответ 1

Для большинства меньших функций не требуется указатель кадра - большие функции МОГУТ нуждаться в этом.

Это действительно о том, насколько хорошо компилятору удается отслеживать, как используется стек, и где вещи находятся в стеке (локальные переменные, аргументы, переданные текущей функции и аргументы, готовые к функции, подлежащей вызову). Я не думаю, что легко охарактеризовать функции, которые нуждаются или не нуждаются в указателе кадра [технически, NO function HAS имеет указатель на фрейм - это скорее случай "если компилятор считает необходимым уменьшить сложность другого кода" ].

Я не думаю, что вам следует "попытаться сделать функции не имеющими фрейм-указатель" как часть вашей стратегии кодирования - как я уже сказал, простые функции им не нужны, поэтому используйте -fomit-frame-pointer, и вы получите еще один регистр для распределителя регистров и сохраните 1-3 инструкции по входу/выходу на функции. Если вашей функции нужен фрейм-указатель, это потому, что компилятор решает, что лучший вариант, чем использование указателя кадра. Это не цель иметь функции без указателя рамки, это цель иметь код, который работает как правильно, так и быстро.

Обратите внимание, что "не имеющий framepointer" должен давать лучшую производительность, но это не какая-то волшебная пуля, которая дает огромные улучшения, особенно не на x86-64, у которой уже есть 16 регистров. В 32-разрядном x86, поскольку у него только 8 регистров, один из которых является указателем на стек, а другой - в качестве указателя фрейма, берется 25% пространства регистров. Чтобы изменить это до 12,5%, это довольно улучшилось. Разумеется, компиляция для 64-битного кода тоже очень поможет.

Ответ 2

Это все о регистре 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), а не удобный указатель базы (ВР/ESP/РСП). Условия для компилятора для опускания кадров стека для некоторых функций могут быть разными, например: (1) функция является функцией листа (т.е. Конечной сущности, которая не вызывает другие функции); (2) не используются исключения; (3) не вызываются вызовы с исходящими параметрами в стеке; (4) функция не имеет параметров.

Опускание фреймов стека (код входа и выхода для подпрограммы) может сделать код меньше и быстрее, но также может отрицательно повлиять на способность отладчиков отслеживать данные в стеке и отображать их для программиста. Это параметры компилятора, которые определяют, в каких условиях функция должна удовлетворять, чтобы компилятор мог присвоить ему код записи стека и код выхода. Например, у компилятора могут быть опции для добавления такого кода входа и выхода в функции в следующих случаях: (a) всегда, (b) никогда, (c) при необходимости (с указанием условий).

Возвращаясь из общих черт к особенностям: если вы будете использовать параметр компилятора -fomit-frame-pointer GCC, вы можете выиграть как код входа и выхода для этой процедуры, так и наличие дополнительного регистра (если он уже не включен по умолчанию либо сам по себе, либо неявно другими вариантами, в этом случае вы уже выиграли от выигрыша от использования регистра EBP/RBP, и никакой дополнительной выгоды не будет получено путем явного указания этого параметра, если он уже неявно). Обратите внимание, однако, что в 16-битном и 32-битном режимах регистр BP не имеет возможности доступа к 8-битным частям, например AX (AL и AH).

Поскольку этот параметр, кроме того, что позволяет компилятору использовать EBP в качестве универсального регистра при оптимизации, также предотвращает создание кода выхода и ввода для фрейма стека, что усложняет отладку - поэтому Документация GCC явно заявляет (необычно подчеркивает жирным шрифтом), что включение этой опции делает невозможным отладку на некоторых машинах

Также помните, что другие параметры компилятора, связанные с отладкой или оптимизацией, могут неявно включать или отключать опцию -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 нет обязательств не изменять поведение этого параметра в будущем без уведомления.

Однако Peter Cordes указал в комментариях, что существует разница в настройках по умолчанию для -fomit-frame-pointer между платформами x86-16 и x86-32/64 платформы.

Эта опция - -fomit-frame-pointer - также относящаяся к Intel С++ Compiler 15.0, а не только к GCC:

Для компилятора Intel этот параметр имеет псевдоним /Oy.

Вот что написал об этом intel:

Эти параметры определяют, используется ли EBP в качестве универсального регистра при оптимизации. Параметры -fomit-frame-pointer и /Oy позволяют это использовать. Параметры -fno-omit-frame-pointer и /Oy -disallow it.

Некоторые отладчики ожидают, что EBP будет использоваться как указатель фрейма стека и не сможет создать обратную трассировку стека, если это не так. -fno-omit-frame-pointer и /Oy -options направляют компилятор на создание кода, который поддерживает и использует EBP в качестве указателя фрейма стека для всех функций, чтобы отладчик все же мог создавать обратную трассировку стека, не выполняя следующие действия:

Для -fno-omit-frame-pointer: отключить оптимизацию с -O0 Для /Oy -: выключение/O1,/O2 или /O 3 оптимизации Опция -fno-omit-frame-pointer устанавливается, когда вы указываете опцию -O0 или -g. Параметр -fomit-frame-pointer устанавливается, когда вы указываете опцию -O1, -O2 или -O3.

Параметр/Oy устанавливается, когда вы указываете опцию /O 1,/O2 или/O3. Опция /Oy - устанавливается, когда вы указываете опцию /Od.

Использование опции -fno-omit-frame-pointer или /Oy - уменьшает количество доступных регистров общего назначения на 1 и может привести к чуть менее эффективному коду.

ПРИМЕЧАНИЕ Для систем Linux *: В настоящее время проблема с обработкой исключений GCC 3.2. Поэтому компилятор Intel игнорирует этот параметр, когда GCC 3.2 установлен для С++, а обработка исключений включена (по умолчанию).

Помните, что приведенная выше цитата применима только для компилятора Intel С++ 15, а не для GCC.