Allocazione dei registri - WinDizio

Transcript

Allocazione dei registri - WinDizio
http://www.windizio.altervista.org/appunti/
File distribuito con licenza Creative Commons BY­NC­SA 2.5 IT
Copyright © 2007 ­ Michele Tartara
Parte I
Allocazione dei registri
Il lavoro dell'allocatore dei registri consiste nel assegnare i molti valori temporanei (temporaries) prodotti durante la generazione
delle istruzioni ai (pochi) registri a disposizione nella macchina reale. Dove possibile, si tenta anche di assegnare i registri in
modo tale da poter rimuovere le istruzioni MOVE.
1 Gra di interferenza
Dato un insieme di temporaries a, b, c, . . . che devono essere allocati ad un gruppo di registri r1 , . . . , rk , si denisce interferenza
una condizione che previene l'allocazione di a e b allo stesso registro.
L'interferenza più comune è causata dalla sovrapposizione degli intervalli di vita di due temporaries. Un'altra possibile causa
è l'uso di istruzioni il cui risultato a non può essere assegnato a un certo registro r1 (in architetture in cui alcuni registri hanno
usi limitati): in tal caso, si dice che a e r1 interferiscono.
Le informazioni del grafo possono essere espresse tramite una matrice oppure tramite un grafo non orientato in cui ogni nodo
rappresenta una variabile e ogni arco un interferenza.
Il grafo di interferenza si ricava dall'analisi dei gra di controllo e dataow.
Il processo di assegnazione di K registri è paragonabile al problema di colorare una mappa con K colori dierenti: ogni
colore non può essere assegnato a temporaries in conitto (cioè che sono vivi in contemporanea).
Se non è possibile K-colorare il grafo dei conitti, è necessario trasferire alcuni temporaries in memoria: ciò è noto come
spilling.
2 Trattamento delle istruzioni MOVE
Una istruzione del tipo t ← s è un'istruzione di copia. Normalmente, essa viene tradotta in un'istruzione MOVE e porterebbe
a denire un'interferenza tra s e t sul grafo. Tuttavia, non è detto che si abbia bisogno di un registro separato: servirà solo nel
caso in cui, successivamente alla move, c'è una denizione di t mentre s è ancora viva.
In breve:
• per ogni istruzione non-MOVE che denisce una variabile a in cui le variabili vive in uscita (live-out) sono b1 , . . . , bj
aggiungere gli archi di interferenza (a, b1 ), . . . , (a, bj )
• per ogni istruzione MOVE a ← c dove le variabili b1 , . . . , bj sono vive in uscita aggiungere gli archi di interferenza
(a, b1 ), . . . , (a, bj ) per ogni bi diverso da c.
Se si riescono ad assegnare più temporaries ad un solo registro si ottiene un risparmio di tempo (l'istruzione MOVE viene
cancellata) oltre che di spazio.
3 Colorazione dei gra
3.1 Per semplicazione
Il processo di allocazione dei registri (cioè di colorazione dei gra) è NP-completo. Tuttavia esiste un algoritmo di approssimazione
in tempo lineare che fornisce buoni risultati. Le sue fasi sono le seguenti:
1. Build
Costruire il grafo di inferenza utilizzando l'analisi dataow.
2. Simplify
Colorare il grafo utilizzando un'euristica semplice. Supponiamo che il grafo G contenga un nodo m con meno di K nodi
adiacenti. Rimuoviamo (e impiliamo su uno stack) il nodo m. Tale semplicazione diminuirà il grado degli altri nodi e
darà la possibilità per ulteriori passi di Simplify.
1
http://www.windizio.altervista.org/appunti/
File distribuito con licenza Creative Commons BY­NC­SA 2.5 IT
Copyright © 2007 ­ Michele Tartara
3. Spill
Se ad un certo punto il grafo è composto solo da nodi di grado signicativo (> K ) l'euristica Simplify non può proseguire
e si deve segnare il nodo come spill (riversamento in memoria) potenziale prima di rimuoverlo dal grafo e porlo sulla pila.
Con una approssimazione ottimistica, si suppone che questa decisione riduca le interferenze degli altri nodi e permetta di
proseguire con l'euristica Simplify.
4. Select
Assegna i colori ai nodi del grafo. Viene eseguito quando il grafo è stato ormai svuotato e lo ricostruisce prelevando un
nodo alla volta dalla cima della pila. Quando aggiungiamo un nodo deve esserci un colore per esso, siccome le premesse per
la sua rimozione nella fase simplify richiedevano che un colore potesse essere assegnato. Se il nodo che si sta esaminando
era stato marcato come spill potenziale dall'euristica Spill non è detto che ci sia un colore disponibile. Se non è possibile
trovare un colore, si ha un actual spill. In tal caso si continua la fase di select per identicare eventuali altri actual spill
5. Start over
Se sono stati individuati actual spill si modica il programma per far sì che le relative variabili vengano salvate in memoria
e caricate solo prima dell'eettivo uso. Si deve quindi ricominciare tutto l'algoritmo a partire da Build. Raramente servono
più di due iterazioni.
3.2 Con coalescenza
Quando nel grafo delle interferenze non sono presenti archi tra l'origine e la destinazione di un'istruzione MOVE, è possibile
eliminare l'istruzione e rimpiazzare i due nodi con un nodo unico che ha come archi l'unione degli archi dei due nodi originali.
Il nodo introdotto ha (in generale) più interferenze dei singoli nodi originali. Bisogna quindi fare attenzione ed applicare la
coalescenza solo in determinati casi, per impedire che un grafo K-colorabile diventi non più K-colorabile.
Sono state sviluppate due strategie (conservative: rinunciano a possibili riduzioni per essere certe di evitare lo spilling) sicure
per decidere quando applicare la coalescenza:
Briggs Si può applicare la coalescenza ai nodi a e b se il nodo risultante ab avrà meno di K vicini di grado signicativo (con
più di K archi). Se il grafo originale era K-colorabile, questa strategia non altera la colorabilità.
George Si può applicare la coalescenza ai nodi a e b se, per ogni vicino t di a, o t interferisce già con b o t è di grado non
signicativo.
3.2.1 Procedura empirica di coloratura con coalescenza
Figura 1: Procedura di coloratura con coalescenza
Le fasi dell'algoritmo di coloratura si alternano come presentato in Figura 1.
Le fasi sono le seguenti:
Build Costruisce il grafo di interferenza
Simplify Rimuove dal grafo i nodi di grado non signicativo non relativi a una MOVE.
Coalesce Applica la coalescenza in modo conservativo (Briggs o George). Dopo l'attuazione di una coalescenza, si torna a
Simplify. Simplify e Coalesce si alternano nchè rimangono solo nodi di grado signicativo o relativi a una MOVE (ma sui
quali Coalesce non è applicabile). Una MOVE si dice constrained se a seguito di una operazione di Coalesce per un'altra
MOVE, non è più possibile applicare Coalesce sulla MOVE stessa. In tal caso, si considerano i nodi coinvolti come non
più MOVE-related.
Freeze Se né Simplify né Coalesce sono applicabili, si cerca un nodo di basso grado relativo a una MOVE e si rinuncia al
tentativo di applicare Coalesce su di esso, che diventa quindi semplicabile. Si riprende quindi ad applicare Simplify e
Coalesce sui nodi rimanenti.
2
http://www.windizio.altervista.org/appunti/
File distribuito con licenza Creative Commons BY­NC­SA 2.5 IT
Copyright © 2007 ­ Michele Tartara
Spill Se non ci sono nodi di basso grado, marchiamo un nodo di grado signicativo per il potential spilling e lo poniamo sulla
pila.
Select Rimuovere tutti i nodi dalla pila assegnando i colori. Se un nodo potential spill si rivela essere un actual spill è necessario
modicare il programma per gestirne la lettura/scrittura da/verso la memoria e ripetere il procedimento di coloratura da
capo (algoritmi più ecienti conservano come valide le Coalesce precedenti al primo potential spill e non le ripetono).
Coalescing of spills Le architetture con pochi registri (come i Pentium) provocano molti spill. In certi casi, si trovano
istruzioni di MOVE tra celle in memoria, che risultano molto pesanti.
È possibile applicare l'algoritmo di coloratura e coalescenza per decidere l'assegnazione delle aree di memoria (che in questo
caso sono i colori) della pila ed eventualmente per sfruttare contemporaneamente la stessa area per più valori uguali.
Questo procedimento deve essere fatto prima di generare le istruzioni di spill dei registri.
4 Nodi precolorati
Se un registro ri appare sul grafo di interferenza, si dice che è un nodo precolorato.
A un temporary può essere assegnato lo stesso colore di un nodo precolorato, purchè essi non siano collegati da un arco sul
grafo. Ciò permette, ad esempio, di sfruttare all'interno di una funzione un registro dedicato al passaggio di parametri se il
valore in esso contenuto non è più utile.
Su un nodo precolorato non è possibile applicare Simplify (perchè essendo precolorato non avrebbe senso rimuoverlo dal grafo
per assegnargli un colore in seguito) nè Spill (un nodo precolorato è un registro, non un valore: non può, sicamente, essere
spostato in memoria). Si può invece applicare Coalesce in modo conservativo.
Se in un grafo sono presenti nodi precolorati, l'algoritmo di colorazione non li rimuove mai dal grafo stesso: rimuove attraverso
Simplify, Coalesce e Spill tutti gli altri e poi li riaggiunge assegnando loro i colori.
5 Registri caller-saved e callee-saved
Per ridurre il numero di registri occupati, si cerca di non mantere i callee-saved in memoria troppo a lungo. Per fare ciò si
considera come se il registro (poniamo r7 ) sia denito all'ingresso di una funzione e sia usato alla sua uscita (questo ne provoca
la sopravvivenza). Si aggiunge poi come prima istruzione della funzione una move verso un temporary e come ultima istruzione
la move inversa. In tal modo, se è necessario, l'allocatore dei registri potrà eseguire uno spill del valore verso la memoria.
Per quanto riguarda i registri caller-saved, l'istruzione assembly CALL ha il compito di fare una dene (cioè di creare
un'interferenza) per tali registri. In questo modo, le variabili che non sono vive al di là di una funzione tenderanno a essere
memorizzate in registri caller-saved
Usando semplicemente questi accorgimenti, gli algoritmi di allocazione già visti tratteranno correttamente i registri calleesaved.
6 Allocazione dei registri per alberi
Quando si devono allocare i registri per istruzioni organizzate secondo una struttura ad albero non è necessaria un'analisi dataow
globale o l'uso di gra di interferenza.
Trattandosi di alberi di espressioni, già sappiamo che è necessario assegnare un registro per ogni tile non banale (cioè che
non è già presente in un registro, come ad esempio FP), escluse eventualmente istruzioni senza eetti sui registri come MOVE
tra celle di memoria.
L'Algoritmo 1 attraversa l'albero in postordine assegnando un registro ad alla radice di ogni tile. È molto semplice ma non
sempre riesce a fornire una allocazione ottimale.
Per ottenere una tale allocazione è necessario utilizzare algoritmi più evoluti basati sulla programmzione dinamica.
L'idea è quella di etichettare ogni tile con il numero di registri di cui avrà bisogno durante la sua valutazione.
L'algoritmo di etichettatura (labeling) di Sethi-Ullman calcola il numero di registri need[t] necessari per portare a termine
la valutazione del sottoalbero stesso. Successivamente, o si emettono le istruzioni in modo tale che i sottoalberi vengano emessi
in ordine crescente di need (per complilaturi che utilizzano la coloratura dei gra per l'allocazione dei registri) minimizzando
quindi il numero di spill, oppure si utilizza l'algoritmo di Sethi-Ullman per l'allocazione dei registri, che si basa sul seguente
ragionamento: si supponga che un tile t abbia due gli ulef t e uright che richiedono rispettivamente n e m registri per essere
3
http://www.windizio.altervista.org/appunti/
File distribuito con licenza Creative Commons BY­NC­SA 2.5 IT
Copyright © 2007 ­ Michele Tartara
Algorithm 1 Semplice algoritmo di allocazione dei registri
function SimpleAlloc(t)
for each nontrivial tile u that is a child of t
SimpleAlloc(u)
for each nontrivial tile u that is a child of t
n←n−1
n←n+1
assign rn to hold the value at the root of t
calcolati. Se si calcola prima ulef t e poi uright il numero di registri richiesti è max(n, m + 1). Se invece si valuta prima uright ci
vorranno max(n + 1, m) registri. In entrambi i casi, il +1 è il registro necessario a contenere il risultato della prima elaborazione.
L'algoritmo di Sethi-Ulmann decide di calcolare prima ulef t se n > m e prima uright se n < m.
Questo algoritmo, emette le istruzioni assieme all'allocazione dei registri.
4