1
Fork 0
mirror of https://github.com/Steffo99/appunti-magistrali.git synced 2024-11-24 03:04:18 +00:00

Importa Architettura dei calcolatori da Steffo99/appunti-universitari

This commit is contained in:
Steffo 2024-06-04 10:38:17 +02:00
parent a8b8ad3a87
commit 2e748ea187
Signed by: steffo
GPG key ID: 5ADA3868646C3FC0
18 changed files with 684 additions and 0 deletions

View file

@ -0,0 +1,7 @@
#!/bin/bash
# Assemble RISV-V assembly (.s) files
riscv64-unknown-elf-as -g -o "$1.o" "$1.riscv"
# Link RISC-V object (.o) files
riscv64-unknown-elf-ld -o "$1.elf" "$1.o"
exit $?

View file

@ -0,0 +1,4 @@
#!/bin/bash
riscv64-unknown-elf-objdump "$1.elf"
exit $?

View file

@ -0,0 +1,5 @@
#!/bin/bash
# Compile using g++
riscv64-unknown-elf-g++ -o "$1.elf" "$1.cc"
exit $?

View file

@ -0,0 +1,5 @@
#!/bin/bash
# Compile using g++
riscv64-unknown-elf-gcc -o "$1.elf" "$1.cc"
exit $?

View file

@ -0,0 +1,4 @@
#!/bin/bash
riscv64-unknown-elf-gprof "$1.elf"
exit $?

View file

@ -0,0 +1,4 @@
#!/bin/bash
riscv64-unknown-elf-readelf "$1.elf" -a
exit $?

View file

@ -0,0 +1,17 @@
#!/bin/bash
# Sintassi: ./run.sh nomefile
# Non mettete l'estensione!
# Potrebbe esserci bisogno di installare xterm prima
# Se non funziona, installatelo con
# sudo apt install xterm
echo "Cleaning phase"
rm "$1.o"
rm "$1.elf"
echo "Compilation phase"
riscv64-unknown-elf-as -g -o "$1.o" "$1.riscv"
riscv64-unknown-elf-ld -o "$1.elf" "$1.o"
echo "Debugging phase"
xterm -e "qemu-riscv64 -g 1234 \"$1.elf\"" &
xterm -e "riscv64-unknown-elf-gdb --ex=\"target remote localhost:1234\" \"$1.elf\"" &
wait

View file

