Le procedure L insieme delle istruzioni (4) Architetture dei Calcolatori (lettere A-I) In ogni linguaggio di programmazione si struttura il proprio codice usando procedure (funzioni, metodi, ) L utilizzo di procedure permette Astrazione Riusabilità del codice Maggiore leggibilità e strutturazione del codice Testing più approfondito Creazione di librerie di funzioni utilizzate di frequente Fornite al programmatore dall ambiente Non è necessario scrivere funzioni comuni (matematiche, di input/output, ) ma basta usarle! Valeria Cardellini 1 Le procedure (2) Quando c è una chiamata di procedura, il programma: Salta all inizio della procedura Esegue il codice della procedura Torna nel punto di origine in cui era stata chiamata la procedura Chiamante (caller) Chiamata (callee) Le procedure sono caratterizzate da parametri di ingresso e la loro esecuzione può modificare lo stato del programma e/o tornare uno o più valori Prima della chiamata di una procedura E necessario 1) Porre i parametri della procedura in una posizione accessibile alla procedura chiamata 2) Trasferire il controllo alla procedura chiamata (salto) 3) Ottenere i valori restituiti come risultato 4) Ritornare al punto d origine (di chiamata) della procedura chiamante Per l accesso ai parametri di ingresso (punto 1) Si usano 4 registri specifici $a0, $a1, $a2, $a3 (argument register) Per trasferire il controllo alla procedura chiamata (punto 2) Istruzione jal (jump and link) Valeria Cardellini 2 Valeria Cardellini 3
L istruzione jal jal: jump and link Sintassi: jal label L istruzione jal permette di: Saltare all indirizzo specificato da label (come l istruzione j) Memorizzare l indirizzo di ritorno nel registro Indirizzo della prossima istruzione da eseguire In viene memorizzato PC+4 Valori restituiti (punto 3) La procedura chiamata pone il risultato (i parametri di uscita) nei registri $v0 e $v1 (return value register) Ritorno al punto di chiamata della procedura chiamante (punto 4) Si utilizza l istruzione jr con il registro (return address) viene caricato in PC Riassumendo La procedura chiamante deve Mettere i valori dei parametri da passare alla procedura chiamata nei registri $a0-$a3 Utilizzare l istruzione jal per saltare alla procedura chiamata e memorizzare il valore di PC+4 nel registro La procedura chiamata deve Eseguire il compito richiesto Memorizzare il risultato nei registri $v0, $v1 Restituire il controllo alla procedura chiamante con l istruzione jr jr Valeria Cardellini 4 Valeria Cardellini 5 Problemi Mantenere i valori passati come parametri alla procedura Una procedura può avere bisogno di più registri rispetto ai 4 a disposizione per il passaggio dei parametri ed i 2 per la restituzione dei valori Salvare i registri che la procedura chiamata potrebbe modificare, ma che la procedura chiamante ha bisogno di mantenere inalterati Fornire lo spazio necessario per le variabili locali alla procedura chiamata Gestione di procedure annidate (procedure che chiamano al loro interno altre procedure) e procedure ricorsive (procedure che invocano dei cloni di se stesse) Lo stack Lo stack (pila) è una struttura dati costituita da una coda LIFO (last-in-first-out) I dati sono inseriti nello stack con l operazione push I dati sono prelevati dallo stack con l operazione pop E necessario un puntatore alla cima (top) dello stack per salvare i registri che servono alla procedura chiamata Il registro $sp (stack pointer) contiene l indirizzo del top dello stack e viene aggiornato ogni volta che viene inserito o estratto il valore di un registro Utilizzo dello stack Valeria Cardellini 6 Valeria Cardellini 7
Gestione dello stack nel MIPS Lo stack cresce da indirizzi di memoria alti verso indirizzi di memoria bassi Il registro $sp contiene l indirizzo della prima locazione libera in cima allo stack 0x7ffffffc stack 0 L inserimento di un dato nello stack (operazione di push) avviene decrementando $sp per allocare lo spazio ed eseguendo sw per inserire il dato Il prelevamento di un dato nello stack (operazione di pop) avviene eseguendo lw ed incrementando $sp (per eliminare il dato), riducendo così la dimensione dello stack Valeria Cardellini 8 Gestione dello stack nel MIPS (2) Tutto lo spazio di uno stack di cui ha bisogno una procedura (record di attivazione) viene esplicitamente allocato dal programmatore in una sola volta, all inizio della procedura Lo spazio dello stack viene allocato sottraendo a $sp il numero di byte necessari addi $sp, $sp, -24 # alloca 6 parole nello stack Al rientro da una procedura, il record di attivazione viene rimosso (deallocato) dalla procedura stessa, incrementando $sp della stessa quantità di cui lo si era decrementato alla chiamata addi $sp, $sp, 24 # dealloca 6 parole nello stack E necessario liberare lo spazio deallocato per evitare di riempire tutta la memoria Valeria Cardellini 9 Salvataggio dei registri nello stack Quando si chiama una procedura, i registri utilizzati dalla procedura chiamante vanno Salvati nello stack Il loro contenuto va ripristinato alla fine dell esecuzione della procedura Per evitare di salvare inutilmente il contenuto dei registri, i registri sono divisi in due classi Registri temporanei ($t0,, $t7): il contenuto non è salvato dalla procedura chiamata nello stack Registri salvati o non temporanei ($s0,, $s7): il contenuto è salvato dalla procedura chiamata nello stack se utilizzati dalla chiamante Valeria Cardellini 10 Convenzioni per il salvataggio dell ambiente L esecuzione di una procedura non deve interferire con l ambiente chiamante I registri usati dalla procedura chiamante devono poter essere ripristinati al rientro della procedura chiamata Convenzioni adottate dal MIPS Per ottimizzare gli accessi alla memoria, la chiamante e la chiamata salvano sullo stack soltanto un particolare gruppo di registri La chiamante, se vuole che siano preservati, salva i registri $t0-$t9, i registri $a0-$a3 ed eventuali argomenti aggiuntivi La chiamata salva, i registri $s0-$s7 (se li usa), le strutture dati locali (ad es. array, strutture) e le variabili locali Preservato Non preservato Registri saved: $s0-$s7 Registri temporanei: $t0-$t9 Registro stack pointer: $sp Registri argomento: $a0-$a3 Registro return address: Registri di ritorno: $v0-$v1 Architetture dei Calcolatori Stack 2004/05 sopra lo stack pointer Valeria Cardellini Stack sotto lo stack pointer 11
Struttura di una procedura Ogni procedura è composta da tre parti Un prologo Salvataggio dell ambiente Un corpo Esecuzione della procedura vera e propria Un epilogo Ripristino dell ambiente Valeria Cardellini 12 Esempio di procedura foglia E una procedura che non ha annidate al suo interno chiamate ad altre procedure Non serve salvare nello stack (perché nessun altro lo modifica) Codice C: int leaf_example(int g, int h, int i, int j) { int f; f = (g+h) (i+j); return f; } Assumiamo che le variabili g, h, i e j corrispondono a $a0-$a3 e f a $s0 Valeria Cardellini 13 Esempio di procedura foglia (2) Esempio di procedura foglia (3).globl main leaf_example: addi $sp, $sp, -4 # decrementa lo stack di 4 per salvare $s0 sw $s0, 0($sp) # push di $s0 nello stack add $t0, $a0, $a1 # $t0 = g + h add $t1, $a2, $a3 # $t1 = i + j sub $s0, $t0, $t1 # f = $t0 - $t1 add $v0, $s0, $zero # restituisci f come risultato lw $s0, 0($sp) addi $sp, $sp, 4 jr main: addi $s0, $zero, 1 addi $a0, $zero, 5 addi $a1, $zero, 6 addi $a2, $zero, 4 addi $a3, $zero, 3 jal leaf_example # pop di $s0 dallo stack # incrementa lo stack # ritorna alla procedura chiamante Valeria Cardellini 14 Valeria Cardellini 15
Esempio di procedura ricorsiva E una procedura che contiene una chiamata a se stessa al suo interno Occorre prestare attenzione al salvataggio nello stack dei registri che occorrono alla procedura chiamante (parametri di input, indirizzo di ritorno) Codice C: int fact(int n) { if (n < 1) return(1); else return (n * fact(n-1)); } Esempio di procedura ricorsiva (2).globl main fact: addi $sp, $sp, -8 sw, 4($sp) sw $a0, 0($sp) # per salvare 2 registri nello stack # push di nello stack # push dell argomento n nello stack slti $t0, $a0, 1 # test se n < 1 beq $t0, $zero, L1 # se n>= 1, vai a L1 addi $v0, $zero, 1 # se n < 1, return 1 addi $sp, $sp, 8 # incrementa lo stack jr # ritorna alla procedura chiamante Assumiamo che la variabile n corrisponde a $a0 Valeria Cardellini 16 Valeria Cardellini 17 Esempio di procedura ricorsiva (3) L1: addi $a0, $a0, -1 # n >= 1: decrementa n di 1 jal fact # chiama fact(n-1) main: lw $a0, 0($sp) lw, 4($sp) addi $sp, $sp, 8 mul $v0, $a0, $v0 jr addi $a0, $zero, 4 jal fact # pop di n dallo stack # pop di dallo stack # dealloca stack # return n*fact(n-1) # ritorno alla procedura chiamante Esempio di procedura ricorsiva (4) Stack alla chiamata fact(0) nel caso in cui main chiama fact(4) stack $a0 = 4 fact(4) Crescita dello stack $a0 = 3 fact(3) $a0 = 2 fact(2) $a0 = 1 $a0 = 0 fact(1) fact(0) Valeria Cardellini 18 Valeria Cardellini 19
Record di attivazione Il record di attivazione (procedure frame) è un area di memoria necessaria alla memorizzazione dei dati ed all esecuzione di una procedura Valori passati alla procedura stack frame Higher memory addresses Salvataggio dei registri modificati Argument 6 Variabili locali alla procedura Argument 5 $fp Il frame consiste nello spazio di Saved registers memoria tra il registro frame pointer ($fp), che punta alla prima Stack grows parola del frame, e lo stack pointer Local variables ($sp), che punta all ultima parola $sp Lower memory addresses del frame - Si può accedere ai dati tramite $fp Il programmatore assembler deve provvedere esplicitamente ad allocare/deallocare lo spazio necessario Valeria Cardellini 20 Record di attivazione (2) Allocazione dello stack prima (a), durante (b) e dopo (c) la chiamata di una procedura Valeria Cardellini 21 Utilizzo della memoria in MIPS Il ruolo dei registri e le convenzioni I sistemi basati sul processore MIPS dividono la memoria in tre parti Segmento stack Inizia all indirizzo 0x7ffffffc e cresce verso il basso (segmento dati) Segmento dati Diviso in dati statici e dinamici Il registro global pointer ($gp) punta ai dati statici del segmento dati (indirizzo 0x10008000) Segmento testo Contiene le istruzioni del programma Nome $zero $at $v0-$v1 $a0-$a3 $t0-$t7 $s0-$s7 $t8-$t9 $k0-$k1 $gp $sp $fp Numero 0 1 2-3 4-7 8-15 16-23 24-25 26-27 28 29 30 31 Utilizzo Costante 0 Riservato per l assemblatore Valori di ritorno da una procedura Argomenti di una procedura Registri temporanei (non preservati) Registri salvati (preservati) Registri temporanei (non preservati) Riservati al kernel del S.O. per la gestione delle eccezioni Global pointer (puntatore all area globale di memoria) Stack pointer Frame pointer Indirizzo di ritorno Valeria Cardellini 22 Valeria Cardellini 23
Esempio: copia parziale di un vettore Scrivere una procedura in assembler MIPS che effettua la copia dei soli elementi positivi del vettore di interi elem nel vettore pos_elem; la procedura restituisce il numero di byte del vettore pos_elem Assumiamo che $a0 contenga l indirizzo base dell array elem $a1 contenga l indirizzo base dell array pos_elem $a2 contenga il numero di byte dell array elem Vedere cp_pos.s per esempio completo con main Valeria Cardellini 24 Esempio: copia parziale di un vettore (2) cp_pos:addi $sp, $sp, -4 sw $a1, 0($sp) # salva l indirizzo di pos_elem add $t0, $a0, $a2 # $t0 = ind. ultimo byte di $a0 next: beq $t0, $a0, exit lw $t1, 0($a0) addi $a0, $a0, 4 blez $t1, next # se $t1 <= 0, vai a next sw $t1, 0($a1) addi $a1, $a1, 4 j next exit: lw $t1, 0($sp) sub $v0, $a1, $t1 # $v0 = num. di interi positivi (in byte) lw $a1, 0($sp) addi $sp, $sp, 4 # dealloca lo stack jr Valeria Cardellini 25