_the_answer:
subl $12, %esp
movl $42, %eax
addl $12, %esp
ret
Il primo sub decrementa il puntatore dello stack, per creare spazio per le variabili che possono essere utilizzate nella funzione. Uno slot può essere utilizzato per il puntatore del frame, un altro per contenere l'indirizzo di ritorno, ad esempio. Hai detto che dovrebbe omettere il puntatore del frame. Questo di solito significa che omette i carichi/negozi per salvare/ripristinare il puntatore del frame. Ma spesso il codice riserva ancora memoria per questo. Il motivo è che rende il codice che analizza lo stack molto più facilmente. È facile dare all'offset dello stack una larghezza minima e quindi sai che puoi sempre accedere a FP + 0x12, per ottenere il primo slot di variabile locale, anche se ometti di salvare il puntatore del frame.
Bene, eax
su x86 viene utilizzato per gestire il valore restituito al chiamante, per quanto ne so. E l'ultimo addl distrugge solo il frame creato in precedenza per la tua funzione.
Il codice che genera le istruzioni all'inizio e alla fine delle funzioni è chiamato "epilogo" e "prologo" della funzione. Ecco ciò che la mia porta fa quando si deve creare il prologo di una funzione in GCC (è modo più complesso per le porte del mondo reale che intendono essere il più veloce e versatile possibile):
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));
}
}
ho omesso qualche codice che si occupa di altri problemi, in primo luogo dicendo a GCC quali sono le istruzioni importanti per la gestione delle eccezioni (cioè dove il puntatore del frame è archiviato e così via). Bene, i registri salvati con il callee sono quelli che il chiamante non ha bisogno di salvare prima di una chiamata. La funzione chiamata si preoccupa di salvarli/ripristinarli secondo necessità. Come vedi nelle prime righe, assegniamo sempre spazio per l'indirizzo di ritorno e il puntatore del frame. Quello spazio è solo pochi byte e non ha importanza. Ma noi generiamo solo i negozi/carichi quando necessario. Da notare infine che il puntatore del frame "hard" è il "vero" registro del puntatore del frame. È necessario a causa di alcune ragioni interne di gcc. Il flag "frame_pointer_needed" viene impostato da GCC, ogni volta che posso non omettere di memorizzare il frame-pointer. Per alcuni casi, deve essere memorizzato, ad esempio quando viene utilizzato alloca
(modifica dinamicamente lo stackpointer). Il GCC si preoccupa di tutto questo.Nota che è passato del tempo da quando ho scritto quel codice, quindi spero che i commenti aggiuntivi che ho aggiunto sopra non siano tutti sbagliati :)
4.0.1 (Apple Inc. build 5488). Indovina che è un bug. –
@Mike, non un bug. Il codice funziona bene poiché il sub è invertito dal addl. È inefficiente ma sicuramente non è un bug. – paxdiablo
Potrebbe non essere un bug, potrebbe essere solo che 4.3 è più intelligente nel capire quali istruzioni sono sicure da rimuovere. – flussence