为什么gcc输出中有额外的指令?
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--省略帧指针-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));
}
}