Data Flow Analysis
Static Analysis: Control Flow Graph
Ogni nodo del CFG rappresenta un istruzione.
Variabili ed argomenti di funzioni vengono trattati allo stesso modo, come simboli temporanei.
Un arco p→q può essere eseguito subito dopo q. In questo caso p è il predecessore di q.
Il punto di ingresso non ha predecessori, è chiamato nodo iniziale.
Il punto di uscita (
return) non ha successori, è chiamato nodo finale.Istruzioni non condizionali hanno al più un successore, mentre le istruzioni condizionali ne hanno due (punti di biforcazione). Un nodo con due o più predecessori è chiamato punto di confluenza.
Il CFG può essere visto come un FSA il cui alfabeto di terminali è l'insieme delle istruzioni I, ed il linguaggio delle stringhe generate da uno specifico CFG è l'insieme dei percorsi dallo stato iniziale allo stato finale.
Def, Use sets
Un assegnamento di una variabile è chiamato definizione, mentre un occorrenza all'interno di un espressione viene chiamata uso.
Ogni nodo p del CFG ha due insiemi di variabili: def(p) e uuse(p). Ad esempio, se il nodo contiene la seguente istruzione:
p:a:=a⊕b
I suoi insiemi di definizioni ed utilizzi saranno:
Esempio di CFG con usi e definizioni
Definiamo l'insieme delle definizioni e l'insieme degli utilizzi di una generica variabile a come: D(a)⊆I e U(a)⊆I, ad esempio D(a)={1,4} e U(a)={2,5} nell'esempio sottostante:

Liveness Analysis: Data Flow Equations
Possibili utilizzi di liveness analysis:
Useless Definitions: Eliminazioni di definizioni di variabili che non vengono mai usate
Common Subexpressions: Se un espressione è già stata computata, può essere memorizzata in un temporary e riutilizzata in seguito. Simile a liveness analysis, ma non considera le singole variabili ma le combinazioni di essere nelle espressioni.
Constant Folding: La computazione di espressioni con operandi constanti può essere effettuata a compile time.
Constant Propagation: Simile a constant folding, ma se una temporary è costante le sue occorrenze succesive sono rimpiazzate con tale costante.
Arc Liveness
Prendiamo in considerazione un CFG e un arco p→q1. Una variabile è live sull'arco se ∃ un percorso p→q1→…→qn, con n≥1, che raggiunge qn e tale che:
a∈use(qn),
Nessuno dei nodi qj sul percorso p→q1→…qj→…→qn, ad eccezione di p e qn, contengono una definizione di a.
Idea: controllo bottom-up che alcune definizioni siano live in un percorso specifico del CFG. Con liveness si fa riferimento alla persistenza di uno specifico assegnamento di una variabile in una certa sequenza di istruzioni.
Note
qn può coincidere con p.
Se p ha due successori, a è live all'uscita di p se è live su uno dei due archi.
a è live all'ingresso di p se è live su tutti i vertici che entrano in p.
Il fatto che a sia live all'uscita di p non implica che a∈def(p).
Liveness Property
La liveness property di a all'uscita di un nodo p è definita dalla seguente condizione:
Nel linguaggio L(A) esiste una stringa (un percorso) composta da:
Un generico percorso dal nodo iniziale
p
Un generico percorso in cui a non è mai definita: v∩D(a)=0
Un nodo q che utilizza a: a∈use(q)
Un generico percorso fino al nodo finale
L'insieme di tutte le stringhe che rispettano questa condizione formano un linguaggio regolare. Questa strategia non è molto efficiente, esiste un metodo più pratico.
Data Flow Equations
Sistema di equazioni tali per cui per ogni nodo p abbiamo:
Un equazione che correla le variabili live all'ingresso e live all'uscita di esso.
Un equazione che correla le variabili live all'uscita di p con quelle live all'ingresso dei suoi immediate succesors.
Siano livein(p) and liveout (p) gli insiemi di variabili live all'ingresso e all'uscita di p, e suc(p),pred(p) gli immediate successors e immediate predecessors di p:
Per un nodo finale p :
Per ogni altro nodo p :
Equazione 1: nessuna variabile è live all'uscita del programma
Equazione 2: una variabile è live all'ingresso di p se è utilizzata in p o se è live all'uscita di p e non è definita in esso.
Equazione 3: L'insieme delle variabili live all'uscita di p è l'unione di tutte le variabili live all'ingresso dei successori di p.
Fixed Point Solution
Il sistema può essere risolto iterativamente, partendo dall'insieme vuoto all'iterazione iniziale (i=0):
I valori sono ricavati sostituendo le variabili sconosciute con gli insiemi vuoti, ottenendo una nuova iterazione. La soluzione è ricavata quando due iterazioni successive convergono (i.e. sono uguali).
Proprietà delle Data Flow Equations, Dimostrazione di terminazione
Cardinalità limitata: Sia livein(p) che liveout(p) hanno una cardinalità limitata, il cui limite superiore è la cardinalità dell'insieme delle variabili del programma. È importante fare in modo che questa proprietà sia rispettata quando gli insiemi delle proprietà vengono definiti.
Monotonicità: Non rimuoviamo mai variabili da un'approssimazione precedente, cioè non rimuoviamo mai una variabile dal'insieme corrente. Nello specifico la seconda equazione (livein(p)= use (p)∪( live out (p)\def(p))) Non deve mai rimuovere elementi dall'insieme.
Terminazione: Proprietà strutturale dell'algoritmo, se un iterazione non modifica il risultato ottenuto all'iterazione precedente, l'algoritmo si ferma.
Di conseguenza l'algoritmo può aggiungere al massimo tutte le variabili a tutti i live sets, e poi deve terminare dal momento che la proprietà di monotonicità gli impedisce di far ripartire il ciclo da un'altra soluzione.
Nota: questa dimostrazione è chiesta quasi sempre all'esame.
Esempio


