Каково назначение регистра указателя кадра EBP?

95

Я новичок в ассемблере и заметил, что код x86, выдаваемый компиляторами, обычно сохраняет указатель кадра даже в режиме выпуска / оптимизации, когда он может использовать EBPрегистр для чего-то еще.

Я понимаю, почему указатель кадра может упростить отладку кода и может быть необходим, если он alloca()вызывается внутри функции. Однако в x86 очень мало регистров, и использование двух из них для хранения местоположения кадра стека, когда одного было бы достаточно, для меня не имеет смысла. Почему опускание указателя кадра считается плохой идеей даже в оптимизированных / выпускаемых сборках?

дсимча
источник
20
Если вы думаете, что у x86 очень мало регистров, вам следует проверить 6502 :)
Седат Капаноглу,
1
C99 VLA также может извлечь из этого пользу.
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功 01
2
stackoverflow.com/questions/1395591/…
Чиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
1
Разве указатель кадра не делает указатель стека избыточным? . TL; DR: 1. нетривиальное выравнивание стека 2. распределение стека ( alloca) 3. простота реализации во время выполнения: обработка exceptoins, песочница, сборщик мусора
Александр Малахов

Ответы:

102

Указатель кадра - это ссылочный указатель, позволяющий отладчику узнать, где находится локальная переменная или аргумент с одним постоянным смещением. Хотя значение ESP изменяется в процессе выполнения, EBP остается неизменным, что позволяет достичь той же переменной при том же смещении (например, первый параметр всегда будет на EBP + 8, а смещения ESP могут значительно измениться, поскольку вы будете нажимать / выскакивать)

Почему компиляторы не выбрасывают указатель кадра? Поскольку с указателем кадра отладчик может выяснить, где локальные переменные и аргументы используют таблицу символов, поскольку они гарантированно имеют постоянное смещение относительно EBP. В противном случае не существует простого способа определить, где находится локальная переменная в любой точке кода.

Как упоминал Грег, это также помогает раскручивать стек для отладчика, поскольку EBP предоставляет список фреймов стека с обратной связью, что позволяет отладчику определять размер фрейма стека (локальные переменные + аргументы) функции.

Большинство компиляторов предоставляют возможность опускать указатели кадров, хотя это действительно затрудняет отладку. Этот параметр никогда не следует использовать глобально, даже в коде выпуска. Вы не знаете, когда вам нужно отладить сбой пользователя.

Седат Капаноглу
источник
10
Компилятор, вероятно, знает, что делает с ESP. Однако остальные очки действительны, +1
erikkallen
8
Современные отладчики могут выполнять трассировку стека даже в коде, скомпилированном с использованием -fomit-frame-pointer. Этот параметр установлен по умолчанию в недавнем gcc.
Питер Кордес
2
@SedatKapanoglu: В разделе данных записана необходимая информация: yosefk.com/blog/…
Питер Кордес
3
@SedatKapanoglu: .eh_frame_hdrраздел также используется для исключений времени выполнения. Вы найдете его (с objdump -h) в большинстве двоичных файлов в системе Linux, это около 16 КБ для /bin/bash, против 572Б для GNU /bin/true, 108 КБ для ffmpeg. Существует опция gcc, позволяющая отключить его генерацию, но это «нормальный» раздел данных, а не раздел отладки, который stripудаляется по умолчанию. В противном случае вы не смогли бы выполнить обратную трассировку через библиотечную функцию, в которой не было символов отладки. Этот раздел может быть больше, чем push/mov/popинструкции, которые он заменяет, но у него почти нулевая стоимость времени выполнения (например, кеш uop).
Питер Кордес,
3
Относительно «например, первый параметр всегда будет в EBP-4»: Разве это не первый параметр в EBP + 8 (на x86)?
Aydin K.
31

Просто добавляю свои два цента к уже хорошим ответам.

Наличие цепочки фреймов стека является частью хорошей языковой архитектуры. BP указывает на текущий кадр, где хранятся локальные переменные подпрограммы. (Локальные переменные имеют отрицательные смещения, а аргументы - положительные смещения.)

