Metody realizacji języków programowania/MRJP Wykład 8: Różnice pomiędzy wersjami
Linia 99: | Linia 99: | ||
przed pętle. | przed pętle. | ||
Prosty przykład: | |||
i = 0 | i = 0 | ||
'''do''' { | '''do''' { | ||
Linia 120: | Linia 120: | ||
(3) i3 = i2 + 1 | (3) i3 = i2 + 1 | ||
(4) '''if''' (i3 < 15) '''goto''' (2) | (4) '''if''' (i3 < 15) '''goto''' (2) | ||
W ogólnej sytuacji stosujemy schemat: | |||
jeśli <math>I</math> jest instrukcją <math>X = Y op Z</math>, oraz <math>Y</math>, <math>Z</math> nie odwołują się do <math>\phi</math>-węzła lub definicji wewnątrz pętli, wtedy | |||
* usuń instrukcję <math>I</math> (to jest instrukcja niezmiennicza dla tej pętli) | |||
* utwórz nową tymczasową zmienną <math>T</math> | |||
* wstaw nową instrukcję <math>T = Y op Z</math> oraz <math>X = T</math> do (ewentualnie nowego) bloku przed tą pętlą. | |||
Proces ten rozszerzamy wykonując iteracyjnie: | |||
* jeśli argumenty operacji pewnej instrukcji są zdefiniowane przez instrukcje niezmiennicze (tzn. ta instrukcja jest również niezmiennicza w pętli), wykonuj analogiczne przenoszenie kodu do bloku przed pęltą. | |||
== Zmienne indukcyjne == | == Zmienne indukcyjne == |
Wersja z 16:40, 16 lip 2006
Wprowadzenie
Jeszcze nie ma...
Podstawowe przekształcenia poprawiające kod
Propagacja kopii
Instrukcja postaci jest instrukcją kopiowania. Naturalne jest zastąpienie wystąpień zmiennej przez .
Przykład:
x = y a = x + y b = a + x
Zastępujemy przez:
x = y a = y + y b = a + y
Po wykonaniu propagacji kopii, instrukcja kopiująca jest martwym kodem. Ostatecznie otrzymamy:
a = y + y b = a + y
Eliminacja wspólnych podwyrażeń
Wyrażenie postaci jest nazywane wspólnym podwyrażeniem jeśli występuje ono w kilku miejscach oraz wartości zmiennych i użytych w kolejnych wystąpieniach nie zmieniły się po poprzednim obliczeniu . W takiej sytuacji można zastąpić kolejne wystąpienia przez wartość wyliczoną za pierwszym razem.
Przykład:
a = 4 * i b = 4 * i c = a + b
Drugie wyliczenie można zastąpić przez . Przekształcony blok:
a = 4 * i b = a c = a + b
Eliminację wspólnych podwyrażeń można stosować
- lokalnie - gdy przeszukujemy jeden blok bazowy,
- globalnie - dla całego grafu przepływu (trudniejsze z powodu pętli).
Usuwanie martwego kodu
Zmienną nazywamy żywą w danym miejscu programu, jeżeli jej wartość może zostać użyta. W pozostałych przypadkach nazywamy zmienną martwą w tym miejscu. Analogicznie definiujemy kod martwy, tzn. taki który oblicza wartości bezużyteczne. Na przykład wyliczenie wartości dla zmiennej martwej lub instrukcja postaci
if (false) ...
to kod martwy.
W oczywisty sposób kod martwy usuwamy.
Często kod martwy powstaje po propagacji kopii lub po zwijaniu stałych.
Zwijanie stałych
Jest to proces upraszczania wyrażeń stałych. Przykład:
x = 30 * 2
można zastąpić przez
x = 60
Zwijanie stałych czasem wykonuje się na wcześniejszych etapach kompilacji.
Propagacja stałych
Jest to proces zastępowania wystąpień stałych przez ich wartości. Przykład
x = 60 y = 3 * x
można zastąpić przez
x = 60 y = 3 * 60
i dalej po zwinięciu wartości w drugim przypisaniu otrzymamy
x = 60 y = 180
W wyniku propagacji stałych instrukcja definiująca stałą staje się martwym kodem, który można usunąć w trakcie eliminacji martwego kodu.
Optymalizacja pętli
Wstępniak!
Przemieszczenie kodu
Polega na zmiejszeniu ilości kodu w pętli, przez przemieszczenie obliczeń niezmienniczych przed pętle.
Prosty przykład:
i = 0 do { a = 13 i = i + 1 } while (i < 15)
przedstawiamy w postaci SSA:
(0) i1 = 0 (1) i2 = (i1,i3) (2) a = 13 (3) i3 = i2 + 1 (4) if (i3 < 15) goto (1)
Wyrażeniem niezmienniczym jest instrukcja przypisania w linii (2). Możemy ją przemieścić przed pętlę:
(0) i1 = 0 (1) a = 13 (2) i2 = (i1,i3) (3) i3 = i2 + 1 (4) if (i3 < 15) goto (2)
W ogólnej sytuacji stosujemy schemat: jeśli jest instrukcją , oraz , nie odwołują się do -węzła lub definicji wewnątrz pętli, wtedy
- usuń instrukcję (to jest instrukcja niezmiennicza dla tej pętli)
- utwórz nową tymczasową zmienną
- wstaw nową instrukcję oraz do (ewentualnie nowego) bloku przed tą pętlą.
Proces ten rozszerzamy wykonując iteracyjnie:
- jeśli argumenty operacji pewnej instrukcji są zdefiniowane przez instrukcje niezmiennicze (tzn. ta instrukcja jest również niezmiennicza w pętli), wykonuj analogiczne przenoszenie kodu do bloku przed pęltą.