L'istruzione load
copia un dato dalla memoria ad un registro.
L'indirizzo del dato viene specificato da:
Invece l'istruzione ld
(load double word) carica un double word dalla memoria ad un registro:
ld x5, 24(x21)
viene caricata una double word nel registro x5, presa a partire dall'indirizzo scritto sul registro x21 con offset 24.
Questa istruzione corrisponde alla seguente in linguaggio C:
long a;
long v[10];
// altro codice
a = v[3];
- x5 è il registro di a
- x21 è il registro dove è scritto l'indirizzo in memoria del vettore della posizione 0
- 24 è l'offset
Dal momento che nel registro x21 è salvato l'indirizzo della cella iniziale del vettore, per raggiungere la posizione 3 (v[3]
) bisogna scorrere l'array di un certo numero di byte.
Essendo l'array di tipo long
ogni posizione dell'array è di 8 byte (1 double word), quindi dovendo raggiungere la posizione 3, bisogna moltiplicare
L'accesso alla memoria può essere effettuato anche con un altro orientamento:
ld x5, -24(x21)
L'istruzione store
copia un dato da un registro alla memoria
store x5, 16(x21)
Salva nella posizione v[2]
il valore presente nel registro x5
Un esempio di accesso alla memoria:
Da notare in questa immagine l'array con 8 posizioni, numerato da 0 a 7, da destra verso sinistra. Questo ci fra capire che RISC-V è un architettura che usa lo standard little endian per rappresentare dati in memoria. Ogni posizione della memoria è di 64 bit, ovvero di 8 byte. Un dato scritto in memoria ha il bit meno significativo a destra mentre il bit più significativo è a sinistra.
Sempre per quanto riguarda la memoria e in particolare i suoi indirizzi è doveroso precisare che RISC-V consente il caricamento in memoria e il prelevamento di un dato dalla memoria non solo dell'ordine delle double word. Infatti, è possibile usare load
e store
usando anche byte, half-word e word.
|Nome|Dimensione|Istruzioni load/store|
|--|--|--|
|byte|1 byte, 8 bit|lb, lbu, sb
|half-word|2 byte, 16 bit|lh, lhu, sh
|word|4 byte, 32 bit|lw, lwu, sw
|double word|8 byte, 64 bit|ld, sd
Gli operatori lb, sb indicano load byte, store byte. Stessa cosa vale per lh, sh, quindi, load half-word, store half-word. E così via.
Gli operatori che contengono la lettera u sono utilizzate se non si vuole effettuare l'estensione del segno. Infatti, quando viene caricato un dato in memoria, se esso non un dato di 8 byte, viene effettuato in automatico l'estensione del segno, se questo lo si vuole evitare, si possono utilizzare gli operatori lbu, lhu, lwu.
A questo punto bisogna porre l'accento sull'allineamento degli indirizzi.
Il caricamento di una double word occupa per intero una cella della memoria. Tuttavia, essendo possibile caricare anche dati che non sono di 8 byte, si potrebbero creare delle situazioni in cui la memoria è frammentata in modo non allineato.
Se si specifica un indirizzo non allineato rispetto a quanto l'istruzione desidera, il RISC-V impiegherà un tempo per l'accesso maggiore.
Questo vuol dire che se voglio caricare una word che è 4 byte, se ho un array di 2 posizioni (ciascuna di 32 bit) per caricare i dati in determinate celle della memoria utilizzerò dei multipli di 4.
Infatti, se l'indirizzo base di questo array è x21.
La posizione 0 del vettore sarà (0)x21: offset 0, indirizzo base x21.
Mentre la posizione 1 del vettore sarà (4)x21: offset di 4 byte (ovvero dopo lo spazio per la posizione 0 dell'array), indirizzo base 21.
Stessa cosa vale per tutte le altre dimensioni di dati.
Se ho un array con 2 posizioni (ciascuna di 16 bit) per caricare, se l'array ha un indirizzo base x21, farò:
Posizione 0: (0)x21
Posizione 1: (2)x21
Dal momento che 16 bit sono 2 byte.
In C le dimensioni dei dati della memoria, sono correlati al tipo di dato:
|Dimensione|Tipo in C|
|--|--|
|8 byte|long|
|4 byte|int|
|2 byte|short|
|1 byte|char|
Secondo alcuni benchmark (SPEC CPU 2006) in più della metà delle operazioni aritmetiche, uno degli operandi dell'operazione è un valore costante:
Ad esempio la somma:
b = b + 5
ld x9, indirizzoDellaCostante5 (x3)
add x5, x5, x9
ld x9, indirizzoDellaCostante5 (x3)
add x5, x5, x9
Ad esempio la somma:
b = b + 5
ld x9, indirizzoDellaCostante5 (x3)
add x5, x5, x9
=== end-multi-column
In x9 viene caricato il valore costante 5 preso dal registro x3, poi viene aggiunto a x5 il suo stesso valore aumentato di x9 (5).
Un alternativa: istruzioni aritmetiche in cui uno degli operandi è una costante. L'istruzione di somma immediata è chiamata addi
(add immediate), l'esempio di prima
b = b + 5
addi x5, x5, 5
b = b + 5
--- column-end ---
addi x5, x5, 5
=== end-multi-column
La sottrazione immediate non esiste, si usano le costanti con valore negativo.