Register Allocation
Register Allocation: procedura che mappa le variabili nei registri.
L'accesso ai registri è molto più veloce dell'accesso alla memoria centrale.
Il mapping dalle variabili ai registri ĆØ di tipo many-to-one.
Il numero di live variables può essere maggiore del numero di registri.
Per liberare registri quando necessario, le variabili memorizzate in essi devono essere prima salvare in memoria centrale.
Scopes dell'allocazione delle variabili nei registri:
For expressions (naive).
For basic blocks.
For one procedure (quella su cui ci concentreremo).
For the entire program (impossibile nella pratica).
Vantaggi di unitĆ di allocazione piccola:
Facile da integrare con la fase di Instruction Selection.
Più semplice e veloce di soluzioni più complesse.
Prono ad applicazioni di compilazione dinamica.
Liveness Interference
Criterio per l'allocazione delle variabili nei registri: Se gli intervalli di liveness di due variabili sono disgiunti, può essere utilizzato lo stesso registro per memorizzare entrambe le variabili (in istanti di tempo diversi). Bisogna cioè evitare di utilizzare lo stesso registro per memorizzare due variabili che sono alive in contemporanea.
Liveness interference relation: Una relazione binaria e simmetrica tra due variabili a e b che denota il fatto che i liveness intervals di a e b sono sovrapposti.
Graph Coloring
La relazione di interferenza può essere rappresentata come un grafo non orientato. I registri possono essere rappresentati assegnando un colore distinto a ciascuno di essi. L'assegnamento dei registri consiste nella colorazione dei nodi del grafo in modo che nodi adiacenti abbiano colori diversi (problema analogo alla colorazione delle mappe politiche).
Se non ĆØ possibile trovare una soluzione dato un certo grafo ed un certo numero di registri -> spill delle variabili in memoria, nuova assegnazione dei colori. Per effettuare tale operazione vengono utilizzare euristiche a complessitĆ lineare.
Algoritmo
Definiamo S come lo stack in cui inserisco i nodi estratto dal grafo delle interferenze, e K come il numero di registri a disposizione.
Costruisco l'interference graph.
Per ogni nodo p, estraggo p e lo inserisco in S se il nodo in questione ha <K vicini.
Ripeto la stessa procedura per i nodi rimasti, però li marco come potential spills.
Riparto dall'interference graph; tramite una procedura iterativa estraggo i nodi da S. Ad ogni passo estraggo un nodo p e gli assegno un colore.
Se il nodo p appena estratto non è marcato come potential spill, assegno un colore al nodo corrispondente nel grafo in modo che non sia in conflitto con i colori dei vicini. Se il nodo è marcato come potential spill, tento comunque l'assegnazione di un colore. Se cioò non è possibile, marco la variabile come actual spill, e la spillo in memoria.
Il passo 4 termina quando S=ā . Quando ciò accade, avremo colorato tutto il grafo.
Iteriamo Simplify e Spill finchĆØ il grafo non ĆØ vuoto:
Simplify
Se G contiene un nodo m con meno di K vicini ->
Costruzione di Gā²=G\{m}.
Push di m in S
Ripeti fino ad un fixed point (nessun nodo t.c. <K vicini)
Spill
Se Gī =0, tutti i nodi rimanenti hanno ā„K vicini. Sceglie un nodo s e lo assegna come candidato per lo spilling.
pushdi s in S, computazione di Gā²=G\{s}Ritorno a Simplify.
Al termine avremo lo stack pieno con alcuni nodi marcati come candidati per lo spilling. Ripartendo dal grado che rappresenta le interference relation della procedura poi avremo che:
Select
Per ogni nodo sāS, assegna un colore ad esso e lo estrae dallo stack con una
pop:Se s non interferisce con i nodi estratti in precedenza, il colore appena assegnatogli va bene (nota che se il nodo non ĆØ marcato per lo spilling sarĆ sicuramente colorabile, mentre se ĆØ marcato per lo spilling potrebbe non esserlo), in caso contrario il nodo verrĆ marcato come actual spill.
Alla fine di questo passo, se sono presenti actual spill, questi vengono eseguiti nel codice, e poi salta a Start over. Il ciclo viene iterato finchĆØ non riusciamo a risolvere il problema di graph coloring per tutti i nodi del grafo.
Esempio di applicazione dell'algoritmo:


Linear Scan (LinearScanRegisterAllocation)
LinearScanRegisterAllocation)Per ogni Basic Block controlla quali variabili sono live tramite una scansione lineare del codice O(V), con V numero delle variabili. Se il numero di registri R ĆØ molto grande, la complessitĆ diventa O(VĆR). Il risultato ĆØ lo stesso di graph coloring nel 95% dei casi, ed ĆØ più efficiente.
Per ogni live interval, in ordine di starting point incrementale:
Controllo se è possibile liberare registri non più utilizzati, se presenti, e in caso affermativo li libero.
Se non sono presenti registri liberi -> Procedura di spill:
Seleziono la variabile attiva con l'end point maggiore e confronto quest'ultimo con l'endpoint del nuovo intervallo. Spillo in memoria (nello stack) quello che ha l'end point maggiore fra i due.
Pseudocodice: LinearScanRegisterAllocation
activeā{} āliveĀ intervalĀ i,Ā inĀ orderĀ ofĀ increasingĀ startĀ point:
EXPIREOLDINTERVALS(i)
if length(active)=R then SPILLATINTERVAL(i); else:
register [i]ā a register removed from pool of free registers
add i to active, sorted by increasing end point
Pseudocodice: ExpireOldIntervals
La scelta dell'ordinamento è euristica, di solito la lunghezza di un intervallo corrisponde al range di istruzioni in cui un registro è stato utilizzato per l'ultima volta. Dal momento che pochi valori hanno più di un utilizzo, scegliendo l'intervallo più grande rimanente, riduco i registri con il tempo di utilizzo maggiore.
ā interval jā active, in order of increasing end point:
if endpoint [j]ā„startpoint[i] then return
active = active \{j}
a add register[j] to pool of free registers
Pseudocodice: SpillAtInterval(i)
spill ā last interval in active
if endpoint [ spill ]> endpoint [i] then
register [i]ā register [ spill]
location[spill] ā new stack location
active = active \{ spill }
add i to active, sorted by increasing end point
else location [i]ā new stack location
Note
SSA semplifica l'interference analysis dal momento che ogni assegnamento della stessa variabile viene considerato come una variabile diversa.
Liveness analysis dovrebbe essere eseguita prima della risoluzione delle Ļ functions.
Liveness analysis ĆØ veloce grazie alla proprietĆ di dominanza di SSA (le definizioni dominano gli usi).
La durata dell'algoritmo di Liveness analysis ĆØ proporzionale alla dimensione del grafo da esso costruito, non alla dimensione del CFG.
Last updated