_the_answer:
subl $12, %esp
movl $42, %eax
addl $12, %esp
ret
第一个 subl 递减堆栈指针,为函数中可能使用的变量腾出空间。例如,一个槽可用于帧指针,另一个用于保存返回地址。你说它应该省略帧指针。这通常意味着它省略了加载/存储来保存/恢复帧指针。但通常代码仍会为其保留内存。原因是它使分析堆栈的代码更加容易。很容易给堆栈的偏移量一个最小宽度,所以你知道你总是可以访问 FP+0x12 来获得第一个局部变量槽,即使你省略了保存帧指针。
好吧,eax
据我所知,x86 用于处理调用者的返回值。最后一个 addl 只是破坏了之前为您的函数创建的框架。
在函数的开头和结尾生成指令的代码称为函数的“尾声”和“序言”。这是我的端口在必须在 GCC 中创建函数序言时所做的事情(对于打算尽可能快速和多功能的实际端口来说,这要复杂得多):
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));
}
}
我省略了一些处理其他问题的代码,主要是告诉 GCC 哪些指令对异常处理很重要(即帧指针的存储位置等等)。好吧,被调用者保存的寄存器是调用者在调用之前不需要保存的寄存器。被调用的函数关心根据需要保存/恢复它们。正如您在第一行中看到的,我们总是为返回地址和帧指针分配空间。该空间只是几个字节,无关紧要。但是我们只在必要时生成存储/加载。最后注意“硬”帧指针是“真实”帧指针寄存器。由于某些 gcc 内部原因,这是必要的。“frame_pointer_needed”标志由 GCC 设置,只要我不能省略存储帧指针。在某些情况下,它必须被存储,例如当alloca
(它动态地改变堆栈指针)被使用时。GCC 关心这一切。请注意,自从我编写该代码以来已经有一段时间了,所以我希望我在上面添加的其他注释不是全错的 :)