鉴于以下最小测试用例:
void exit(int);
int main() {
exit(0);
}
Run Code Online (Sandbox Code Playgroud)
GCC 4.9及更高版本的32位x86目标产生类似于:
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
subl $4, %esp
subl $12, %esp
pushl $0
call exit
Run Code Online (Sandbox Code Playgroud)
请注意复杂的堆栈重新排列代码.但是,将函数重命名为除main之外的任何函数,它会给出(更合理):
xmain:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
subl $12, %esp
pushl $0
call exit
Run Code Online (Sandbox Code Playgroud)
差异甚至更明显-O.由于main没有任何变化; 重命名,它产生:
xmain:
subl $24, %esp
pushl $0
call exit
Run Code Online (Sandbox Code Playgroud)
在回答这个问题时注意到以上内容:
这种行为(及其动机)是否记录在案,是否有任何方法可以抑制它?GCC具有x86特定于目标的选项,用于设置首选/假定的传入和传出堆栈对齐以及启用/禁用任意函数的重新对齐,但它们似乎并不值得尊重main.
这个答案是基于源头潜水.我不知道开发者的意图或动机是什么.所有涉及的代码似乎都可以追溯到2008年,这是在我自己的GCC工作之后,但很久以前人们的记忆可能已经模糊了.(GCC 4.9是在2014年发布的;你是否回过头了?如果我对这个代码的引入是正确的,那么笨拙的堆栈对齐main应该从版本4.4开始.)
main无论命令行选项如何,GCC的x86后端似乎都被编码为对进入时的堆栈对齐做出超保守的假设.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;
Run Code Online (Sandbox Code Playgroud)
... MAIN_STACK_BOUNDARY如果编译的函数是,则覆盖预期的堆栈对齐到保守常量main. MAIN_STACK_BOUNDARY编译64位代码时为128(位),编译32位代码时为32位.据我所知,没有命令行旋钮可以让它期望堆栈比进入时更加对齐main.我可以main通过告诉它不需要额外的对齐来说服它跳过堆栈对齐,编译你的测试程序-m32 -mpreferred-stack-boundary=2给我
main:
pushl $0
call exit
Run Code Online (Sandbox Code Playgroud)
与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
Run Code Online (Sandbox Code Playgroud)
目的是在重新排列堆栈之前保存指向传入参数区域的指针,以便可以直接访问参数.要么是因为这在管道中发生得相当晚(在寄存器分配之后),要么因为指令被标记为FRAME_RELATED,所以当它们变得不必要时,没有什么能够再次删除那些指令.
我想象中的GCC开发者至少会听到这个bug报告,但他们可能会合理地认为是低优先级,因为这些是在整个程序的生命周期只执行一次指令,他们实际上只死时main没有按不使用它的论点,它们只发生在传统的32位ABI中,我现在认为它被认为是二等目标.
| 归档时间: |
|
| 查看次数: |
174 次 |
| 最近记录: |