Идея о том, что это препятствует использованию в оптимизации идеально хорошего регистра, поднимает вопрос: когда и где оптимизация действительно стоит?

Оптимизация имеет смысл только в тесных циклах, которые 1) не вызывают функции, 2) где счетчик программ тратит значительную часть своего времени, и 3) в коде, который компилятор действительно когда-либо увидит (т.е. небиблиотечные функции). Обычно это очень небольшая часть всего кода, особенно в больших системах.

Другой код можно скрутить и сжать, чтобы избавиться от циклов, и это просто не имеет значения, потому что счетчика программ практически нет.

Я знаю, что вы об этом не спрашивали, но по моему опыту, 99% проблем с производительностью не имеют ничего общего с оптимизацией компилятора. У них есть все, что связано с чрезмерным дизайном.

Майк Данлэйви
источник
Спасибо @Mike, я нашел ваш ответ очень полезным.
sixtyfootersdude 02
2
Отказ от указателя кадра также избавляет вас от пары инструкций при каждом вызове функции, что само по себе является небольшой оптимизацией. Кстати, ваше использование слова «напрашивается вопрос» неверно; вы имеете в виду "поднимает вопрос".
augurar
@augurar: Исправлено. Спасибо. Я сам немного
ворчу
3
@augurar Язык эволюционирует: «Напрашивает вопрос» теперь просто означает «поднимает вопрос». То, что вы придирчиво относитесь к устаревшим методам, ничего не добавляет.
user3364825
9

Конечно, это зависит от компилятора. Я видел оптимизированный код, создаваемый компиляторами x86, которые свободно используют регистр EBP в качестве регистра общего назначения. (Я не помню, с каким компилятором я это заметил.)

Компиляторы также могут поддерживать регистр EBP для помощи при раскручивании стека во время обработки исключений, но опять же это зависит от точной реализации компилятора.

Грег Хьюгилл
источник
Большинство компиляторов по умолчанию используют -fomit-frame-pointerоптимизацию. (когда это позволяет ABI). GCC, clang, ICC и MSVC - все это делают, IIRC, даже при ориентации на 32-разрядную Windows. Да, мой ответ о том, почему лучше использовать регистр ebp, чем регистр esp, для поиска параметров в стеке? показывает, что даже 32-битная Windows может опустить указатель кадра. 32-битный Linux x86 определенно может и делает. И, конечно же, 64-битные ABI с самого начала допускали пропуск указателя кадра.
Питер Кордес
4

Однако у x86 очень мало регистров

Это верно только в том смысле, что коды операций могут адресовать только 8 регистров. Сам процессор на самом деле будет иметь гораздо больше регистров, чем это, и использовать переименование регистров, конвейерную обработку, спекулятивное выполнение и другие модные слова процессора, чтобы обойти этот предел. В Википедии есть хороший вводный абзац о том, что процессор x86 может сделать, чтобы преодолеть ограничение регистра: http://en.wikipedia.org/wiki/X86#Current_implementations .

MSN
источник
1
Исходный вопрос касается сгенерированного кода, который строго ограничен регистрами, на которые ссылаются коды операций.
Darron
1
Да, но именно поэтому опускание указателя кадра в оптимизированных сборках в настоящее время не так важно.
Майкл
1
Однако переименование регистров - это не совсем то же самое, что наличие большего количества доступных регистров. По-прежнему существует множество ситуаций, когда переименование регистров не поможет, а более «обычные» регистры помогут.
jalf
1

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

Сколько вы экономите на чистом регистре? Стоит ли оно того?

dwc
источник
Количество регистров ограничено кодировкой инструкций. x86-64 использует биты в байте префикса REX для расширения части инструкций, определяющей регистр, с 3 до 4 бит для регистров src и dest. Если бы было место, x86-64, вероятно, перешла бы к 32 архитектурным регистрам, хотя сохранение / восстановление такого количества при переключении контекста начинает складываться. 15 - это огромный шаг по сравнению с 7, но 31 в большинстве случаев - гораздо меньшее улучшение. (не считая указатель стека как универсальный.) Быстрое нажатие / извлечение отлично подходит не только для кадров стека. Однако это не компромисс с количеством регистров.
Питер Кордес