La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

1 Generazione di Codice Intermedio Implementazione di Linguaggi 2 A.A. 2004/2005 di Gualdani Alessandro.

Presentazioni simili


Presentazione sul tema: "1 Generazione di Codice Intermedio Implementazione di Linguaggi 2 A.A. 2004/2005 di Gualdani Alessandro."— Transcript della presentazione:

1 1 Generazione di Codice Intermedio Implementazione di Linguaggi 2 A.A. 2004/2005 di Gualdani Alessandro

2 2 Il generatore di codice intermedio nel compilatore ScannerParser Type checker Generatore di codice intermedio Generatore di codice Generatore di codice intermedio Interprete

3 3 Introduzione Sebbene un programma sorgente possa essere tradotto direttamente in codice eseguibile, vi sono alcuni vantaggi nellutilizzo di un codice intermedio indipendente dalla macchina: Retargeting un compilatore per macchine diverse può essere facilmente creato Ottimizzazioni possono essere applicate alla rappresentazione intermedia

4 4 Tipi di rappresentazioni intermedie AST (Abstract Syntax Tree) albero in cui un nodo rappresenta un operatore e i suoi figli rappresentano gli operandi DAG (Directed Acyclic Graph) simile ad un AST tranne per il fatto che una sottoespressione comune ha più di un padre Notazione postfissa è una linearizzazione di un AST: è una lista di nodi dellalbero in cui un nodo appare immediatamente dopo i suoi figli Three address code (notazione a tre indirizzi) descritto in seguito

5 5 Esempio (1/2) Consideriamo il seguente esempio: a := b*-c + b*-c := a+ * * b- b - c := a+ * b- c AST DAG

6 6 Esempio (2/2) Notazione postfissa a b c - * b c - * + := Three address code t1 := - c t2 := b * t1 t3 := - c t4 := b * t3 t5 := t2 + t4 a := t5 t1 := - c t2 := b * t1 t5 := t2 + t2 a := t5 Corrispondente allAST Corrispondente al DAG

7 7 Three address code Il codice a tre indirizzi è una sequenza di istruzioni del tipo: x := y op z dove x, y, z sono nomi, costanti o temporanei generati dal compilatore ed op è un operatore Il nome codice a tre indirizzi deriva dal fatto che, di solito, ogni istruzione contiene tre indirizzi: due per gli operandi ed uno per il risultato

8 8 Istruzioni del codice a tre indirizzi (1/3) Le istruzioni sono simili al codice assembler possono avere etichette ci sono istruzioni (salti) per il controllo di flusso 1. Comandi di assegnamento della forma x := y op z, dove op è un operatore (binario) logico o aritmetico della forma x := op y, dove op è un operatore unario copy statement x := y, dove il valore di y è assegnato ad x

9 9 Istruzioni del codice a tre indirizzi (2/3) 2. Salti incondizionato, goto L condizionato, if x relop y goto L, dove relop è un operatore relazionale ( =, …) 3. Invocazioni/ritorni di procedura istruzioni param x, call p, n, return y (opzionale); il loro uso tipico è nella sequenza generata per linvocazione di procedura p( x 1, x 2, …, x n ) param x 1 param x 2 … param x n call p, n

10 10 Istruzioni del codice a tre indirizzi (3/3) 4. Assegnamenti indiciati della forma x := y[i], assegna alla variabile x il valore nella locazione che si trova i unità dopo y della forma x[i] := y, lopposto dellassegnazione del punto precedente 5. Assegnamenti di indirizzi e puntatori della forma x := &y, assegna ad x la locazione di y della forma x := *y, assegna ad x il contenuto della locazione di y della forma *x := y, assegna alloggetto puntato da x il valore di y

11 11 Implementazione dei comandi a tre indirizzi In un compilatore i codici a tre indirizzi possono essere implementati come record con campi per gli operatori e gli operandi Possibili rappresentazioni: Quadruple Triple Triple indirette

12 12 Implementazione dei comandi a tre indirizzi - Quadruple Una quadrupla è un record con quattro campi op, arg1, arg2, result op memorizza un codice interno per loperatore arg1 ed arg2 memorizzano il primo ed il secondo operando result memorizza il risultato dellistruzione I contenuti di arg1, arg2, result sono, di solito, puntatori alla symbol table

