Я новичок в ассемблере и заметил, что код x86, выдаваемый компиляторами, обычно сохраняет указатель кадра даже в режиме выпуска / оптимизации, когда он может использовать EBP
регистр для чего-то еще.
Я понимаю, почему указатель кадра может упростить отладку кода и может быть необходим, если он alloca()
вызывается внутри функции. Однако в x86 очень мало регистров, и использование двух из них для хранения местоположения кадра стека, когда одного было бы достаточно, для меня не имеет смысла. Почему опускание указателя кадра считается плохой идеей даже в оптимизированных / выпускаемых сборках?
performance
assembly
x86
дсимча
источник
источник
alloca
) 3. простота реализации во время выполнения: обработка exceptoins, песочница, сборщик мусораОтветы:
Указатель кадра - это ссылочный указатель, позволяющий отладчику узнать, где находится локальная переменная или аргумент с одним постоянным смещением. Хотя значение ESP изменяется в процессе выполнения, EBP остается неизменным, что позволяет достичь той же переменной при том же смещении (например, первый параметр всегда будет на EBP + 8, а смещения ESP могут значительно измениться, поскольку вы будете нажимать / выскакивать)
Почему компиляторы не выбрасывают указатель кадра? Поскольку с указателем кадра отладчик может выяснить, где локальные переменные и аргументы используют таблицу символов, поскольку они гарантированно имеют постоянное смещение относительно EBP. В противном случае не существует простого способа определить, где находится локальная переменная в любой точке кода.
Как упоминал Грег, это также помогает раскручивать стек для отладчика, поскольку EBP предоставляет список фреймов стека с обратной связью, что позволяет отладчику определять размер фрейма стека (локальные переменные + аргументы) функции.
Большинство компиляторов предоставляют возможность опускать указатели кадров, хотя это действительно затрудняет отладку. Этот параметр никогда не следует использовать глобально, даже в коде выпуска. Вы не знаете, когда вам нужно отладить сбой пользователя.
источник
-fomit-frame-pointer
. Этот параметр установлен по умолчанию в недавнем gcc..eh_frame_hdr
раздел также используется для исключений времени выполнения. Вы найдете его (сobjdump -h
) в большинстве двоичных файлов в системе Linux, это около 16 КБ для/bin/bash
, против 572Б для GNU/bin/true
, 108 КБ дляffmpeg
. Существует опция gcc, позволяющая отключить его генерацию, но это «нормальный» раздел данных, а не раздел отладки, которыйstrip
удаляется по умолчанию. В противном случае вы не смогли бы выполнить обратную трассировку через библиотечную функцию, в которой не было символов отладки. Этот раздел может быть больше, чемpush/mov/pop
инструкции, которые он заменяет, но у него почти нулевая стоимость времени выполнения (например, кеш uop).Просто добавляю свои два цента к уже хорошим ответам.
Наличие цепочки фреймов стека является частью хорошей языковой архитектуры. BP указывает на текущий кадр, где хранятся локальные переменные подпрограммы. (Локальные переменные имеют отрицательные смещения, а аргументы - положительные смещения.)
Идея о том, что это препятствует использованию в оптимизации идеально хорошего регистра, поднимает вопрос: когда и где оптимизация действительно стоит?
Оптимизация имеет смысл только в тесных циклах, которые 1) не вызывают функции, 2) где счетчик программ тратит значительную часть своего времени, и 3) в коде, который компилятор действительно когда-либо увидит (т.е. небиблиотечные функции). Обычно это очень небольшая часть всего кода, особенно в больших системах.
Другой код можно скрутить и сжать, чтобы избавиться от циклов, и это просто не имеет значения, потому что счетчика программ практически нет.
Я знаю, что вы об этом не спрашивали, но по моему опыту, 99% проблем с производительностью не имеют ничего общего с оптимизацией компилятора. У них есть все, что связано с чрезмерным дизайном.
источник
Конечно, это зависит от компилятора. Я видел оптимизированный код, создаваемый компиляторами x86, которые свободно используют регистр EBP в качестве регистра общего назначения. (Я не помню, с каким компилятором я это заметил.)
Компиляторы также могут поддерживать регистр EBP для помощи при раскручивании стека во время обработки исключений, но опять же это зависит от точной реализации компилятора.
источник
-fomit-frame-pointer
оптимизацию. (когда это позволяет ABI). GCC, clang, ICC и MSVC - все это делают, IIRC, даже при ориентации на 32-разрядную Windows. Да, мой ответ о том, почему лучше использовать регистр ebp, чем регистр esp, для поиска параметров в стеке? показывает, что даже 32-битная Windows может опустить указатель кадра. 32-битный Linux x86 определенно может и делает. И, конечно же, 64-битные ABI с самого начала допускали пропуск указателя кадра.Это верно только в том смысле, что коды операций могут адресовать только 8 регистров. Сам процессор на самом деле будет иметь гораздо больше регистров, чем это, и использовать переименование регистров, конвейерную обработку, спекулятивное выполнение и другие модные слова процессора, чтобы обойти этот предел. В Википедии есть хороший вводный абзац о том, что процессор x86 может сделать, чтобы преодолеть ограничение регистра: http://en.wikipedia.org/wiki/X86#Current_implementations .
источник
Использование стековых фреймов стало невероятно дешевым в любом оборудовании, даже отдаленно современном. Если у вас дешевые фреймы стека, то сохранение пары регистров не так важно. Я уверен, что быстрые кадры стека по сравнению с большим количеством регистров были инженерным компромиссом, и быстрые кадры стека победили.
Сколько вы экономите на чистом регистре? Стоит ли оно того?
источник