@ -0,0 +1,24 @@
# Architettura dei Calcolatori
Docente: [**Andrea Mariongiu**](mailto:andrea.mariongiu@unimore.it)
Crediti: **9 CFU** (72 ore di lezione)
### Materiale
Libri:
- `ISBN9780128122761`
- `ISBN9788871924618`
**[Sito web](http://algo.ing.unimo.it/people/andrea/Didattica/Architetture/index.html)**
[Slides 2017](http://dolly.fim.unimore.it/2017/course/view.php?id=57)
### Esame
- Prova scritta
- Serie di domande a risposta singola, multipla e libera, simile a Programmazione 1
- Rimane valida fino alla fine della sessione seguente
- Prova orale
- Può essere sostituita con lo sviluppo di un _progettino_

View file

@ -0,0 +1,47 @@
# I principi dell'architettura dei calcolatori
## Gli otto grandi principi
- Progetta per la **Legge di Moore**
- **Astrai** per semplificare il design
- Velocizza i **casi più comuni**
- Sfrutta il **parallelismo**
- Instruction level parallelism
- In una sola CPU vengono realizzati più calcoli in parallelo
- Sfrutta le **pipeline**
- Pipelining
- Separare le azioni del processore in fasi
- Il fetch è separato dal decoding, mentre decodifico un'istruzione posso già fetchare quell'altra
- **Prevedi** le istruzioni successive
- **Gerarchizza** le memorie per velocità di accesso
- La CPU cerca sempre di accedere alle memorie più veloci
- Rendi affidabile con la **ridondanza**
### Parallelismo
> Chiedo a 100 persone di fare una moltiplicazione ciascuna invece che fare 100 moltiplicazioni io da solo
### Pipelining
![Senza](https://upload.wikimedia.org/wikipedia/commons/2/2c/Nopipeline.png)
![Con](https://upload.wikimedia.org/wikipedia/commons/2/21/Fivestagespipeline.png)
### Gerarchia delle memorie
> Hit or miss? I hope they never miss.
La CPU prova sempre ad accedere alle memorie più in alto di questa lista, effettuando un **hit** se trova i dati che le servono in una data memoria ed effettuando un **miss** se invece non trova i dati necessari e deve accedere a uno strato inferiore.
- [Cache](https://it.wikipedia.org/wiki/Cache) (piccolissima e velocissima)
- [Random Access Memory](https://it.wikipedia.org/wiki/RAM) (piccola e veloce)
- [Solid State Drive]() (medio e di media velocità)
- [Hard Disk Drive](https://it.wikipedia.org/wiki/Disco_rigido) (grosso, ma lento)
- [Tape storage](https://en.wikipedia.org/wiki/Magnetic_tape_data_storage) (enorme, ma lentissimo)
I _miss_ rallentano l'esecuzione dei programmi, in quanto causano ritardi nel ritrovamento dei dati, passando da _meno di 1 µs_ per le cache a _più di 1 ms_ per gli hard disk.
Inoltre, esistono due tipi di cache, anch'essi gerarchizzati:
- [SRAM](https://it.wikipedia.org/wiki/SRAM)
- [DRAM](https://it.wikipedia.org/wiki/DRAM)

View file

@ -0,0 +1,41 @@
# Dentro il processore
## Come sono fatti i circuiti integrati?
Sono composti da un layer di silicio (semiconduttore), più delle maschere di conduttori (µfili metallici), isolanti (plastica e vetro) e transistor.
### Processo di produzione
- Lingotto di silicio
- Tagliato in _wafer_ da 2mm
- Creazione dei _patterned wafer_
- Testing dei _patterned wafer_
- Taglio i _tested wafers_
- Creo un package con i _tested dies_
- Testo i _packaged dies_
- Spedisco i _tested packaged dies_ ai clienti.
### Lo yield
Lo yield è quanti _tested packaged dies_ riesco a produrre da un singolo lingotto di silicio.
## Come definiamo la performance di un processore?
Ci sono varie metriche per definirla:
- **Response time**: tempo impiegato a compiere un'operazione
- **Throughput**: operazioni fatte in un'unità di tempo
### Il clock
Il clock della CPU è il timer che scandisce le operazioni della CPU.
Ha, ovviamente, un periodo e una frequenza costanti.
### Il CPU time
Il tempo impiegato da una CPU a compiere una determinata operazione è dato da `numero di cicli di clock richiesti * periodo di clock`, ed è detto **CPU time**.
### Il Cost per instruction (CPI)
Alcune istruzioni di una CPU potrebbero richiedere più di un ciclo di clock.

View file

@ -0,0 +1,165 @@
# Assembly
Useremo il RISC-V.
Esso è little-endian, ed è indirizzato a gruppi di 8 bit.
## Registri
RISC-V ha a disposizione **32** registri da 64 bit, che vanno da `x0` a `x31`.
Un dato a 32 bit viene chiamato **word**, uno a 64 bit viene chiamato **doubleword**.
- `x0`: costante
- `x1`: indirizzo risultato
- `x2`: indirizzo della cima dello stack
- `x3`: indirizzo dell'area di memoria con variabili globali
- `x4`: indirizzo dell'area di memoria specifica del thread attivo
- `x5`: registro temporaneo
- `x6`: registro temporaneo
- `x7`: registro temporaneo
- `x8`: frame pointer
- `x9`: registro salvato, può essere copiato in memoria
- `x10`: argomento o risultato delle funzioni
- `x11`: argomento o risultato delle funzioni
- `x12`: argomento delle funzioni
- `x13`: argomento delle funzioni
- `x14`: argomento delle funzioni
- `x15`: argomento delle funzioni
- `x16`: argomento delle funzioni
- `x17`: argomento delle funzioni
- `x18`: registro salvato, può essere copiato in memoria
- `x19`: registro salvato, può essere copiato in memoria
- `x20`: registro salvato, può essere copiato in memoria
- `x21`: registro salvato, può essere copiato in memoria
- `x22`: registro salvato, può essere copiato in memoria
- `x23`: registro salvato, può essere copiato in memoria
- `x24`: registro salvato, può essere copiato in memoria
- `x25`: registro salvato, può essere copiato in memoria
- `x26`: registro salvato, può essere copiato in memoria
- `x27`: registro salvato, può essere copiato in memoria
- `x28`: registro temporaneo
- `x29`: registro temporaneo
- `x30`: registro temporaneo
- `x31`: registro temporaneo
## Operazioni
### Operazioni aritmetiche
Sono sempre formate da tre operandi: registro a cui il risultato verrà assegnato e gli argomenti dell'operazione.
- `add`: addizione
- `sub`: sottrazione
- `addi`: addizione di costanti
#### Esempio
```assembly
add a, b, c
```
##### Equivalente Python
```python
a = b + c
```
### Operazioni di trasferimento dati
Permettono di caricre / scaricare dati dalla memoria.
### Operazioni logiche
Consentono di effettuare operazioni di logica.
- `and`
- `or`
- ...
### Operazioni di shift
Consentono di spostare i bit di un registro di alcune posizioni, moltiplicando o dividendo effettivamente per potenze di 2.
### Operazioni condizionali
Consentono di saltare ad un'altra serie di istruzioni se certe condizioni si verificano.
- `blt`: minore di, signed
- `bge`: maggiore di, signed
- `bltu`: minore di, unsigned
- `bgeu`: maggiore di, unsigned
### Operazioni non-condizionali
Consentono di saltare ad un'altra serie di istruzioni in qualunque caso.
#### Chiamare una procedura
> `jal x1, ProcedureLabel`
Salva l'indirizzo dell'istruzione successiva in x1, poi salta a ProcedureLabel.
#### Ritornare da una procedura
> `jalr x0, 0(x1)`
Ritorna all'indirizzo puntato da `x1`.
## Memoria
Per accedere a una posizione in memoria, si usa `offset(indirizzo)`.
##### Esempio
`64(x22)` accede al 64° byte dopo la posizione di memoria archiviata in `x22`.
## Segni
E' possibile usare rappresentazione standard o a complemento-a-2.
Possiamo usare la **sign extension** quando abbiamo un dato che vogliamo portare a un numero di bit maggiore di quello che è.
##### Esempio
```
[0]101 0101 --> 0000 0000 [0]101 0101
[1]100 0000 --> 1111 1111 [1]100 0000
```
### Operazioni di sincronizzazione
- `lr`: load reserved
- `sc`: store conditional
#### Load reserved
```
lr rd, (rs1)
```
> Non ho la minima idea di come funzioni.
Controlla se `rs1` è bloccato; se sì, mette un valore != 0 in `rd`...?
> Forse... crea un lock in `rd`?
#### Store conditional
```
sc rd, rs2, (rs1)
```
> Non ho la minima idea di come funzioni.
Copia il dato da `rs2` a `rs1`, se non è bloccato, e metti `x0` in `rd`...?
> Carica un dato... se ho ancora il lock, altrimenti, metti `x0` in `rd`.
#### Unlock
```
sd x0, 0(x20)
```
## Operazioni floating point

View file

@ -0,0 +1,29 @@
# Definisci delle costanti
.equ _SYS_EXIT, 93 # Syscall exit
.equ _SYS_WRITE, 64 # Syscall write
# Inizio del programma
.global _start
# Variabili statichie inizializzate a 0
.section .bss
# Dati modificabili
.section .data
# Dati in sola lettura
.section .rodata
msg: .string "Hello mondo!\n"
# Testo del programma
.section .text
_start:
# Chiamata a WRITE(stream, primocarattere, lunghezza)
li a0, 0 # Print to stdout
la a1, msg # Stampa il messaggio in msg
li a2, 13 # Stampa 13 caratteri dopo msg
li a7, _SYS_WRITE # Seleziona la syscall WRITE
ecall
li a7, _SYS_EXIT
ecall

View file

@ -0,0 +1,33 @@
# Che bello! Adoro l'assembly!
# Questo programma funziona, è verificato!
.global _start
# La sezione .data contiene i dati del programma
.section .data
# Creo una variabile con etichetta "ciao"
ciao: .string "0\n\0"
# La sezione .rodata contiene i dati del programma in sola lettura
.section .rodata
# La sezione .text contiene il testo del programma
.section .text
# Creo una label _start che funzionerà da entrypoint
_start:
li t1, 10 # Voglio ripetere il ciclo 10 volte
li a7, 64 # PRINT(stream, indirizzo, numerobytes) è la syscall 64
li a0, 0 # stream è il primo argomento: vogliamo scrivere su stdout (0)
la a1, ciao # indirizzo è il secondo argomento: partiamo dall'etichetta ciao
li a2, 2 # numerobytes è il terzo argomento: vogliamo stampare 2 bytes
print:
ecall # PRINT(a0, a1, a2)
lbu t0, 0(a1) # Carica il byte con il 0
addi t0, t0, 1 # Aggiungici 1
sb t0, 0(a1) # Ricaricalo in memoria
addi t1, t1, -1 # Togli 1 dal numero di cicli rimasti
bne t1, zero, print # Se rimangono dei cicli, torna indietro e falli!
end:
li a7, 93 # Tutti i programmi terminano chiamando la syscall 93, EXIT()
ecall # EXIT()

View file

@ -0,0 +1,40 @@
# Che bello! Adoro l'assembly!
# Funziona!
.global _start
# La sezione .data contiene i dati del programma
.section .data
# La sezione .rodata contiene i dati del programma in sola lettura
.section .rodata
# La sezione .text contiene il testo del programma
.section .text
# Creo una label _start che funzionerà da entrypoint
_start:
li s0, 5 # Scrivi il numero 5
li s3, 3 # COSTANTE: 3
# Pushing phase
pushing:
sb s0, 0(sp) # Metti nello stack s0
addi sp, sp, 1 # Aumenta di 1 lo stack pointer
addi s2, s2, 1 # Aumenta di 1 la dimensione dello stack
addi s0, s0, -1 # Diminuisci di 1 il numero da fattorializzare
bgeu s0, s3, pushing # Continua a pushare finchè non arrivi a 2
li s1, 2
# Popping phase
popping:
addi sp, sp, -1 # Diminuisci di 1 lo stack pointer
# Attenzione: diminuisco lo stack pointer PRIMA di caricare dallo stack, altrimenti caricarei il nulla
lb s0, 0(sp) # Prendi dallo stack il prossimo numero
addi s2, s2, -1 # Diminuisci di 1 la dimensione dello stack
mul s1, s1, s0 # Effettua la moltiplicazione
bne s2, zero, popping # Se ci sono altri valori nello stack, continua a moltiplicare
# Ending phase
ending:
li a7, 93
ecall

View file

@ -0,0 +1,48 @@
# Che bello! Adoro l'assembly!
# Ommioddiofunzionawowimawizard
.global _start
# La sezione .data contiene i dati del programma
.section .data
primo: .word 1, 2, 3, 4, 5
secondo: .word 1, 2, 3, 4, 5
# La sezione .rodata contiene i dati del programma in sola lettura
.section .rodata
# La sezione .text contiene il testo del programma
.section .text
# s0: primo[s0]
# s1: secondo[s0]
# s2: monomio
# s3: indirizzo da cui prendere il valore
# s4: indice
# s5: dimensione array
# a0: somma
_start:
li s2, 0
li a0, 0
li s4, 0
li s5, 5
somma:
la s3, primo
add s3, s3, s4
lw s0, 0(s3)
la s3, secondo
add s3, s3, s4
lw s1, 0(s3)
mulw s2, s1, s0
add a0, a0, s2
addi s4, s4, 4
addi s5, s5, -1
bne zero, s5, somma
_end:
li a7, 93
ecall

View file

@ -0,0 +1,57 @@
# Appunti
## Registri
I registri sono divisi in tre tipi: di **proprietà del chiamante**, di **proprietà del chiamato** (anche se in realtà è solo una convenzione) e... gli **altri**.
Tutti i registri sono grandi 64 bit: possono quindi contenere una _doubleword_.
### Proprietà del chiamante
Questi registri **non devono essere sovrascritti** quando viene chiamata una funzione, o se vengono sovrascritti, **devono essere ripristinati** prima che la funzione restituisca.
Essi sono:
- `s1..s11`: **Saved** registers, registri generici che possono essere usati per qualsiasi cosa.
- `sp`: **Stack Pointer**, il primo indirizzo dello stack disponibile per la scrittura. Va _decrementato_ quando si aggiunge qualcosa allo stack.
- `fp`: **Frame Pointer**, punta all'inizio dello stack.
### Proprietà del chiamato
Questi registri **possono essere sovrascritti** quando viene chiamata una funzione: bisogna salvarli in memoria prima di chiamarla!
Essi sono:
- `t0..t6`: **Temporary** registers, registri generici che possono essere usati per qualsiasi cosa.
- `ra`: **Return Address**, l'indirizzo a cui continuare l'esecuzione del programma dopo aver terminato una funzione. _Solitamente si usa solo con `jal` e `jalr`!_
- `a0..a7`: Function **Arguments**. Se all'interno di una funzione, contengono gli argomenti che sono stati passati. Se all'esterno di una funzione, contengono i valori restituiti dall'ultima funzione chiamata.
### Altri
- `zero`: **Zero**, registro che vale sempre 0, anche dopo essere stato sovrascritto da altro. Come `/dev/null` su Linux.
- `gp`: **Global Pointer**, punta a un indirizzo _[(?)](https://groups.google.com/a/groups.riscv.org/forum/#!topic/sw-dev/60IdaZj27dY)_
- `tp`: **Thread Pointer**, punta al thread-local storage _[(?)](https://groups.google.com/a/groups.riscv.org/d/msg/sw-dev/cov47bNy5gY/zLnlKkw9CQAJ)_
- `pc`: **Program Counter**, punta all'istruzione che sta venendo eseguita.
## Comandi preprocessore
Scrivendo file assembly per RISC-V, è possibile aggiungere direttive per il preprocessore.
Per ora ho scoperto queste:
- `.global INDIRIZZO`: definisce l'indirizzo a cui inizia il programma. _(Forse setta il `gp` a quell'indirizzo?)_
- `.equ NOME, VALORE`: definisce una costante con uno specifico nome. _Il nome delle costanti dovrebbe iniziare sempre con un underscore per convenzione._
- `.section [.data|.rodata|.bss|.text]`: marca l'inizio di una specifica sezione del programma. Le sezioni sono:
- `.data`: Variabili del programma
- `.rodata`: Variabili sola lettura del programma
- `[.bss](https://en.wikipedia.org/wiki/.bss)`: Variabili statiche non costanti non inizializzate a nessun valore. _A volte, vengono inizializzate a 0. Solo a volte. Dipende dall'OS._
- `.text`: Il testo del programma, con le istruzioni in assembly.
- `.TIPOVARIABILE`: crea una variabile di quel tipo e la inizializza a qualcosa.
# Link utili
- [algo.ing.unimo.it](http://algo.ing.unimo.it/people/andrea/Didattica/Architetture/index.html)
- [wikichip.org](https://en.wikichip.org/wiki/risc-v)
- [en.wikiversity.org](https://en.wikiversity.org/wiki/Computer_architecture)
- [it.wikiversity.org](https://it.wikiversity.org/wiki/Materia:Architetture_degli_elaboratori)
- [stackoverflow.com](https://stackoverflow.com/questions/tagged/riscv)
- [softwareengineering.stackexchange.com](https://softwareengineering.stackexchange.com/questions/tagged/assembly)
- [rv8.io](https://rv8.io/isa.html)

View file

@ -0,0 +1,133 @@
# Memorie
La _memoria_ di un calcolatore solitamente è composta da molteplici strati, realizzati per velocizzare gli accessi.
Ogni strato offre lettura più veloce, ma è più costoso da realizzare e ha consumi più alti.
In ordine di velocità, sono:
- Registers
- Cache L1
- Cache L2
- Cache L`X`
- Random Access Memory
- Solid State Drive
- Hard Disk Drive
## Cache
### Cache L1
Piccola, ma miss penalty bassa
### Cache L2
Molto grande, ma miss penalty alta
### Direct mapping
Una cache _direct mapped_ associa a ogni indirizzo di RAM un **indice** uguale agli **N bit meno significativi** dell'indirizzo dell'inferiore, e un **tag** uguale agli **N bit più significativi**.
Se durante un accesso _il tag di un blocco è diverso dal tag dell'indirizzo a cui vogliamo accedere_, si ha un cache miss.
> Il dato di cui abbiamo bisogno è contenuto nell'indirizzo `0x0ABC` della RAM.
> Il suo indice sarà `0xBC`, e il suo tag sarà `0x0A`.
> Essendo la cache vuota all'inizio, viene immediatamente caricato.
>
> Voglio poi accedere all'indirizzo `0x0BBC` della RAM.
> Controlliamo il blocco con indice `0xBC`: il suo tag è `0x0A`, ma noi stiamo cercando `0x0B`! Si ha quindi un cache miss, e devo andare a prendere dalla memoria il dato che sto cercando e scriverlo sulla RAM.
Inoltre, in ogni blocco di memoria della cache è presente **un bit di validità**, che rappresenta se il dato in cache è stato inizializzato o no: parte da `0` e viene impostato a `1` quando viene caricato dalla RAM un dato nel relativo blocco.
> Un esempio potrebbe essere un processore con blocchi di memoria da 32 bit e indirizzi a 64 bit: la cache, contenente 1024 blocchi di memoria, avrà index a 12 bit, e di conseguenza il tag sarà grande 52 bit.
#### Dimensione blocchi
Fare blocchi grandi o fare blocchi piccoli ha significative differenze sulla velocità della cache:
Avere blocchi più grandi significa che ci saranno meno blocchi in tutta la cache, quindi:
```diff
+ Riduce il miss rate per il principio di località spaziale
- Avendo una quantità minore di blocchi, aumenta la possibilità di conflitto
- La miss penalty è più alta
# La miss penalty è compensabile con tecniche come early restart o critical-word-first
```
Con blocchi più piccoli, invece
```diff
+ Miss penalty minore
+ Più blocchi significa meno conflitti
- E' possibile che abbia bisogno di fare più di una richiesta alla cache, rallentandola significativamente
```
#### Scrittura tramite cache
Quando scrivo in memoria un dato presente nella cache, l'informazione presente nella cache diventa errata.
Si può risolvere questo problema con una politica di riscrittura:
- Write-through
- Write-back
##### Write-through
_Quando **viene scritto** su dato in cache_, aggiorna tutte le memorie che lo contengono.
```diff
+ Non necessita di ulteriore memoria di cache
+ Caricare dati è veloce
+ Se si verifica un write miss non ha per forza bisogno di portare in cache il dato da scrivere
- I write richiedono molto più tempo, soprattutto se ripetuti
```
##### Write-back
Scrivi la modifica di dato solo nella cache, e _marca il blocco come **dirty**_.
Quando un blocco dirty viene **sovrascritto**, aggiorna le memorie che lo contengono.
```diff
+ I write richiedono poco tempo, anche se ripetuti
- Caricare dati dalla RAM è più lento
- Necessita di memoria da dedicare al dirty bit
- Devo fetchare obbligatoriamente i dati da sovrascrivere
```
##### Miglioramenti alle policy
E' possibile utilizzare un **write buffer** invece che fare attendere il tempo di scrittura alla CPU. I dati saranno scritti successivamente, ma prima che questi vengano utilizzati.
```diff
+ Il processore non ha bisogno di fermarsi per le write
- Il buffer potrebbe riempirsi, neganode i vantaggi
- Il buffer utilizza memoria che forse sarebbe stata più utile come cache
```
### Fully associative
Ogni dato può essere messo in qualunque indirizzo della cache.
Richiede che l'indirizzo di origine venga salvato assieme al dato, e tanti comparatori.
### Set associative
Divido tutti i blocchi di cache in vie.
Ogni via può contenere `n` dati.
Un set è l'insieme dei dati che hanno lo stesso index ma sono in vie diverse.
Ogni dato può essere messo in qualunque indirizzo del set a cui appartiene.
Identifico il numero di set di appartenenza facendo `indirizzo % numerodiset`.
Una cache `1`-way Set Associative è una cache Direct Mapped, mentre una cache `numeroentries`-way Set Associative è una cache Fully Associative.
Quando non c'è spazio in un set, rimpiazzo un dato secondo la politica **Least Recently Used**, rimuovendo il dato usato meno recentemente. Posso usare anche la politica **Random**, se ho un'alta associatività.
#### Performance
Più una cache è associativa, più il miss rate sarà ridotto, ma l'associatività richiede un maggior numero di comparatori e potenzialmente più ritardi nella restituzione del dato.
Inoltre, la percentuale di miss non diminuisce linearmente con il numero di vie: dopo un certo numero di vie, i guadagni sono molto ridotti.

View file

@ -0,0 +1,21 @@
# Meltdown
Esempio circa comprensibile:
```c++
int meltdown() {
bool array[2] = {false, true};
if( qualcosa_che_restituisce_false() ) {
//Questo viene predetto
//Durante le predizioni non ci sono segfault!
int protetto = *(indirizzo_bersaglio);
//Se il primo bit di protetto è 1, verrà caricato "true" in cache, altrimenti, verrà caricato "false", ANCHE SE E' SOLO UNA PREDIZIONE!
//Dopo essere stato caricato, NON VIENE RIMOSSO, anche se la predizione è sbagliata.
int caricato = array[protetto & 1];
}
if( in_cache(true) ) cout << "Il primo bit del bersaglio è 1.\n";
else cout << "Il primo bit del bersaglio è 0.\n";
}
```