Time Complexity: Worst Case Analysis
for each n in(n)←∅; for each n∀n:in′(n)= out (n)←∅;− initialization in′(n)←in(n); out (n)←out(n)in(n)←cose(n)∪(out(n)\def(n))out(n)←⨆∀q∈succ(p)in(q)in(n)∧out′(n)=out(n)
Ogni ∪ sugli insiemi live-in o live-out -> O(N)
Il ciclo interno calcola un numero costante di unioni per nodo -> O(k)
Ci sono O(N) nodi -> il ciclo
forha complessità O(N2)Ogni iterazione deve aggiungere qualche elemento agli insiemi, ma ogn insieme ha cardinalità con limite superiore N. La somma delle cardinalità ha limite superiore 2N2 -> il loop può essere eseguito al più O(N2) volte.
Complessità totale del caso pessimo: O(N4).
Rappresentazione in memoria dei in, out sets
Bit Vectors: array di bits in cui ogni bit rappresenta un elemento. Dato N, servono wordsizeN words per rappresentare un insieme. Se N è relativamente fissato, la complessità per effettuare l'unione diventa costante.
Linked lists: elementi rappresentati da una linked list. In questo caso il costo dell'unione è proporzionale ad N, e tale operazione è effettuata unendo le due liste senza elementi ripetuti. Non sono quasi mai più veloci dei bit vectors.
Quando i set sono sparsi è preferibile il secondo caso, altrimenti il primo.
Reaching Definitions
Definizioni ambigue e non ambigue
Definizione di una variabile che raggiunge (reaching) un certo punto nel programma. Tipologia di analisi collegata a SSA in quanto il contenuto delle ϕ functions sono le definizioni delle variabili che raggiungono un certo nodo p.
L'istruzione p:a:=a⊕b definisce a. Denotiamola come ap.
Una definizione ambigua è una definizione della variabile a che potrebbe non assegnare un valore ad essa.
Insieme delle definizioni di a nel programma: D(a). Una definizione certa (o non ambigua) di a in un istruzione q, chiamiamola aq, è una definizione che raggiunge l'ingresso di un istruzione p (che può coincidere con q) se esiste nel CFG un percorso da q a p che non attraversa un nodo in cui a viene ridefinita, e di conseguenza p userà il valore aq.
gen, kill sets
Dato uno generico nodo p definiamo:
gen(p)={ap}, contiene cioè le variabili definite da p.
kill(p)=D(a)\{ap}. cioè l'insieme di tutte le variabili ridefinite da p, cioè variabili che sono sia definite da p, sia definite da almeno un altro nodo, chiamamolo q, tale per cui p=q.
Più formalmente, dato I, l'insieme delle istruzioni del programma, consideriamo un istruzione p che definisce una variabile (i.e. produce ap) e sovrascrive (kills) ogni altra definizione di aq, q=p:
Computazione delle in, out equations
Gli insiemi delle definizioni che raggiungono l'ingresso e l'uscita di p sono chiamati rispettivamente in e out. Per un generico nodo p definiamo in(p) come l'unione degli insiemi out(q) ∀q predecessore di p, e out(p) come l'unione tra l'insieme delle variabili definite da p e l'insieme risultante dalla differenza tra in(p) e kill(p).
Per il nodo iniziale 1:
Per ogni altro nodo p :
Dove pred(p) definisce i predecessori immediati di p nel CFG. Inoltre:
Da in(1)=∅ notiamo che gli argomenti del programma non sono tenuti in cosiderazione.
Per quanto riguarda la liveness, le equazioni possono essere risolte iterativamente terminando all'iterazione in cui tutti gli insiemi delle incognite sono vuoti, con l'eccezione di in(1), che è sempre vuoto.
Last updated