13 13 Implementazione dei comandi a tre indirizzi - Triple Una tripla è un record con tre campi op, arg1, arg2 op memorizza un codice interno per loperatore arg1 ed arg2 memorizzano il primo ed il secondo operando Per evitare di inserire nomi temporanei nella symbol table, un valore intermedio viene riferito mediante la posizione dellistruzione che lo calcola

14 14 Implementazione dei comandi a tre indirizzi – Triple indirette Le istruzioni del codice a tre indirizzi vengono rappresentate mediante una lista di puntatori a triple Ad es., si può usare un array statement con lelenco dei puntatori alle triple, nellordine desiderato

15 15 Implementazione dei comandi a tre indirizzi – Esempio (1/3) Consideriamo lassegnamento a := b*-c + b*-c le rappresentazioni a quadruple, triple e triple indirette sono le seguenti: Quadruple oparg1arg2result (0) -ct1 (1) *bt1t2 (2) -ct3 (3) *bt3t4 (4) +t2t4t5 (5) :=t5a

16 16 Implementazione dei comandi a tre indirizzi – Esempio (2/3) Triple oparg1arg2 (0) -c (1) *b (0) (2) -c (3) *b (2) (4) + (1)(3) (5) :=a (4)

17 17 Implementazione dei comandi a tre indirizzi – Esempio (3/3) Triple indirette oparg1arg2 (14) -c (15) *b (14) (16) -c (17) *b (16) (18) + (15)(17) (19) :=a (18) statement (0)(14) (1)(15) (2)(16) (3)(17) (4)(18) (5)(19)

18 18 Traduzione diretta dalla sintassi in three address code Una regola (azione) semantica è assegnata ad ogni produzione Si costruisce un AST e se ne effettua una visita depth-first per effettuare la traduzione in base alle regole semantiche Nella fase di generazione del codice intermedio non è effettuata alcuna ottimizzazione

19 19 Dichiarazioni Man mano che si incontrano le dichiarazioni in una procedura o in un blocco, si alloca lo spazio per la memoria: per ogni nome locale, si crea una entrata nella symbol table con informazioni quali il tipo e lindirizzo relativo per quel simbolo in memoria nella generazione degli indirizzi si presuppone una particolare allocazione dei dati (nel seguito supporremo che gli indirizzi siano multipli di 4)

20 20 Dichiarazioni in una procedura Si utilizza una variabile globale offset per tener traccia del prossimo indirizzo di memoria libero La procedura enter(name, type, offset) crea una nuova entrata nella symbol table per name, associandogli tipo type, a partire dallindirizzo in offset Il non terminale T possiede due attributi: type (nome del tipo) width (quantità di memoria necessaria per allocare valori del tipo T)

21 21 Dichiarazioni – Schema di traduzione P { offset := 0 } D D D ; D D id : T { enter( id.name, T.type, offset); offset := offset + T.width } T integer { T.type := integer; T.width := 4 } T real { T.type := real; T.width := 8 } T array [ num ] of T 1 { T.type := array( num.val, T 1.type); T.width := num.valT 1.width } T T 1 { T.type := pointer( T 1.type); T.width := 4 }

22 22 Comandi di assegnamento Attributi usati in seguito: id. name : attributo che memorizza il nome dellidentificatore E. place : attributo (di unespressione E) che memorizza la variabile temporanea che contiene il valore dellespressione

23 23 Comandi di assegnamento Operazioni usate in seguito: newtemp : funzione che restituisce lindirizzo di una nuova variabile temporanea lookup( id.name) : funzione che verifica se esiste unentrata per id.name nella symbol table; in caso positivo restituisce il puntatore (indirizzo di memoria) allidentificatore, altrimenti ritorna nil emit(code) : è una procedura che genera il codice a tre indirizzi

24 24 Assegnamento – Schema di traduzione S id := E { p := lookup ( id. name ) if p nil then emit ( p := E. place ) else error } E E 1 + E 2 { E.place := newtemp; emit( E.place := E 1.place + E 2.place) } E E 1 * E 2 { E.place := newtemp; emit( E.place := E 1.place * E 2.place) } E - E 1 { E.place := newtemp emit( E.place := - E 1.place) } E ( E 1 ) { E.place := E 1.place } E id { p := lookup ( id. name ) if p nil then E. place := p else error }

