Traduzione cap.5 1 CAP.5 LA PRODUZIONE DI CODICE 5.1 I languaggi intermedi 5.2 Le instruzioni di assegnamento 5.3 I collegamenti e le espressioni booleane 5.4 La ripresa indietro
Traduzione cap I linguaggi intermedi analizzatore sintattico controllatore semantico produttore di codice intermedio produttore di codice Vantaggi: separare la parte frontale dalla parte finale, portabilità migliorata, facilità di ottimizazzione. Rappresentazione di un programma con un albero astratto o con un grafo aciclico orientato. Qui, questi alberi saranno rappresentati da un pseudo-codice, il codice con tre indirizzi, ben adattato alle strutture di controllo embricate e alle espressioni algebriche. Codice di bassissimo livello.
Traduzione cap.5 3 Istruzioni x <- y op z dove x, y et z sono costanti o nomi espliciti o prodotti dal compilatore stesso (temporanei) e op è un operatore qualsiasi (rappresentato da _nome ). Esempio: a := b * - c + b * - c t1 <- _opp c t2 <- b _mul t1 t3 <- _opp c t4 <- b _mul t3 t5 <- t2 _plu t4 a <- t5 è rappresentato da : t1 <- _opp c t2 <- b _mul t1 t3 <- t2 _plu t2 a <- t3 Albero astratto, non ottimizzato Grafo aciclico, ottimizzato Temporari = nodi interni dellalbero o del grafo aciclico.
Traduzione cap.5 4 Varie istruzioni, con etichette simboliche se necessario, per controllare il flusso. Assegnamento x <- y op z Assegnamento x <- op y Copia x <- y Collegamento incondizionale goto L Collegamento condizionale if x oprel y goto L Puntatori e indirizzi x <- &y et x <- *y et *x <- y Etichette etiq (niente operazione) Vediamo come produrre codice a tre indirizzi con un traduttore diretto dalla sintassi.
Traduzione cap.5 5 RegolaAzione E E +EE.place := NewTemp ; E.code := E 1.code || E 2.code || Print (E. place <- E 1. place _plu E 2. place ) E E *EE.place := NewTemp ; E.code := E 1.code || E 2.code || Print (E. place <- E 1. place _mul E 2. place ) E – EE.place := NewTemp ; E.code := E 1.code || Print (E. place <- _opp E 1. place ) E (E )E.place := E 1.place ; E.code := E 1.code E id E.place := id.place ; E.code := Qui, || disegna la concatenazione, NewTemp fabbrica una nuova variabile temporanea e Print stampa suoi argomenti.
Traduzione cap Le istruzioni di assegnamento Interazione con la tabella dei simboli. Nelle istruzioni, invece di utilizzare i nomi, si ricerca nella tabella dei simboli se questo nome già esiste. Se è il caso, viene ritornato suo indice. Si no, un errore è segnalato. Qui segue una parte della grammatica attribuita per gli assegnamenti : I id:= E{ p := Look ( id.name ) ; if p <> nil then I.code := E.code || Print ( id.place <- E.place ) else error } E E +E{ E.place := NewTemp ; E.code := E 1.code || E 2.code || Print ( E. place <- E 1. place _plu E 2. place )} E (E ){ E.place := E 1.place ; E.code := E 1.code } E id { p := Look ( id.name ) ; if p <> nil then begin E.place := id.place ; E.code := end else error }
Traduzione cap.5 7 Ottimizazzione delluso delle variabili temporanee. Si possono reutilizzare variabili temporanee. Infatti, queste sono "liberate" dopo la loro uso come argomento di una operazione. Si può dunque modificare la funzione NewTemp per ritornare la prima variabile temporanea libera. Assegnamento degli elementi delle tabelle Supponiamo che le tabelle abbiano una sola dimensione, con indici da 0 fino a n – 1. Una tabella è dichiarata come T[n].
Traduzione cap.5 8 Due tipi di costruzione utilizzano elementi di tabelle: E id[ E ] dove in elemento di una tabella è utilizzato invece di un identificatore in unespressione, e I id[ E ]:= E dove si realizza un assegnamento a un elemento di una tabella. Infatti, lindice indica solo uno spostamento dellindirizzo in memoria. T[5] è dunque sinonimo di *(T+5) (o, più esattamente trasformato). Questo è realizzato con E id[ E ] {p := NewTemp ; E.place := NewTemp ; E.code := E 1.code || Print ( p <- id.place _plu E 1. place ) || Print ( E.place <- * p ) } I id[ E ]:= E {p := NewTemp ; I.code := E 1.code || E 2.code || Print ( p <- id.place _plu E 1. place ) || Print ( * p <- E 2. place ) } (Le verificazioni della presenza nella tabella dei simboli sono state omesse)
Traduzione cap.5 9 Esempio: Supponiamo che T e U siano due tabelle, listruzione T[a + b] := U[b + T[c]] produce il codice: t1 <- a _plu b t2 <- T _plu c t3 <- *t2 t4 <- b _plu t3 t5 <- U _plu t4 t6 <- *t5 t7 <- T _plu t1 *t7 <- t6
Traduzione cap I collegamenti e le espressioni booleane Grammatica utilizzata per espressioni booleane: E E or E | E and E | not E | ( E ) | E oprel E | true | false Convenzione numerica (1 = true, 0 = false) Supponiamo che abbiamo operazioni corrispondenti nel codice a tre indirizzi ( _or _and _not e per operatori relazionali come _equ ). La valutazione delle espressioni booleane non presenta differenze con quella delle espressioni algebriche (Se si vuole una distinzione tra numeri e valori logici, è meglio usare unaltro simbolo invece del E). Abbiamo bisogno di un construttore di etichette simboliche NewLabel.
Traduzione cap.5 11 Azioni per un confronto. E E > E p := NewLabel ; q := NewLabel ; E.place := NewTemp ; E.code := E 1. code || E 2. code || Print ( if E 1. place _leq E 2. place goto p ) || Print ( E.place <- 1 ) || Print ( goto q ) || Print ( p ) Print ( E.place <- 0 ) || Print ( q ) Regola Azioni
Traduzione cap.5 12 Azioni per un while. I while E do I p := NewLabel ; q := NewLabel ; I.code := Print ( p ) || E.code || Print ( ifE. place _equ 0 goto q ) || I 1. code || Print ( goto p ) || Print ( q ) Regola Azioni
Traduzione cap.5 13 Azioni per un if...then I if E then I Regola Azioni p := NewLabel ; I.code := E.code || Print ( ifE. place _equ 0 goto p ) || I 1. code || Print ( p )
Traduzione cap.5 14 Azioni per un if...then..else I if E then I else I Regola Azioni p := NewLabel ; q := NewLabel ; I.code := E.code || Print ( ifE. place _equ 0 goto p ) || I 1. code || Print ( goto q ) || Print ( p ) || I 2. code || Print ( q )
Traduzione cap.5 15 Esempio while a > b do if c > d then a := 1 else b := 0 produce il codice: etiq7 if a _leq b goto etiq1 t1 <- 1 goto etiq2 etiq1 t1 <- 0 etiq2 if t1 _equ 0 goto etiq8 if c _leq d goto etiq3 t2 <- 1 goto etiq4 etiq3 t2 <- 0 etiq4 if t2 _equ 0 goto etiq5 a <- 1 goto etiq6 etiq5 b <- 0 etiq6 goto etiq7 etiq8 Questo è un poco pesante (grande numero di etichette). Si può ottimizzare il codice.
Traduzione cap.5 16 Certe volte si utilizza un metodo di valutazione pigra delle congiunzioni o disgiunzioni. In questa, se il valore dellespressione non necessita il calcolo del secondo termine dellespressione, non viene calcolata. E E land E Azioni p := NewLabel ; q := NewLabel ; E.code := E 1.code || Print ( if E 1. place _equ 0 goto p ) || E 2. code || Print ( if E 2. place _equ 0 goto p ) || Print ( E.place <- 1 ) || Print ( goto q ) || Print ( p ) || Print ( E.place <- 0 ) || Print ( q )
Traduzione cap La ripresa indietro Lottimizzazione delle etichette può essere realizzato in un secondo passo sul codice a tre indirizzi prodotto. Si può anche evitare completamente luso di etichette simboliche colla tecnica di ripresa indietro. Come si vede nellesempio precedente, le etichette non sono create secondo lordine nel quale sono utilizzate nel codice. Certi collegamenti devono di più essere fatti verso istruzioni non ancora scritte. Nella tecnica di ripresa indietro, i collegamenti sono lasciati provvisoriamente indeterminati e vengono completati quando si produce il codice dellistruzione di destinazione. Questi collegamenti sono gestiti come liste di numeri di istruzioni. (supponiamo qui che ogni istruzione ha un numero proprio).
Traduzione cap.5 18 Questo permette anche di non scrivere i pezzi di codice in modo separato, ma direttamente su una fila, lasciando però righe di goto incomplete. Per gestire le liste, abbiamo CreateList ( i ) fabbrica una nuova lista che contiene i e ritorna un puntatore verso essa; Fusion ( p 1, p 2 ) concatena due liste e ritorna un puntatore; Back ( p, i ) inserisce i come etichetta in tutte le istruzioni della lista p, poi libera lo spazio occupato da p. Una variabile globale numero contiene il numero della prossima istruzione da produrre. Un altro vantaggio è che la grammatica prodotta è sempre S-attribuita. Ogni espressione booleana ha due attributi E.true e E.false che sono liste di istruzioni da riprendere più tardi.
Traduzione cap.5 19 E E lor M E{ Back (E 1.false, M.num ) ; E.true := Fusion (E 1.true, E 2.true ) ; E.false := E 2.false } E E land M E{ Back (E 1.true, M.num ) ; E.true := E 2.true ; E.false := Fusion (E 1.false, E 2.false ) } E not E{ E.false := E 1.true ; E.true := E 1.false } E ( E ){ E.true := E 1.true ; E.false := E 1.false } E id oprel id { E.true := CreateList (numero ) ; E.false := CreateList (numero + 1) ; Print ( if id 1.place oprel.op id 2.place goto ) ; Print ( goto ) } E true{ E.true := CreateList (numero ) ; Print ( goto ) } E false{ E.true := CreateList (numero ) ; Print ( goto ) } M { M.num := numero }
Traduzione cap.5 20 Nellesempio a < b or c < d and e < f si ottiene finalmente: 100 : if a _les b goto 101 : goto : if c _les d goto : goto 104 : if e _les f goto 105 : goto E.true vale {100, 104} E.false vale {103, 105}. I collegamenti di queste istruzioni saranno completati più tardi. Si nota che abbiamo utilizzato la valutazione pigra. Implementare la valutazione normale è un pò più complicato. Ora, la traduzioni dei collegamenti condizionali. I if E then I | if E then I else I | while E do I | begin L end | A L L ; I | I dove L è una lista di istruzioni e A un assegnamento.
Traduzione cap.5 21 Per la traduzione, si utilizzano due liste L.next et I.next che contengono i numeri delle istruzioni che contengono un collegamento verso listruzione che segue L o I. Caso del while do : I while M E do M I { Back ( E.true, M 2.num ) ; Back (I 1.next, M 1.num ) ; Print ( goto M 1.num ) ; I.next := E.false } Caso del if then else : I if E then M I N else M I{ Back ( E.truei, M 1.num ) ; Back ( E.false, M 2.num ) ; I.next := Fusion (I 1.next, N.next, I 2.next ) } Il marchio N serve a passare sopra il secondo I. N { N.next := CreateList (numero ) ; Print ( goto ) }
Traduzione cap.5 22 Lassegnamento inizializza I.next e produce il codice adeguato: I id:= E { I.next := CreateList ( ) ; Print ( id.place <- E.place ) } Per la riduzione in una lista : L L ; M I {Back (L 1.next, M.num ); L.next := I.next } Si ottiene la grammatica seguente:
Traduzione cap.5 23 I if E then M I{ Back ( E.true, M.num ) ; I.next := Fusion (E.false, I 1.next ) } I if E then M I N else M I{ Back ( E.true, M 1.num ) ; Back ( E.false, M 2.num ) ; I.next := Fusion (I 1.next, N.next, I 2.next ) } I while M E do M I { Back ( E.true, M 2.num ) ; Back (I 1.next, M 1.num ) ; Prod ( goto M 1.num ) ; I.next := E.false } M { M.num := numero } N { N.next := CreateList (numero ) ; Prod ( goto ) } I begin L end{ I.next := L.next } I id:= E{ I.next := CreateList ( ) ; Print ( id.place <- E.place ) } L L ; M I {Back (L 1.next, M.num ); L.next := I.next }
Traduzione cap.5 24 if a < b lor c < d land e < f then x := 1 else begin x := 0 ; u := 1 end ; while a < b do x := x + 1 ; Così, il frammento: produce : 100 : if a _les b goto : goto : if c _les d goto : goto : if e _les f goto : goto : x < : goto : x < : u < : if a < b goto : goto 112 : t1 := x _plu : x <- t1 114 : goto 110 Il risultato è la variabile L e L.next vale {111}.