在gcc编译的main()中无用的序言的动机,禁用它?

R..*_*R.. 6 c gcc

鉴于以下最小测试用例:

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)

在回答这个问题时注意到以上内容:

我如何摆脱调用__x86.get_pc_thunk.ax

这种行为(及其动机)是否记录在案,是否有任何方法可以抑制它?GCC具有x86特定于目标的选项,用于设置首选/假定的传入和传出堆栈对齐以及启用/禁用任意函数的重新对齐,但它们似乎并不值得尊重main.

zwo*_*wol 6

这个答案是基于源头潜水.我不知道开发者的意图或动机是什么.所有涉及的代码似乎都可以追溯到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中,我现在认为它被认为是二等目标.

  • @R ..我碰巧在这台计算机上有一个GCC结账......修订历史本身并不是非常有用,但我发现了一点点挖掘https://gcc.gnu.org/ml/gcc-patches/ 2008-04/msg00349.html ......听起来这是对x86序言生成的一个相当重大的改变. (2认同)