Этот ответ основан на исходном погружении. Я не знаю, каковы были намерения или мотивы разработчиков. Весь задействованный код, похоже, датируется 2008-м годом, после моего собственного времени работы над GCC, но достаточно давно, чтобы воспоминания людей, вероятно, стали размытыми. (GCC 4.9 был выпущен в 2014 году; вы пошли дальше?) Если я прав насчет того, когда этот код был введен, неуклюжее выравнивание стека для main
должно начаться в версии 4.4.)
Серверная часть GCC x86, по-видимому, была закодирована, чтобы делать сверхконсервативные предположения о выравнивании стека при входе в main
, независимо от параметров командной строки. Функция ix86_minimum_incoming_stack_boundary
вызывается для вычисления ожидаемого выравнивания стека при вводе для каждой функции и последней вещи, которую она делает ...
12523 /* Stack at entrance of main is aligned by runtime. We use the
12524 smallest incoming stack boundary. */
12525 if (incoming_stack_boundary > MAIN_STACK_BOUNDARY
12526 && DECL_NAME (current_function_decl)
12527 && MAIN_NAME_P (DECL_NAME (current_function_decl))
12528 && DECL_FILE_SCOPE_P (current_function_decl))
12529 incoming_stack_boundary = MAIN_STACK_BOUNDARY;
12530
12531 return incoming_stack_boundary;
... переопределяет ожидаемое выравнивание стека на консервативную константу, MAIN_STACK_BOUNDARY
, если компилируемая функция имеет значение main
. MAIN_STACK_BOUNDARY
равно 128 (битам) при компиляции 64-битного кода и 32 при компиляции 32-битного кода. Насколько я могу судить, нет ручки командной строки, которая заставила бы его ожидать, что стек будет более выровнен, чем при входе в main
. Я могу убедить его пропустить выравнивание стека для main
, сказав, что дополнительное выравнивание не требуется, компиляция вашей тестовой программы с помощью -m32 -mpreferred-stack-boundary=2
дает мне
main:
pushl $0
call exit
с GCC 7.3.
Манипуляции только для записи %ecx
кажутся ошибкой пропущенной оптимизации. Они приходят из этой части ix86_expand_prologue
:
13695 /* Grab the argument pointer. */
13696 t = plus_constant (Pmode, stack_pointer_rtx, m->fs.sp_offset);
13697 insn = emit_insn (gen_rtx_SET (crtl->drap_reg, t));
13698 RTX_FRAME_RELATED_P (insn) = 1;
13699 m->fs.cfa_reg = crtl->drap_reg;
13700 m->fs.cfa_offset = 0;
13701
13702 /* Align the stack. */
13703 insn = emit_insn (ix86_gen_andsp (stack_pointer_rtx,
13704 stack_pointer_rtx,
13705 GEN_INT (-align_bytes)));
13706 RTX_FRAME_RELATED_P (insn) = 1;
13707
Цель состоит в том, чтобы сохранить указатель на область входящего аргумента перед перестройкой стека, чтобы получить прямой доступ к аргументам. Либо потому, что это происходит довольно поздно в конвейере (после выделения регистра), либо потому, что инструкции помечены как FRAME_RELATED, ничему не удается удалить эти инструкции снова, когда они оказываются ненужными.
Я полагаю, что разработчики GCC по крайней мере прослушивают отчет об ошибке по этому поводу, но они могут обоснованно считать его низким приоритетом, потому что это инструкции, которые выполняются только один раз за время существования всей программы. на самом деле они только мертвы, когда main
не использует его аргументы, и они случаются только в традиционном 32-битном ABI, который, как мне кажется, в настоящее время считается целью второго класса.