2015-09-17 33 views
5

Ho provato a scrivere un semplice codice di test simili (main.c):Perché ARM gcc inserisce il registro r3 e lr nello stack all'inizio di una funzione?

main.c 
void test(){ 
} 
void main(){ 
    test(); 
} 

Poi usato braccio-non-eabi-gcc per compilare e objdump per ottenere il codice assembly:

arm-none-eabi-gcc -g -fno-defer-pop -fomit-frame-pointer -c main.c 
arm-none-eabi-objdump -S main.o > output 

Il codice assembly spinge i registri r3 e lr, anche la funzione non ha fatto nulla.

La mia domanda è perché arm gcc sceglie di spingere r3 nello stack, anche la funzione test() non la usa mai? Gcc sceglie casualmente 1 registro per premere? Se è per il requisito di allineamento pila (8 byte per ARM), perché non sottrarre semplicemente la sp? Grazie.

================== Aggiornamento =====================

@KemyLand per la risposta, ho un altro esempio: il codice sorgente è:

void test1(){ 
} 
void test(int i){ 
     test1(); 
} 
void main(){ 
     test(1); 
} 

io uso lo stesso comando di compilazione sopra, quindi il seguente assemblea:

main.o:  file format elf32-littlearm 


Disassembly of section .text: 

00000000 <test1>: 
void test1(){ 
} 
    0: e12fff1e  bx  lr 

00000004 <test>: 
void test(int i){ 
    4: e52de004  push {lr}   ; (str lr, [sp, #-4]!) 
    8: e24dd00c  sub  sp, sp, #12 
    c: e58d0004  str  r0, [sp, #4] 
     test1(); 
    10: ebfffffe  bl  0 <test1> 
} 
    14: e28dd00c  add  sp, sp, #12 
    18: e49de004  pop  {lr}   ; (ldr lr, [sp], #4) 
    1c: e12fff1e  bx  lr 

00000020 <main>: 
void main(){ 
    20: e92d4008  push {r3, lr} 
     test(1); 
    24: e3a00001  mov  r0, #1 
    28: ebfffffe  bl  4 <test> 
} 
    2c: e8bd4008  pop  {r3, lr} 
    30: e12fff1e  bx  lr 

Se spinta {r3, lr} nel primo esempio serve per usare meno istruzioni, perché in questa funzione test(), il comp iler non ha usato solo un'istruzione?

push {r0, lr} 

Si usa 3 istruzioni invece di 1.

push {lr} 
sub sp, sp #12 
str r0, [sp, #4] 

proposito, perché sub sp con 12, la pila è di 8 byte allineato, può semplicemente sub con 4 giusto?

+1

possibile duplicato di [Perché il puntatore dello stack è stato spostato di 4 byte maggiore della dimensione del frame dello stack durante la compilazione con arm-linux-gnueabi-gcc?] (http://stackoverflow.com/questions/22279911/why-is-the-stack-pointer-moved-down-4-bytes-greater-than-the-stack-frame-size-wh) – auselen

+1

e un altro possibile duplicato di http://stackoverflow.com/questions/16120123/arm-why-do-i-need-to-push-pop-two-registers-at-function-calls – auselen

+0

per assicurare l'allineamento dello stack, è stato chiesto e ha risposto qui un numero di volte –

risposta

5

Secondo la Standard ARM Embedded ABI, r0 attraverso r3 sono utilizzati per passare gli argomenti a una funzione, e il valore di ritorno della stessa, nel frattempo lr (alias: r14) è il registro di collegamento, il cui scopo è quello di contenere l'indirizzo di ritorno per un funzione.

È ovvio che è necessario salvare lr, altrimenti main() non avrebbe modo di tornare al proprio chiamante.

E 'ormai noto menzionare che ogni singola istruzione ARM accetta 32 bit e, come hai detto, ARM ha un requisito di allineamento dello stack di chiamata di 8 byte. E, come bonus, stiamo usando il Embedded ARM ABI, quindi la dimensione del codice deve essere ottimizzata. Pertanto, è più efficiente avere una singola istruzione a 32 bit sia salvando lr sia allineando lo stack premendo un registro inutilizzato (r3 non è necessario, perché test() non accetta argomenti né restituisce nulla), quindi esegue un singolo 32 -bit istruzioni, piuttosto che aggiungere ulteriori istruzioni (e quindi, sprecare memoria preziosa!) per manipolare il puntatore dello stack.

Dopo tutto, è logico concludere che si tratta solo di un'ottimizzazione di GCC.