Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
OpublikowałPaweł Klatka Został zmieniony 9 lat temu
1
Generowanie kodu pośredniego Java ML Pascal C C++ Alpha Pentium Sparc Java ML Pascal C C++ Alpha Pentium Sparc MIPS IR IR – intermediate representation
2
Cechy kodu pośredniego Z poprzedniej strony wynikają różne ważne wnioski m.in. - nawet gdy nasz kompilator ma tylko jeden front-end i tylko jedną maszynę docelową to warto starannie dobrać sposób reprezentowania kodu pośredniego, - kod pośredni powinien być łatwy w produkcji, - powinno się zapewnić łatwą translację na kod realnej maszyny docelowej, - każda konstrukcja powinna mieć jasne i proste znaczenie (ma to znaczenie dla procesu ulepszania kodu) Kod pośredni w.9 4-17-2002 2
3
Drzewa jako reprezentacja kodu Oczywiście, można uznać, że reprezentacją kodu jest drzewo składniowe. Kod pośredni w.9 4-17-2002 3 assign | | id a + | | * | | id b uminus | | id c * | | id b uminus | | id c a := b*-c + b*-c
4
Drzewa jako reprezentacja kodu II Oczywiście, można uznać, że reprezentacją kodu jest drzewo składniowe. Przestaje to być zaskakujące gdy drzewo takie zapiszemy liniowo. Kod pośredni w.9 4-17-2002 4 assign | | id a + | | * | | id b uminus | | id c * | | id b uminus | | id c 0 id b 1 id c 2 uminus 1 3 * 0 2 4 id b 5 id c 6 uminus 1 7 * 4 6 8 + 3 7 9 id a 10 assign 9 8 11
5
Dagi jako reprezentacja kodu Zamiast drzew możemy użyć dagów tzn directed acyclic graphs czyli grafów skierowanych acyklicznych. Kod pośredni w.9 4-17-2002 5 assign | | id a + | | * | | id b uminus | | id c 0 id b 1 id c 2 uminus 1 3 * 0 2 4 + 3 3 5 id a 6 assign 9 8 7
6
“Czwórki” czyli kod trójadresowy Po udekorowaniu drzewa składniowego odpowiednimi atrybutami np. typy, offsety nazw lokalnych, apetyt na wielkości tymczasowe,... można zapisać drzewo (lub dag) w postaci liniowej. Kod czwórkowy jest ciągiem instrukcji o jednej operacji op i trzech adresach. Ogólna postać instrukcji jest: x := y op z x, y i z są stałymi, nazwami albo wygenerowanymi przez kompilator zmiennymi tymczasowymi bądź etykietami, op oznacza operator arytmetyczny, relacyjny bądź logiczny. Kod pośredni w.9 4-17-2002 6
7
Repertuar instrukcji trójadresowych 1 instrukcje przypisania x := y op z 2 instrukcje przypisania x := op z 3 instrukcje kopiujące x := y 4 skoki bezwarunkowe goto L 5 skoki warunkowe if x oprel y goto L 6 instrukcje sekwencji wywołujących param x i call p, n oraz return y 7 operacje na tablicach x :=a[i] oraz a[i] := x 8 operacje na wskaźnikach x := &y, x := *y, *x :=y ____ Jest to przykład. Zestaw operatorów i repertuar instrukcji zależy od języka źródłowego i od zbioru maszyn docelowych. Kod pośredni w.9 4-17-2002 7
8
Przykład Generowanie kodu pośredniego może odbywać się dzięki akcjom semantycznym operującym na atrybutach węzłów drzewa składniowego. Gramatyka S id:=E E E 1 +E 2 E E 1 * E 2 E - E 1 E (E 1 ) E id Atrybutami nieterminali niech będą E.pozycja i E.kod oraz S.kod. Metoda nowatymcz obiektu g klasy GeneratorTymcz zwraca w kolejnych wywołaniach nowe nazwy t 1, t 2,... Kod pośredni w.9 4-17-2002 8
9
Przykład c.d. Produkcja Reguła semantyczna S id:=E S.kod := E.kod ^ gen( id.pozycja ':=' E.pozycja E E 1 +E 2 E.pozycja := g.nowatymcz; E.kod := E 1.kod ^ E 2.kod ^ gen(E.pozycja ':=' E 1.pozycja '+' E 2.pozycja) E E 1 * E 2 E.pozycja := g.nowatymcz; E.kod := E 1.kod ^ E 2.kod ^ gen(E.pozycja ':=' E 1.pozycja '*' E 2.pozycja) E - E 1 E.pozycja := g.nowatymcz; E.kod := E 1.kod ^ gen(E.pozycja ':=' '-' E 2.pozycja) E (E 1 ) E.pozycja := E 1.pozycja; E.kod := E 1.kod E id E.pozycja := id.pozycja; R.kod := ' ' Kod pośredni w.9 4-17-2002 9
10
Przykład - zastosowanie Zastosowanie tych produkcji wraz z odpowiadającymi im regułom semantycznym daje dla drzewa składniowego ze str. 3 następujący ciąg instrukcji kodu pośredniego t1 := - c t2 := b * t1 t3 := - c t4 := b * t3 t5 := t2 + t4 a := t5 Uwaga. W większości kompilatorów kod taki zapisywany jest na pliku. Kod pośredni w.9 4-17-2002 10
11
Kod instrukcji sterujących Zobaczmy jak wygląda generowanie kodu pośredniego dla instrukcji while. Potrzebne nam są teraz dwa atrybuty: S.początek i S.wyjście. Produkcja Reguła semantyczna S while E do S 1 S.początek := nowaEtykieta; S.wyjście := nowaEtykieta; S.kod := gen(S.początek ':') ^ E.kod ^ gen('if' E.pozycja=0 'goto' S.wyjście) ^ S1.kod ^ gen('goto' S.początek) ^ gen(S.wyjście ':') Daje to następujący obraz Kod pośredni w.9 4-17-2002 11
12
Kod instrukcji sterujących II Kod pośredni w.9 4-17-2002 12 S.początek: S.wyjście: E.kod if E.pozycja = 0 goto S.wyjście S 1.kod goto S.początek
13
Metoda genkod w obiektach-węzłach Jest oczywiste, że tworząc kod pośredni przy pomocy metod programowania obiektowego jesteśmy skłonni raczej posłużyć się metodami wirtualnymi niż wykorzystywać reguły semantyczne. Istota algorytmu pozostaje bez zmian, inaczej tylko go zapisujemy. W każdej podklasie klasy Expression deklarujemy odpowiednią metodę genkod w taki sposób, że w klasach Suma i Iloczyn pojawią się metody genkod różniące się wygenerowaną instrukcją trójadresową: gen(E.pozycja ':=' E 1.pozycja '+' E 2.pozycja) w węźle sumy, i gen(E.pozycja ':=' E 1.pozycja '*' E 2.pozycja) w węźle iloczynu. Kod pośredni w.9 4-17-2002 13
14
Szkic obiektowego generowania kodu class Expression () { Expression left, right; KodPośredni kod; Adres pozycja; void genkod(){} } // Expression; class Suma extends Expression() { void genkod() { left.gencod(); right.gencod(); t = g.nowaTymczas; gen('t := left.pozycja '+' right.pozycja); } } // Suma class Iloczyn extends Expression () { // podobnie } Kod pośredni w.9 4-17-2002 14
15
Oszczędne używanie nazw tymczasowych Możemy przyjąć, że szafujemy nazwami tymczasowymi bez ograniczeń, że jest ich dowolnie dużo do naszej dyspozycji. Tak postępujemy w przypadku kompilatorów optymalizujących bo... Możliwe są dwa podejścia: najpierw wygenerować wiele nazw tymczasowych, a potem umieszczać kilka nazw tymczasowych pod jednym adresem – sprowadza się to do kolorowania pewnego grafu. Inne podejście polega na obserwacji, że wiele nazw tymczasowych jest tworzonych dla jednokrotnego wykorzystania. Przykład oblicz wartość wyrażenia E1 i zapisz w t1 oblicz wartość wyrażenia E2 i zapisz w t2 t3 := t1 + t2 Te zmienne nie będą używane w żadnym innym miejscu programu! Kod pośredni w.9 4-17-2002 15
16
Przykład dyscypliny stosowej Rozpatrzmy instrukcję x := a*b+c*d-e*f Przyjmijmy, że zmienne tymczasowe mają tę samą szerokość. Utrzymujemy licznik zmiennych tymczasowych L. Za każdym razem gdy potrzebna jest nowa zmienna użyjemy nazwy $L. Za każdym razem gdy użyjemy zmiennej tymczasowej zmniejszamy licznik o jeden L := L-1. Instrukcja Wartość L 0 $0:=a*b 1 $1:=c*d 2 $0:=$0+$1 1 użyto dwu zmiennych jako argumentów $1:=e*f 2 $0:=$0-$1 1 x := $0 0 Kod pośredni w.9 4-17-2002 16
17
Adresowanie elementów w tablicy W niemal każdym języku programowania elementy w tablicy zajmują kolejne miejsca w pamięci. Wynika stąd, że można szybko do nich dotrzeć element jego adres A[i] baza+(i – lower(A) * w gdzie baza – adres pamięci zarezerwowanej dla tablicy w - szerokość elementu w tablicy A, zależy od typu, lower(A) – dolne ograniczenie indeksów tablicy A UWAGA. Kompilator, który nie dodaje sprawdzenia zakresu indeksu i jest niedobrym produktem. Dlaczego? Jak to zrobić? Kod pośredni w.9 4-17-2002 17
18
Adresowanie tablic dwuwymiarowych W wielu kompilatorach tablice dwuwymiarowe umieszczane są w tablicy jednowymiarowej. Zachodzą wtedy dwa wypadki: tablica przechowywana jest wierszami np. Pascal, C, albo A[i,j] ma adres baza+((i-dół wierszy )*n2+j-dół kolumn )*w gdzie n2 = góra kolumn – dół kolumn +1 baza = adres początku tablicy w pamięci, tablica przechowywana jest kolumnami np. Fortran. A[i,j] baza+((j-dół kolumn )*n1+i-dół wierszy )*w Zauważ, że zastosowano schemat Hoernera, co jest ważne w przypadku tablic więcej wymiarowych. Kod pośredni w.9 4-17-2002 18
19
Adresowanie tablic dwuwymiarowych II W Loglanie i w Javie tablice są obiektami pewnego rodzaju. Tablice jednowymiarowe adresuje się tak samo. Tablice dwu- i więcej wymiarowe traktuje się jak tablice jednowymiarowe tablic o n-1 wymiarach. Czyli zapis A[i,j] jest skrótem dla A[i][j] Kod pośredni w.9 4-17-2002 19
20
Tworzenie kodu dla wyrażeń logicznych Kod pośredni w.9 4-17-2002 20
21
Instrukcje sterujące Kod pośredni w.9 4-17-2002 21
22
Wyra żeniia logiczne realizowane przez skoki warunkowe Kod pośredni w.9 4-17-2002 22
23
Backpatching p Kod pośredni w.9 4-17-2002 23
24
Wywołania procedur Rozważmy gramatykę instrukcji procedury S call id ( Elist) Elist Elist, E | E Jak pamiętamy z poprzedniego wykładu instrukcja procedury tłumaczy się na sekwencję wywołującą złożoną z ciągu poleceń obliczających kolejne parametry aktualne i przekazujących je instrukcją param x, oraz z instrukcji call p, n która wywołuje procedurę biblioteki wspomagania przetwarzania (run-time system, lub maszyna wirtualna). Polecenie call ma dwa argumenty: pierwszym jest informacja związana z nazwą procedury, drugim jest liczba n przekazywanych argumentów. Kod pośredni w.9 4-17-2002 24
25
Wywołania procedur II Rozpatrzmy prosty przykład: instrukcja procedury postaci id(E, E,...,E) załóżmy, że parametry są przekazywane przez referencję trzeba więc wygenerować kod pośredni prowadzący do obliczenia tych argumentów, które nie są nazwami prostymi, a po nich umieścić instrukcje param, po jednej dla każdego argumentu. Można to zrobić posługując się kolejką. Akcja semantyczna dla Elist -> Elist będzie zawierała krok zapamiętujący atrybut E.pozycja w kolejce kolejka. Akcja semantyczna dla S -> call id (Elist) wygeneruje instrukcję param dla każdego elementu z kolejka Kod pośredni w.9 4-17-2002 25
26
Wywołania procedur III p Kod pośredni w.9 4-17-2002 26
27
p p Kod pośredni w.9 4-17-2002 27
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.