Stored Program • • • • • • Istruzioni sono stringhe di bit Programmi: sequenze di istruzioni Programmi (come i dati) memorizzati in memoria La CPU legge le istruzioni dalla memoria (come i dati) Ciclo macchina (ciclo Fetch – Decode – Execute) • La CPU legge (fetch) l’istruzione corrente (indirizzata da un registro Program Counter = PC), e la pone in un registro speciale interno • La CPU usa i bit dell’istruzione per "controllare" le azioni da svolgere, e su questa base esegue l’istruzione CPU determina la “prossima” istruzione e ripete il ciclo Operandi e memoria • I dati devono essere prelevati dalla memoria prima che le operazioni possano utilizzarli Load word lw $t0, indirizzo-di-memoria • Processore Registro Memoria I dati devono essere immagazzinati in memoria quando necessario Store word sw $t0, indirizzo-di-memoria Processore Registro Memoria Operandi e memoria • Il compilatore organizza i dati del programma nella memoria • Conosce la locazione in memoria di ogni variabile del programma • Per ogni istruzione di load/store genera l’opportuno indirizzo di memoria int a int b int c int d[10] indirizzo base … Operandi immediati • Una istruzione potrebbe richiedere una costante come input • Un’istruzione immediata usa una costante numerica come uno degli input (al posto di un operando registro) addi $s0, $zero, 1000 # l’indirizzo base del programma è 1000 # ed è memorizzato in $s0 immediate # $zero è un registro speciale # contenente sempre 0 addi $s1, $s0, 0 # indirizzo della variabile a addi $s2, $s0, 4 # indirizzo della variabile b addi $s3, $s0, 8 # indirizzo della variabile c addi $s4, $s0, 12 # indirizzo della variabile d[0] Indirizzamento della memoria • Siccome la dimensione minima di una cella di memoria si 1 byte, l’indirizzamento di memoria è “al byte” (indirizzamento al byte) • Nel MIPS (e in molte altre architetture) è obbligatorio che le parole di memoria abbiano indirizzi multipli di 4 (vincolo di allineamento) • Codice C: d[3] = d[2] + a • Assembler: lw $t0, 8($s4) # d[2] è caricato in $t0 lw $t1, 0($s1) # a è caricato in $t1 offset add $t0, $t0, $t1 # la somma è in $t0 sw $t0, 12($s4) # $t0 è scaricato in d[3] Istruzioni Aritmetico/Logiche • • • • • • Le istruzioni aritmetiche del MIPS permettono solo operazioni elementari (add, sub, mult, div) tra coppie di operandi a 32 bit Le istruzioni logiche (and, or) sono bit a bit Tutte le istruzioni hanno 3 operandi L’ordine degli operandi è fisso L’operando destinazione in prima posizione Esempio • Codice C: A = B + C • Codice Assembler: add $t0, $s1, $s2 • Il compilatore associa le variabili ai registri Codifica in formato R-Type esempio add $t0, $s1, $s2 semantica $t0 = $s1 + $s2 codifica 000000 10001 10010 01001 00000 100000 op op: rs rt rd shamt funct operazione base dell’istruzione rs: registro del primo operando rt: registro del secondo operando rd: registro destinazione, che contiene il risultato dell’operazione shamt: utilizzato per istruzioni di shift; posto a zero per le altre istruzioni funct: seleziona la specifica variante dell’operazione base definita nel campo op Istruzioni di trasferimento dati • • • • • • Leggere dalla memoria su registro: lw (load word) Scrivere da registro alla memoria: sw (store word) Esempio: A è un array di numeri interi • Codice C: A[8] += h • Codice Assembler: lw $15, 32($4) add $15, $5, $15 sw $15, 32($4) displacement Indirizzo di memoria &A[8] → $4 + 32 Nota: sw ha la destinazione come ultimo operando Nota: le istruzioni aritmetiche operano su registri, non su celle di memoria. Codifica in formato I-Type esempio lw $t0, 32($s3) semantica $t0 = memory[$s3] + 32 codifica 000000 10010 01001 0000000000100000 op rs: rs rt 16-bit constant registro della locazione di memoria rt: registro destinazione 16-bit constant: operando immediato (displacement) Questo formato codifica anche operazioni aritmetico-logiche con valori immediati, i.e., costanti: addi $t0, $s1, 8 Operazioni Logiche esempio sll $t2, $s0, 4 semantica $t2 = $s0 << 4 codifica 000000 00000 10000 01010 00100 000000 0 0 rt rd shamt 0 Istruzioni di controllo del flusso • • • Il flusso di esecuzione è normalmente sequenziale Le istruzioni di controllo cambiano la prossima istruzione da eseguire Istruzioni di salto condizionato branch if equal beq $s4, $s5, LABEL • Esempio if (i == j) i = i + j; ... • branch if not equal bne $s6, $s5, LABEL bne $s4, $s5, LABEL add $s4, $s4, $s5 LABEL: ... Formato I-Type (operando immediato relativo a PC) Istruzioni di controllo del flusso • Salto non condizionato j LABEL • Esempio if (i != j) h = i + j; else h = i – j; ... • ELSE: EXIT: ... beq $s4, $s5, ELSE add $3, $4, $5 j EXIT sub $3, $4, $5 Formato J-Type op 26-bit constant Istruzioni di controllo del flusso • • • Come facciamo a esprimere “Salta se minore o uguale di”? In MIPS c’è un’istruzione aritmetica, set-on-less-than Istruzione Significato slt $t1, $s4, $s5 if ($s4 < $s5) $t1 = 1; else $t1 = 0; Formato R-Type / I-Type (versione immediata) Esempio: Ciclo Tradurre il seguente codice C in codice assembly: while (save[i] == k) i += 1; i e j sono, rispettivamente, in $s3 e $s5 and la base dell’array save è in $s6 Loop: sll Exit: $t1, $s3, 2 # Moltiplico i per 4 add $t1, $t1, $s6 # Calcolo l’indirizzo di save[i] lw $t0, 0($t1) bne $t0, $s5, Exit # Condizione di uscita dal ciclo addi $s3, $s3, 1 # Corpo del ciclo j Loop # Ricomincio… # Carico save[i] in $t0 Procedure Per eseguire una procedura (detta callee, chiamato), un programma (detto caller, chiamante) deve eseguire 6 passi: 1. mettere i parametri in un luogo accessibile alla procedura 2. trasferire il controllo alla procedura 3. acquisire le risorse necessarie per l’esecuzione della procedura 4. eseguire il compito richiesto 5. mettere il risultato in un luogo accessibile al chiamante 6. restituire il controllo al chiamante Registri MIPS I 32 registri MIPS sono partizionati come segue: • Reg 0 : $zero memorizza sempre la costante 0 • Regs 2-3 : $v0, $v1 valori di ritorno di una procedura • Regs 4-7 : $a0-$a3 valori di ingresso di una procedura • Regs 8-15 : $t0-$t7 registri temporanei • Regs 16-23 : $s0-$s7 registri variabili • Regs 24-25 : $t8-$t9 altri registri temporanei • Reg 28 : $gp global pointer • Reg 29 : $sp stack pointer • Reg 30 : $fp frame pointer • Reg 31 : $ra return address Jump-and-Link • Un registro speciale contiene l’indirizzo dell’istruzione correntemente eseguita – questo registro è il program counter (PC) • L’invocazione della procedura è eseguita invocando l’istruzione jump-andlink (jal) • Il valore corrente di PC (in realtà PC+4) è salvato nel registro $ra • Si salta all’indirizzo iniziale della procedura specificato come parametro dell’istruzione jal • Poiché jal potrebbe sovrascrivere un valore pre-esistente di $ra, questo deve essere salvato in memoria prima di invocare l’istruzione jal • Tramite l’istruzione jump-register $ra si ripristina il flusso di controllo del chiamante al termine della procedura • Cosa succede se i registri $a0-$a3 e $v0, $v1 non sono sufficienti? Stack • Bisogna copiare il contenuto dei registri in memoria • La struttura dati utilizzati è lo stack (pila), una coda di tipo LIFO (lastin first-out) utilizzata per salvare il contenuto dei registri • Per ragioni storiche, lo stack “cresce” verso indirizzi di memoria bassi • L’indirizzo dell’elemento dello stack allocato più di recente è memorizzato nello stack pointer ($sp) • • Inserendo dati nello stack (push), il valore di $sp diminuisce • Estraendo dati dallo stack (pop), il valore di $sp aumenta Per convenzione, il contenuto dei registri $s0-$s7 deve essere salvato sullo stack prima dell’invocazione di una procedura Gestione dei dati dello stack • Una nuova procedura deve creare spazio per tutte le sue variabili sullo stack • Prima di eseguire la jal, il chiamante deve salvare i valori necessari dei registri $s0-$s7, $a0-$a3 e $ra nel suo stack • Gli argomenti sono copiati in $a0-$a3 e l’istruzione jal è eseguita • Il chiamato può creare il suo spazio stack, aggiornando il valore di $sp • Al termine dell’invocazione, il chiamato copia il valore di ritorno in $v0 e libera lo stack se lo ha occupato • Il chiamante può recuperare dallo stack i suoi valori precedentemente salvati ($s0-$s7, $a0-$a3 e $ra) Esempio int leaf_example (int g, int h, int i, int j) { int f; f = (g + h) – (i + j); return f; } indirizzi alti $sp indirizzi bassi Memoria leaf_example: addi $sp, $sp, -4 sw $s0, 0($sp) add $t0, $a0, $a1 add $t1, $a2, $a3 sub $s0, $t0, $t1 add $v0, $s0, $zero lw $s0, 0($sp) addi $sp, $sp, 4 jr $ra Nuovi dati delle procedure Codici ASCII • load byte: lb $t0, 0($sp) • store byte: sb $t0, 0($sp) Esempio void strcpy(char x[], char y[]) { int i; i=0; while ((x[i] = y[i]) != `\0’) i += 1; } strcpy: addi sw add L1: add lb add sb beq addi j L2: lw addi jr $sp, $sp, -4 $s0, 0($sp) $s0, $zero, $zero $t1, $s0, $a1 $t2, 0($t1) $t3, $s0, $a0 $t2, 0($t3) $t2, $zero, L2 $s0, $s0, 1 L1 $s0, 0($sp) $sp, $sp, 4 $ra Grandi costanti • Le istruzioni immediate possono specificare solo costanti su 16 bit • L’istruzione lui è usata per copiare costanti a 16 bit nei 16 bit più significativi di un registro • Combinando questa istruzione con un ori (OR immediato) possiamo copiare costanti a 32 bit Grandi costanti • Le istruzioni immediate possono specificare solo costanti su 16 bit • L’istruzione lui è usata per copiare costanti a 16 bit nei 16 bit più significativi di un registro • Combinando questa istruzione con un ori (OR immediato) possiamo copiare costanti a 32 bit • Nei salti condizionati (beq, bne) l’indirizzo di salto (nuovo valore di PC) è una costante a 16 bit, relativa al valore corrente di PC • Nel salto (j) l’indirizzo di salto è una costante a 26 bit, di valore assoluto • Se necessario, possiamo usare il contenuto di un altro registro come base Metodi di indirizzamento del MIPS 1. Indirizzamento immediato: l’operando è una costante contenuta nell’istruzione 2. Indirizzamento tramite registro: l’operando è in un registro 3. Indirizzamento tramite base e spiazzamento: l’operando è in una cella di memoria individuata sommando il contenuto di un registro con una costante contenuta nell’istruzione 4. Indirizzamento relativo a PC: l’indirizzo di salto è ottenuto sommando il contenuto di PC con una costante contenuta nell’istruzione 5. Indirizzamento pseudodiretto: l’indirizzo di salto è ottenuto concatenando i 26 bit nell’istruzione con i 4 bit più significativi di PC