280 likes | 456 Views
Procedure in assembler. Procedure. Una procedura, o subrutine, è uno strumento che i programmatori usano per strutturare i programmi, sia per renderli più facili da capire che per permettere il riutilizzo del codice.
E N D
Procedure Una procedura, o subrutine, è uno strumento che i programmatori usano per strutturare i programmi, sia per renderli più facili da capire che per permettere il riutilizzo del codice. Le procedure consentono di concentrarsi su una parte del problema, i parametri permettono di scambiare dati.
I registri giocano un ruolo importante per tenere traccia delle procedure e memorizzare i valori di ritorno. Convenzioni: Indirizzo di ritorno $ra Passaggio dei parametri $a0, $a1, $a2, $a3 Valori di ritorno $v0, $v1 Variabili locali $s0, $s1, … , $s7 Procedure
Procedure main() {int i,j,k,m; i = mult(j,k); ... m = mult(i,i); ... } /* really dumb mult function */ int mult (int mcand, int mlier){int product; product = 0;while (mlier > 0) { product = product + mcand; mlier = mlier -1; }return product;}
Istruzioni per procedure (1/4) H L L ... sum(a,b);... /* a,b:$s0,$s1 */}int sum(int x, int y) { return x+y;} address1000 add $a0,$s0,$zero # x = a1004 add $a1,$s1,$zero # y = b1008 addi $ra,$zero,1016 #$ra=10161012j sum#jump to sum1016 ... 2000 sum: add $v0,$a0,$a12004jr $ra# new instruction MIPS
Istruzioni per procedure (2/4) • Esiste una istruzione singola che salta ad un indirizzo e contemporaneamente salva l’indirizzo dell’istruzione successiva nel registro $ra : jump and link (jal) • Prima:1008 addi $ra,$zero,1016 #$ra=10161012 j sum #go to sum • Dopo:1012 jal sum # $ra=1016,go to sum
Istruzioni per procedure (3/4) • La sintassi per jal (jump and link) è la stessa di j (jump): jal label • jal dovrebbe essere chiamato laj : “link and jump”: • Step 1 (link): Salva l’indirizzo dell’isruzione che segue in $ra • Step 2 (jump): Salta all’etichetta indicata
Istruzioni per procedure (4/4) • La sintassi per jr (jump register): jr register • Invece di prevedere un etichetta alla quale saltare, l’istruzione jr prevede un registro che contiene un indirizzo. • jal memorizza l’indirizzo di ritorno nel registro ($ra) • jr ritorna a quell’indirizzo
Nested Procedures (1/2) int sumSquare(int x, int y) { return mult(x,x)+ y;} • Qualcuno ha chiamato sumSquare, ora sumSquare sta chiamando mult. • Così c’è un valore in $ra al quale sumSquare vuole ritornare, ma sarà sovrascritto dalla chiamata a mult. • Bisogna salvare l’indirizzo di ritorno sumSquare prima di chiamare mult.
Nested Procedures (2/2) • In generale può esserci il bisogno di salvare qualche altra informazione oltre che quelle in $ra. • Quando un programma gira, sono allocate tre importanti aree della memoria: • Static: variabili dichiarate nel programma una volta per tutte, finiscono di esistere solo quando il programma termina. E.g., globali • Heap: variabili dichiarate dinamicamente • Stack (pila): Spazio utilizzato dalle procedure durante l’esecuzione: dove è possibile salvare I valori dei registri
Spazio per le procedure $sp puntatore stack Spazio creato esplicitamente, e.g., puntatori Variabili globali Heap Code Static Stack Programma HLL memory Allocation Indirizzi ¥ 0
Usare la Stack (1/2) • La stack è una coda del tipo last-in first-out (l’ultimo ad entrare è il primo ad uscire) • Abbiamo un registro $sp che punta sempre all’ultimo spazio usato nella stack dove la procedura può memorizzare I registri da riversare e dove può recuperare I vecchi valori dei registri. • Il puntatore dello stack viene aggiornato ogni volta che viene inserito o estratto il valore di un registro. • Lo stack “cresce” a partire da indirizzi di memoria alti verso indirizzi di memoria bassi.
Usare la Stack (2/2) • int sumSquare(int x, int y) { return mult(x,x)+ y; } sumSquare: addi $sp,$sp,-8 # space on stack sw $ra, 4($sp) # save ret addr sw $a1, 0($sp) # save y “push” Memorizzare un dato add$a1,$a0,$zero # mult(x,x) jal mult # call mult lw $a1, 0($sp) # restore y add $v0,$v0,$a1 # mult()+y lw $ra, 4($sp) # get ret addraddi $sp,$sp,8 # restore stack jr $ramult: ... “pop” Estrarre un dato
Passi per una chiamata di procedura 1) Salvare I valori necessari nella stack. 2) Assegnare gli argomenti, se ce ne sono. 3) jal 4) Riprendere i valori dalla stack.
Regole per le procedure • Vengono chiamate con un istruzione jal, ritornano al punto chiamante con jr $ra • Accettano fino a 4 argomenti $a0, $a1, $a2 e $a3 • Il valore di ritorno è sempre in $v0 (e se necessario in $v1) • Devono seguire le convenzioni per I registri
Registri MIPS The constant 0 $0 $zeroReserved for Assembler $1 $atReturn Values $2-$3 $v0-$v1Arguments $4-$7 $a0-$a3Temporary $8-$15 $t0-$t7Saved $16-$23 $s0-$s7More Temporary $24-$25 $t8-$t9Used by Kernel $26-27 $k0-$k1Global Pointer $28 $gpStack Pointer $29 $spFrame Pointer $30 $fpReturn Address $31 $ra Usare nomi per I registri – il codice è più chiaro!
Convenzioni per i registri (1/5) • Caller: La procedura chiamante • Callee: La procedura chiamata • Quando callee ritorna dall’esecuzione, caller deve sapere quali registri può aver cambiato e quali sono sicuramente rimasti invariati. • Register Conventions: Un insieme di regole generalmente accettate a proposito di quali registri una chiamata ad una procedura può (e non può cambiare).
Convenzioni per i registri (2/5) • $0: No Change. Sempre 0. • $s0-$s7: No Change. Se callee cambia questi valori ci deve rimettere I valori originali prima di ritornare. • $sp: No Change. Il puntatore alla stack deve essere lo stesso prima e dopo la chiamata jal.
Convenzioni per i registri (3/5) • $ra: Change. jal stesso cambierà questo registro. Caller ha bisogno di salvarlo nella stack se c’è un’altra chiamata. • $v0-$v1: Change. Aspettano I nuovi valori. • $a0-$a3: Change. Sono gli argomenti. Caller deve salvarli se ha ancora bisogno di loro dopo la chiamata. • $t0-$t9: Change. Sono chiamati temporanei: ogni procedura può cambiarli in ogni momento. Caller deve salvarli se ha ancora bisogno di loro dopo la chiamata.
Convenzioni per i registri (4/5) • Cosa significa? • Se la procedura R chiama la procedura E, allora R deve salvare ogni registro temporaneo che può usare nella stack prima di un jal . • La procedura E deve salvare ogni S registro che intende usare prima di recuperare I suoi valori. • Ricordate: Caller/callee hanno bisogno di salvare solo I registri di cui hanno bisogno.
Convenzioni per i registri (5/5) • Notate che se callee è arrivato ad usare qualche registro s deve: • Salvare s nella stack • Usare I registri • Riprendere s dalla stack • jr $ra
“In conclusione…” (1/2) • Le procedure sono chiamate con jal, e ritornano con jr $ra. • Usate la stack per salvare tutto quello di cui avete bisogno. • Register Conventions: Usatele!.
“In conclusione…” (2/2) • Istruzioni che conosciamo Arithmetic: add, addi, sub, addu, addiu, subu Memory: lw, sw Decision: beq, bne, slt, slti, sltu, sltiu Unconditional Branches (Jumps): j, jal, jr
Esempio (1/5) main() {int i,j,k,m; /* i-m:$s0-$s3 */ i = mult(j,k); ... m = mult(i,i); ... } int mult (int mcand, int mlier){int product; product = 0;while (mlier > 0) { product += mcand; mlier -= 1; }return product;}
Esempio (2/5) __start: add $a0,$s1,$0 # arg0 = jadd $a1,$s2,$0 # arg1 = k jal mult # call multadd $s0,$v0,$0 # i = mult()... add $a0,$s0,$0 # arg0 = iadd $a1,$s0,$0 # arg1 = i jal mult # call multadd $s3,$v0,$0 # m = mult()... done
Esempio (3/5) • Note: • La funzione main finisce con done e non con jr $ra: non c’è bisogno di salvare $ra nella stack
Esempio (4/5) mult: add $t0,$0,$0 # prod=0 Loop: slt $t1,$0,$a1 # mlr > 0?beq $t1,$0,Fin # no=>Fin add $t0,$t0,$a0 # prod+=mcaddi $a1,$a1,-1 # mlr-=1 j Loop # goto Loop Fin: add $v0,$t0,$0 # $v0=prod jr $ra # return
Esempio (5/5) • Note: • Non vengono fatti jal da mult e non usiamo registri salvati: non abbiamo bisogno di mettere qualcosa da ricordare nella stack. • $a1 è direttamente modificato • Il risultato è messo in $v0