Когда я компилирую 32-битный код C с GCC и параметром -fomit-frame-pointer, указатель кадра (ebp) не используется, если только моя функция не вызывает функции Windows API с stdcall и хотя бы одним параметром.
Например, если я использую только GetCommandLine() из Windows API, который не имеет параметров/аргументов, GCC пропустит указатель фрейма и будет использовать ebp для других целей, ускоряя код и избавляясь от бесполезного пролога.
Но в тот момент, когда я вызываю функцию stdcall Win32, которая принимает хотя бы один аргумент, GCC полностью игнорирует -fomit-frame-pointer и все равно использует указатель кадра, а код хуже при проверке, поскольку он не может использовать ebp для общих целей. вещи. Не говоря уже о том, что я нахожу указатель кадра совершенно бессмысленным. Я имею в виду, что я хочу скомпилировать для выпуска и распространения, зачем мне отладка? (если я хочу отлаживать, я просто использую отладочную сборку после воспроизведения ошибки)
Мой стек, безусловно, НЕ содержит динамического распределения, такого как alloca. Итак, стек имеет определенную структуру, но GCC выбирает тупой метод, несмотря на мои варианты? Есть ли что-то, чего мне не хватает, чтобы заставить его не использовать указатель кадра?
Моя вторая проблема заключается в том, что он отказывается использовать инструкции «push» для функций Win32. Все остальные компиляторы, которые я пробовал, использовали push-инструкции для помещения в стек, что приводило к гораздо лучшему, более компактному коду, не говоря уже о том, что это самый естественный способ поместить аргументы для stdcall. Тем не менее, GCC упрямо использует инструкции «mov» для перемещения в каждом месте вручную со смещением относительно esp, потому что ему нужно, чтобы указатель стека оставался полностью статичным. stdcall устроен таким образом, чтобы вызывающая сторона была удобна для вызывающей стороны, и все же GCC полностью упускает из виду суть stdcall, поскольку генерирует этот дрянной код при взаимодействии с ним. Что еще хуже, так как указатель стека статичен, он по-прежнему использует указатель кадра? Просто почему?
Я пробовал -mpush-args, ничего не делает.
Я также заметил, что если я сделаю свой стек достаточно большим, чтобы он превышал страницу (4096 байт), GCC добавит пролог с функцией, которая ничего не делает, кроме «побитового или» стека через каждые 4096 байт с нулем (что ничего не делает) . Я предполагаю, что это для прикосновения к стеку и автоматического выделения памяти с ошибками страницы, если стек был зарезервирован? К сожалению, это происходит, даже если я устанавливаю начальную фиксацию стека (не резерва) на достаточно высокое значение, чтобы удерживать мой стек, не говоря уже о том, что это вообще не нужно. Избыточный код в лучшем виде.
Есть ли эти ошибки в GCC? Или я что-то пропустил в опциях? Должен ли я использовать что-то еще? Пожалуйста, сообщите мне, если я пропустил некоторые параметры.
Я серьезно надеюсь, что мне не придется создавать встроенный макрос asm только для вызова функций stdcall и использования инструкций push (и, я думаю, это также позволит избежать указателя кадра). Звучит чересчур для чего-то настолько простого, что должно быть в современных компиляторах. И да, я использую GCC 4.8.1, так что это не древняя версия.
В качестве дополнительного вопроса можно ли заставить GCC не сохранять регистры в стеке в прологе функции? Я использую свою собственную прямую точку входа с аргументом -nostartfiles, потому что это чисто Windows-приложение, и оно отлично работает без стандартного запуска библиотеки. Если я использую attribute((noreturn)), он отменит эпилог, восстанавливающий регистры, но все равно поместит их в стек в прологе, я не знаю, есть ли способ заставить его не сохранять регистры для этой функции точки входа. В любом случае, это не имеет большого значения, я думаю, это было бы более полным. Спасибо!