25 25 Espressioni booleane Nei linguaggi di programmazione le espressioni booleane hanno il duplice scopo: di calcolare dei valori di verità di controllare il flusso di esecuzione in statement quali, ad es., if o while In seguito considereremo espressioni booleane con la seguente grammatica: E E or E | E and E | not E | ( E ) | id relop id | true | false dove: relop {, =,,, } gli operatori and e or associano a sinistra ed or ha precedenza minore di and e not

26 26 Espressioni booleane – Metodi di rappresentazione I principali metodi per rappresentare i valori di unespressione booleana sono: 1. codificare true e false attraverso valori interi (di solito 1 e 0 ) e valutare unespressione booleana analogamente ad una aritmetica 2. rappresentare unespressione booleana attraverso il flusso di controllo, cioè rappresentando il valore dellespressione booleana mediante la posizione raggiunta nel programma

27 27 Espressioni booleane – Rappresentazione numerica Supponiamo true codificato con 1 e false con 0 Ad es. a or b and not c è rappresentata dal seguente codice a tre indirizzi: Una espressione relazionale come, ad es., a

28 28 Espressioni booleane – Rappresentazione numerica Operazioni usate in seguito: nextstat : indirizzo della prossima istruzione emit : incrementa nextstat dopo aver prodotto il codice a tre indirizzi

29 29 Espressioni booleane – Rappresentazione numerica – Schema di traduzione E E 1 or E 2 { E.place := newtemp; emit( E.place := E 1.place or E 2.place) } E E 1 and E 2 { E.place := newtemp; emit( E.place := E 1.place and E 2.place) } E not E 1 { E.place := newtemp; emit( E.place := not E 1.place) } E ( E 1 ){ E.place := E 1.place } E id 1 relop id 2 { E.place := newtemp; emit(if id 1. place relop.op id 2. place goto nexstat +3 ); emit( E.place := 0); emit(goto nexstat+2); emit( E.place := 1) } E true{ E.place := newtemp; emit( E.place := 1) } E false{ E.place := newtemp; emit( E.place := 0) }

30 30 Espressioni booleane – Rappresentazione attraverso il flusso di controllo Lidea base è che la valutazione di una espressione produce due etichette: quella a cui si salta se lespressione è vera e quella a cui si salta se lespressione è falsa Supponiamo, ad es., che lespressione E sia della forma a

31 31 Espressioni booleane – Rappresentazione attraverso il flusso di controllo Supponiamo ora che E sia della forma E 1 or E 2 Se E 1 è vera, allora sappiamo immediatamente che anche E è vera (così E 1. true è lo stesso che E. true ) Se E 1 è falsa, allora dobbiamo valutare E 2 (così E 1. false è letichetta della prima istruzione nel codice per E 2 ; le uscite true e false di E 2 sono le stesse di E) Analoghe considerazioni si applicano alla traduzione E 1 and E 2 La traduzione di espressioni E della forma not E 1 provoca solo uno scambio delle uscite true e false di E 1 con quelle di E

32 32 Espressioni booleane – Rappresentazione attraverso il flusso di controllo Attributi ed operazioni usati in seguito: newlabel : funzione che ritorna una nuova etichetta le espressioni booleane E hanno due attributi, E. true ed E. false, che contengono le etichette (indirizzi) dove bisogna saltare se lespressione è vera o falsa ed un attributo E. code che contiene il codice prodotto per valutarle loperatore || rappresenta la concatenazione di codice

33 33 Espressioni booleane – Rappresentazione attraverso il flusso di controllo – Schema di traduzione E E 1 or E 2 { E 1.true := E.true; E 1.false := newlabel; E 2.true := E.true; E 2.false := E.false; E.code := E 1.code || emit( E1.false:) || E 2.code } E E 1 and E 2 { E 1.true := newlabel; E 1.false := E.false; E 2.true := E.true; E 2.false := E.false; E.code := E 1.code || emit( E 1.true:) || E 2.code } E not E 1 { E 1.true := E.false; E 1.false := E.true; E.code := E 1.code } E ( E 1 ){ E 1.true := E.true; E 1.false := E.false; E.code := E 1.code } E id 1 relop id 2 { E.code := emit(if id 1. place relop.op id 2. place goto E.true) || emit(goto E.false) } E true{ E.code := emit(goto E.true) } E false{ E.code := emit(goto E.false) }

