为什么gcc输出中有额外的指令?

为什么gcc输出中有额外的指令?,gcc,assembly,x86,Gcc,Assembly,X86,GCC编译(使用GCC--省略帧指针-s): 进入 “$12”常量在这里做什么,“%esp”寄存器是什么?简短回答:堆栈帧 详细回答:当您调用函数时,编译器将操纵堆栈指针,以允许使用函数变量等本地数据。由于您的代码正在更改esp,堆栈指针,因此我假设这里正在发生这种情况。我认为GCC足够聪明,可以在实际不需要的地方对其进行优化,但您可能没有使用优化。使用GCC 4.3.2我得到以下函数: the_answer: movl $42, %eax ret …加上周围的垃圾,通过使用以下命令行:

GCC编译(使用
GCC--省略帧指针-s
):

进入


“$12”常量在这里做什么,“%esp”寄存器是什么?

简短回答:堆栈帧


详细回答:当您调用函数时,编译器将操纵堆栈指针,以允许使用函数变量等本地数据。由于您的代码正在更改
esp
,堆栈指针,因此我假设这里正在发生这种情况。我认为GCC足够聪明,可以在实际不需要的地方对其进行优化,但您可能没有使用优化。

使用GCC 4.3.2我得到以下函数:

the_answer:
movl    $42, %eax
ret
…加上周围的垃圾,通过使用以下命令行:
echo'int the_answer(){return 42;}'|gcc--省略帧指针-S-x c-o--

您使用的是哪个版本

_the_answer:
    subl    $12, %esp
    movl    $42, %eax
    addl    $12, %esp
    ret
第一个subl递减堆栈指针,为函数中可能使用的变量腾出空间。例如,一个插槽可用于帧指针,另一个插槽可用于保存返回地址。您说过它应该忽略帧指针。这通常意味着它忽略了加载/存储以保存/恢复帧指针。但通常代码仍会为其保留内存。原因是它使分析堆栈的代码更容易。为堆栈的偏移量指定一个最小宽度很容易,因此您知道始终可以访问FP+0x12,以获取第一个局部变量插槽,即使忽略保存帧指针也是如此

据我所知,x86上的eax用于处理调用方的返回值。最后一个addl只会破坏先前为函数创建的框架

在函数开始和结束时生成指令的代码称为函数的“尾声”和“序言”。以下是我的端口在GCC中创建函数序言时所做的操作(对于希望尽可能快速和通用的实际端口来说,这要复杂得多):

void eco32\u序言(void){
int i,j;
/*为所有被叫方保存的寄存器保留空间,以及2个附加寄存器:
*用于帧指针和返回地址*/
int regs_saved=寄存器_to_be_saved()+2;
int stackptr_off=(regs_saved*4+get_frame_size());
/*递减堆栈指针*/
发射移动insn(堆栈指针rtx,
gen_rtx_减号(SImode、堆栈指针_rtx、,
总工程师(stackptr_off));
/*如果需要,请保存回信地址*/
如果(eco32_ra_ever_killed()){
/*注:reg 31是返回地址寄存器*/
发射移动传感器(发电机rtx传感器),
加上常数(堆栈指针),
-4+堆垛机关闭),
gen_rtx_REG(SImode,31));
}
/*如果需要,保存帧指针*/
如果(需要帧指针){
发射移动传感器(发电机rtx传感器),
加上常数(堆栈指针),
-8+堆垛机关闭),
硬帧指针(rtx);
}
/*保存被调用方保存寄存器*/

对于(i=0,j=3;i堆栈对齐。在函数项中,
esp
是-4 mod 16,因为返回地址是由
call
推送的。减去12会重新对齐它。在x86上没有很好的理由让堆栈对齐到16字节,除非是在使用mmx/sse/等的多媒体代码中,但在3.x时代的某个地方,gcc开发人员会决定堆栈无论如何都应该保持对齐,强加开场白/尾声开销,增加堆栈大小,并导致所有程序上的缓存抖动增加,以满足一些特殊用途的兴趣(这碰巧是我感兴趣的一些领域,但我仍然认为这是不公平的错误决定)

通常,如果您启用任何优化级别,gcc将删除叶函数(不进行函数调用的函数)堆栈对齐的无用序言/尾声,但它将在您开始调用时返回


您还可以使用
-mprefered stack boundary=2

4.0.1(Apple Inc.build 5488)解决此问题。我猜这是一个错误。@Mike,不是一个错误。代码运行良好,因为subl被addl反转。它效率低下,但肯定不是一个错误。可能不是一个错误,可能只是因为4.3在确定哪些指令可以安全删除方面更聪明。会随“-O3”一起消失。也许苹果的GCC具有更低的默认优化级别?取决于苹果开发者认为这是值得的。不过这本身并不是一个gcc问题。后端作者需要考虑+1这应该是公认的答案!我使用gcc 4.8.x(MinGW)尝试了一个类似的例子,默认情况下,在
\u main
过程中,它发出
和l$-16,%esp
作为序言的一部分,这也有效地将堆栈指针对齐到16个字节。不过,在被调用的函数
\u answer
中不会出现这样的指令
the_answer:
movl    $42, %eax
ret
_the_answer:
    subl    $12, %esp
    movl    $42, %eax
    addl    $12, %esp
    ret
void eco32_prologue(void) {
    int i, j;
    /* reserve space for all callee saved registers, and 2 additional ones:
     * for the frame pointer and return address */
    int regs_saved = registers_to_be_saved() + 2;
    int stackptr_off = (regs_saved * 4 + get_frame_size());

    /* decrement the stack pointer */
    emit_move_insn(stack_pointer_rtx, 
                   gen_rtx_MINUS(SImode, stack_pointer_rtx, 
                                 GEN_INT(stackptr_off)));

    /* save return adress, if we need to */
    if(eco32_ra_ever_killed()) {
        /* note: reg 31 is return address register */
        emit_move_insn(gen_rtx_MEM(SImode, 
                           plus_constant(stack_pointer_rtx, 
                                         -4 + stackptr_off)),  
                       gen_rtx_REG(SImode, 31));
    }

    /* save the frame pointer, if it is needed */
    if(frame_pointer_needed) {
        emit_move_insn(gen_rtx_MEM(SImode, 
                           plus_constant(stack_pointer_rtx, 
                                         -8 + stackptr_off)), 
                       hard_frame_pointer_rtx);
    }

    /* save callee save registers */
    for(i=0, j=3; i<FIRST_PSEUDO_REGISTER; i++) {
        /* if we ever use the register, and if it's not used in calls
         * (would be saved already) and it's not a special register */
        if(df_regs_ever_live_p(i) && 
           !call_used_regs[i] && !fixed_regs[i]) {
            emit_move_insn(gen_rtx_MEM(SImode, 
                               plus_constant(stack_pointer_rtx, 
                                             -4 * j + stackptr_off)), 
                           gen_rtx_REG(SImode, i));
            j++;
        }
    }

    /* set the new frame pointer, if it is needed now */
    if(frame_pointer_needed) {
        emit_move_insn(hard_frame_pointer_rtx, 
                       plus_constant(stack_pointer_rtx, stackptr_off));
    }
}