_the_answer: subl $12, %esp movl $42, %eax addl $12, %esp ret
The first subl reduces the stack pointer to make room for variables that can be used in your function. One slot can be used for a frame pointer, the other for storing a return address, for example. You said that it should omit the frame pointer. Usually this means that it does not allow loading / saving to save / restore the frame pointer. But often the code still reserves memory for it. The reason is that it makes code that parses the stack a lot easier. It is easy to give the stack offset a minimum width, and you know that you can always access FP + 0x12 to get into the first local slot of a variable, even if you omit saving the frame pointer.
Well, eax on x86 is used to handle the return value to the caller as far as I know. And the last addl just destroys the previously created frame for your function.
The code that generates instructions at the beginning and end of functions is called the “epilogue” and “prologue” of the function. Here is what my port does when it needs to create a function prolog in GCC (it is more complex for real ports that intend to be as fast and universal as possible):
void eco32_prologue(void) { int i, j; int regs_saved = registers_to_be_saved() + 2; int stackptr_off = (regs_saved * 4 + get_frame_size()); emit_move_insn(stack_pointer_rtx, gen_rtx_MINUS(SImode, stack_pointer_rtx, GEN_INT(stackptr_off))); if(eco32_ra_ever_killed()) { emit_move_insn(gen_rtx_MEM(SImode, plus_constant(stack_pointer_rtx, -4 + stackptr_off)), gen_rtx_REG(SImode, 31)); } if(frame_pointer_needed) { emit_move_insn(gen_rtx_MEM(SImode, plus_constant(stack_pointer_rtx, -8 + stackptr_off)), hard_frame_pointer_rtx); } for(i=0, j=3; i<FIRST_PSEUDO_REGISTER; i++) { 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++; } } if(frame_pointer_needed) { emit_move_insn(hard_frame_pointer_rtx, plus_constant(stack_pointer_rtx, stackptr_off)); } }
I omitted some code that deals with other problems, first of all with GCC indicating what instructions are important for handling exceptions (for example, where the frame pointer is stored, etc.). Well, saved registries are those that the caller does not need to save until the call. The called function takes care of saving / restoring them as needed. As you see in the first lines, we always allocate space for the return address and frame pointer. This space is only a few bytes and does not matter. But if necessary, we only generate stores / loads. Finally, note that the “hard” frame pointer is the “real” frame pointer register. This is necessary due to some internal reasons for gcc. The "frame_pointer_needed" flag is set by GCC when I cannot omit saving the frame pointer. In some cases, it should be stored, for example, when alloca used (it changes the dynamic speaker). GCC takes care of all this. Please note that some time has passed since I wrote this code, so I hope that the additional comments that I added above are not all wrong :)