34 34 Comandi per il controllo del flusso La grammatica che consideriamo è la seguente: S if E then S 1 | if E then S 1 else S 2 | while E do S 1 le espressioni booleane E vengono tradotte mediante rappresentazione attraverso il flusso di controllo la traduzione degli statement S consente di controllare il flusso in base allistruzione che deve seguire il codice S. code : viene realizzato attraverso lattributo S. next che contiene letichetta della prima istruzione da eseguire dopo il codice per S

35 35 Comandi per il controllo del flusso if E then S 1 Nella traduzione dello statement S if E then S 1, una nuova etichetta E. true viene creata e attaccata alla prima istruzione generata per lo statement S 1 Il codice per E genera un salto alletichetta E. true se E è vera ed un salto a S. next se E è falsa (quindi letichetta E. false è in corrispondenza della prossima istruzione da eseguire cioè S. next ) … E. code S 1. code E. true : E. false : to E. true to E. false

36 36 Comandi per il controllo del flusso if E then S 1 else S 2 Nella traduzione dello statement S if E then S 1 else S 2 si salta alla prima istruzione di S 1 se E è vera, alla prima istruzione di S 2 se E è falsa S. next, come nello statement S if E then S 1, rappresenta letichetta della prima istruzione da eseguire dopo aver eseguito il codice per S … E. code S 1. code E. true : E. false : to E. true to E. false goto S. next S 2. code S. next :

37 37 Comandi per il controllo del flusso while E do S 1 Nella traduzione dello statement S while E do S 1, una nuova etichetta S. begin è creata ed attaccata alla prima istruzione generata per E ed unaltra E. true per la prima istruzione per S 1 Il codice per E genera un salto ad E. true se E è vera, ad S. next se E è falsa (cioè E. false punta ad S. next ) Dopo lesecuzione del codice per S 1 si effettua un salto ad S. begin per valutare lespressione booleana … E. code S 1. code E. true : E. false : to E. true to E. false goto S. begin S. begin :

38 38 Comandi per il controllo del flusso Schema di traduzione S if E then S 1 { E.true := newlabel; E.false := S.next; S 1.next := S.next; S.code := E.code || emit( E.true:) || S 1.code } S if E then S 1 else S 2 { E.true := newlabel; E.false := newlabel; S 1.next := S.next; S 2.next := S.next; S.code := E.code || emit( E.true:) || S 1.code || emit(goto S.next) || emit( E.false:) || S 2.code } S while E do S 1 { S.begin := newlabel; E.true := newlabel; E.false := S.next; S 1.next := S.begin; S.code := emit( S.begin:) || E.code || emit( E.true:) || S 1.code || emit(goto S.begin) }

39 39 Invocazioni di procedure Consideriamo una semplice grammatica per invocare procedure: S call id ( Elist ) Elist Elist, E | E Quando si genera il codice a tre indirizzi per le procedure, è necessario valutare le espressioni degli argomenti quindi fare seguire la lista di parametri; per far ciò: si utilizza una coda (variabile globale queue ) in cui i valori delle espressioni Elist vengono inseriti la routine che implementa linvocazione emette una istruzione param per ogni elemento della coda

40 40 Invocazioni di procedure Schema di traduzione S call id ( Elist ) { t := 0; for each item p on queue do emit(param p ); t := t+1; emit(call id.place, t) } Elist Elist, E { append E. place to the end of queue } Elist E { initialize queue to contain only E. place }

41 41 Backpatching (1/2) Il modo più semplice per implementare le traduzioni finora presentate è quello di usare due passate: 1. prima si costruisce un albero sintattico per linput 2. poi si effettua una visita depth-first dellalbero, calcolando le traduzioni date nella definizione

42 42 Backpatching (2/2) Il problema principale nel generare codice per le espressioni booleane e per il controllo di flusso in una singola passata è dato dal fatto che potremmo non conoscere letichetta argomento di una goto quando listruzione è generata Se non si vogliono effettuare due passate, un modo per ovviare al problema è: tener traccia di una lista di istruzioni goto con lindirizzo del salto lasciato (temporaneamente) non specificato riempire gli argomenti del salto quando lindirizzo viene generato Questa tecnica si chiama backpatching

