在gcc编译过的main()中启用无用序的动机,将其禁用吗? [英] Motivation for useless prologue in gcc-compiled main(), disabling it?

查看:85
本文介绍了在gcc编译过的main()中启用无用序的动机,将其禁用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出以下最小测试用例:

Given the following minimal test case:

void exit(int);

int main() { 
    exit(0);
}

具有32位x86目标的GCC 4.9及更高版本会产生类似以下内容:

GCC 4.9 and later with 32-bit x86 target produces something like:

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

请注意复杂的堆栈重排代码.将函数重命名为除main以外的任何函数,它给出了(更加合理的):

Note the convoluted stack-realignment code. With the function renamed to anything but main, however, it gives the (much more reasonable):

xmain:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $8, %esp
        subl    $12, %esp
        pushl   $0
        call    exit

-O之间的差异更加明显.由于main没有任何变化;重命名,它会产生:

The differences are even more pronounced with -O. As main nothing changes; renamed, it yields:

xmain:
        subl    $24, %esp
        pushl   $0
        call    exit

上面的内容在回答这个问题时被注意到:

The above was noticed in answering this question:

如何摆脱通话__x86.get_pc_thunk.ax

此行为(及其动机)是否记录在任何地方,是否有任何方法可以抑制它? GCC具有特定于x86目标的选项,可以设置首选/假定的传入和传出堆栈对齐方式,以及为任意功能启用/禁用重新对齐方式,但是对于main而言,它们似乎并不适用.

Is this behavior (and its motivation) documented anywhere, and is there any way to suppress it? GCC has x86 target-specific options to set the preferred/assumed incoming and outgoing stack alignment and enable/disable realignment for arbitrary functions, but they don't seem to be honored for main.

推荐答案

此答案基于原始资料.我不知道开发商的意图或动机是什么.涉及的所有代码似乎可以追溯到2008ish,那是我自己在GCC上工作之后的时间,但是很久以前,人们的记忆可能变得模糊起来. (GCC 4.9于2014年发布;您能追溯到更远吗?如果我对引入此代码的时间感到正确,则main的笨拙堆栈对齐应在4.4版本中开始.)

This answer is based on source diving. I do not know what the developers' intentions or motivations were. All of the code involved seems to date to 2008ish, which is after my own time working on GCC, but long enough ago that people's memories have probably gotten fuzzy. (GCC 4.9 was released in 2014; did you go back any farther than that? If I'm right about when this code was introduced, the clumsy stack alignment for main should start happening in version 4.4.)

GCC的x86后端似乎已经过编码,可以对进入main时的堆栈对齐方式做出保守的假设,而不考虑命令行选项.函数 ix86_minimum_incoming_stack_boundary 被调用以计算每个函数在入口时的预期堆栈对齐方式,以及它所做的最后一件事...

GCC's x86 back end appears to have been coded to make extra-conservative assumptions about the stack alignment on entry to main, regardless of command-line options. The function ix86_minimum_incoming_stack_boundary is called to compute the expected stack alignment on entry for each function, and the last thing it does ...

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,则

...将预期的堆栈对齐方式改写为保守常数MAIN_STACK_BOUNDARY. MAIN_STACK_BOUNDARY在编译64位代码时为128(位),在编译32位代码时为32(位).据我所知,没有命令行旋钮使它期望堆栈比进入main时的堆栈更对齐.我通过告诉它不需要其他对齐方式,说服它跳过main的堆栈对齐方式,用-m32 -mpreferred-stack-boundary=2编译测试程序可以使我

... is override the expected stack alignment to a conservative constant, MAIN_STACK_BOUNDARY, if the function being compiled is main. MAIN_STACK_BOUNDARY is 128 (bits) when compiling 64-bit code and 32 when compiling 32-bit code. As far as I can tell, there is no command-line knob that will make it expect the stack to be more aligned than that on entry to main. I can persuade it to skip stack alignment for main by telling it that no additional alignment is needed, compiling your test program with -m32 -mpreferred-stack-boundary=2 gives me

main:
        pushl   $0
        call    exit

使用GCC 7.3.

%ecx的只写操作似乎是未优化的错误.它们来自

The write-only manipulations of %ecx appear to be a missed-optimization bug. They are coming from this part of 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,因此在没有必要的情况下,什么也无法再次删除.

The intention is to save a pointer to the incoming argument area before realigning the stack, so that it is straightforward to access arguments. Either because this happens fairly late in the pipeline (after register allocation), or because the instructions are marked FRAME_RELATED, nothing manages to delete those instructions again when they turn out to be unnecessary.

我想海湾合作委员会的开发人员至少会关于此的错误报告,但是他们可能会合理地认为它的优先级较低,因为这些指令在整个生命周期中仅执行一次程序中,它们实际上只有在main不使用其参数时才失效,并且仅在传统的32位ABI中发生,我印象中,如今它已被视为第二类目标.

I imagine the GCC devs would at least listen to a bug report about this, but they might reasonably consider it low priority, because these are instructions that are executed only once in the lifetime of the whole program, they're only actually dead when main doesn't use its arguments, and they only happen in the traditional 32-bit ABI, which I have the impression is considered a second-class target nowadays.

这篇关于在gcc编译过的main()中启用无用序的动机,将其禁用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