Spiegazione approssimativa dell'algoritmo di navigazione Graph del flusso di controllo

2

Chiedendo a un alto livello come si dovrebbe andare a navigare un Control-Flow Graph (CFG) per eseguire operazioni come l'eliminazione del codice morto. Questo diagramma mostra un problema di eliminazione del codice morto, come capire che queste variabili sono correlate l'una con l'altra.

Ti stai chiedendo come procedere nel iterare attraverso il CFG per trovare cose come:

  1. Variabili che devono essere eliminate (codice morto)
  2. Variabili che devono essere divise in un modulo SSA
  3. Cicli che possono essere uniti, forse.
  4. Riordinamento dei nodi, forse.
  5. Altre cose.

Non c'è bisogno di conoscere le soluzioni esatte a questi problemi, semplicemente cercando di capire in modo approfondito cosa serve a capire la relazione e il significato tra i nodi (o blocchi di nodi) ) del CFG, quindi posso guardare oltre. Se dovessi fare questo in maniera ingenua, per ogni nodo, attraverserei nuovamente il grafico per trovare relazioni con esso, il che sembra un approccio subottimale / sbagliato. Ad esempio, trovare le definizioni raggiungibili sembra, per ciascuna variabile, controllare ogni altra variabile nel programma , creando un grafico altamente connesso . Dopo aver effettuato queste connessioni, devi effettivamente fare qualcosa con i dati, quindi è ancora più elaborato. Questo sembra un sacco di memoria / attraversando, quindi chiedendo se questa è la pista giusta.

    
posta Lance Pollard 30.04.2018 - 03:26
fonte

1 risposta

1

Penso che tu stia chiedendo di Analisi del flusso di dati .

Prima di SSA, dovremmo usare i vettori bit, dove ciascun bit nel vettore bit rappresenta una variabile nel metodo che viene compilato (potrebbe essere una variabile di programma o un registro cpu reale o virtuale). Useremmo una coppia di vettori bit, uno per i difetti e uno per gli usi, e tali vettori bit sarebbero concettualmente collegati ad ogni istruzione; anche se in pratica, in genere memorizziamo tali vettori di bit all'inizio e alla fine di ogni blocco di base.

Questi vettori di bit vengono inizialmente calcolati attraversando ogni blocco di base: andando avanti attraverso il blocco, la definizione di raggiungimento alla fine del blocco di base ha un bit impostato per ogni variabile impostata (def'd) nel blocco di base e, andando indietro nel blocco, gli usi esposti verso l'alto all'inizio del blocco di base hanno un bit impostato per ciascuna variabile che viene utilizzata all'interno del blocco di base. Durante l'attraversamento all'indietro, una def mask utilizza la stessa variabile, quindi solo le variabili utilizzate e non mascherate lo rendono in quel vettore di bit memorizzato per l'inizio del blocco.

Quindi i vettori di bit per i blocchi di base vengono propagati per tutto il metodo che viene compilato (ad esempio gli altri blocchi di base) attraversando il grafico del flusso di controllo, unendo le informazioni fino a quando non vengono apportate modifiche (le funzioni di unione sono punti fissi, ovvero convergono). Se riconosciamo particolari costrutti del flusso di controllo (while loop, if then, etc ...), possiamo fermare la fusione prima dell'iterazione finale, in cui non si verificano cambiamenti.

Durante l'utilizzo, una volta calcolato il flusso di dati per il metodo, è possibile eseguire l'ottimizzazione come codice morto. Questa ottimizzazione potrebbe iniziare alla fine di un blocco di base, iniziando con l'uso di vettori bit e camminando all'indietro. Attraversando all'indietro, una copia del vettore di bit utilizza potrebbe essere modificata secondo ciascuna istruzione in modo da avere informazioni sul livello di istruzione.

Il codice morto (in termini di variabili assegnate ma non utilizzate) viene quindi rilevato sapendo che, in una def specifica, quella variabile non è nel set di utilizzo. L'istruzione che calcola che def può essere eliminata. Il flusso di dati deve essere aggiornato di conseguenza, ed è normale quindi trovare più istruzioni (in precedenza, quelle che calcolavano i valori per l'istruzione eliminata da usare) che ora sono anche morti.

SSA - introducendo una nuova versione di una variabile (ad esempio ogni volta che viene assegnata) e quindi monitorando le versioni di variabili anziché solo le variabili - consente ad alcune delle informazioni raccolte di flusso di dati di applicarsi all'intero metodo, invece di variare per posizione nel codice come prima di SSA.

    
risposta data 01.05.2018 - 22:19
fonte

Leggi altre domande sui tag