43 43 Argomenti non trattati Dichiarazioni con procedure annidate [1, §8.2] Dichiarazione di record [1, §8.2] Indirizzamento di elementi in un array [1, §8.3] Conversioni di tipo allinterno di assegnamenti [1, §8.3] Istruzione case [1, §8.5]

44 44 Unaltra rappresentazione intermedia Michael Franz e Thomas Kistler hanno realizzato una rappresentazione intermedia che coniuga la portabilità del codice con le ridotte dimensioni in termini di spazio occupato La rappresentazione intermedia da loro proposta va sotto il nome di slim binaries

45 45 Slim Binaries

46 46 Introduzione I file oggetto contengono una rappresentazione intermedia compatta La codifica degli slim binaries è basata sullosservazione che parti differenti di un programma sono spesso simili le une alle altre; queste similarità vengono sfruttate utilizzando un algoritmo di compressione predittivo che consente di codificare le sottoespressioni ricorrenti in un programma in modo efficiente dal punto di vista dello spazio La generazione di codice macchina per la specifica architettura viene effettuata on- the-fly

47 47 Schema di compressione Lo schema di compressione adattivo codifica linput utilizzando un dizionario Inizialmente il dizionario consiste di un piccolo numero di operazioni primitive (quali assignment, addition e multiplication) un piccolo numero di data item che appaiono nel programma che si sta processando (ad es. integer i, procedure P)

48 48 Generazione della rappresentazione intermedia La traduzione da codice sorgente in rappresentazione intermedia si compone di due fasi: 1. si fa il parsing del codice sorgente e si costruisce un AST ed una symbol table; dopo la fase di parsing, la symbol table viene scritta sullo slim binary (servirà per inizializzare i data symbol del dizionario e per informazioni di tipo per il generatore di codice) 2. lAST è attraversato e codificato in una sequenza di simboli del dizionario

49 49 Esplicitazione della Fase 2 (1/2) Lencoder processa interi sottoalberi dellAST alla volta e per ognuno ricerca nel dizionario una sequenza di simboli che esprima lo stesso significato Es. la chiamata di procedura P(i+1) può essere rappresentata dalla combinazione delle operazioni procedure call e addition e dai data symbol procedure P, variable i e constant 1 Dopo la codifica di una sottoespressione, il dizionario è aggiornato usando adattività e predizioni euristiche: ulteriori simboli che descrivono variazioni dellespressione appena codificata sono aggiunti al dizionario e simboli che si riferiscono a scope lessicali chiusi sono rimossi

50 50 Esplicitazione della Fase 2 (2/2) Es. dopo aver codificato lespressione i+1, i simboli i-plus-something e something- plus-one potrebbero essere aggiunti; supponiamo di incontrare più avanti nel processo di codifica lespressione (simile) i+j: questa potrebbe essere rappresentata utilizzando solo due simboli i-plus- something e j.

51 51 Traduzione da codice sorgente in slim binary

52 52 Slim binaries come moduli software Gli slim binaries possono essere integrati in un ambiente che supporti i moduli software, con caricamento dinamico Un modulo esporta funzionalità attraverso la propria interfaccia ed importa funzionalità attraverso linterfaccia di altri moduli Possibilità di sostituire un modulo con un altro che fornisce la stessa interfaccia La generazione del codice eseguibile avviene come parte del processo di caricamento dinamico ed in modo trasparente allutente

53 53 Confronto tra Slim Binaries e codice nativo (spazio occupato)

54 54 Aree di applicazione degli slim binaries Realizzazione di sistemi estendibili mediante aggiunta di moduli software Ununica versione per ogni componente, adatta a qualsiasi piattaforma; facilità di download da un server viste le ridotte dimensioni Riduzione dello spazio dei programmi rispetto alle versioni native

55 55 Bibliografia [1]A.V. Aho, R. Sethi, J.D. Ullman Compilers Principles, Techniques, and Tools Addison-Wesley [2]M. Franz, T. Kistler Slim Binaries Dicembre 1997


Scaricare ppt "1 Generazione di Codice Intermedio Implementazione di Linguaggi 2 A.A. 2004/2005 di Gualdani Alessandro."

Presentazioni simili


Annunci Google