Single Static Assignment

Z Wikipedii, wolnej encyklopedii

Single Static Assignment to postać programu używana przez kompilatory w trakcie optymalizacji, w której każdej zmiennej wartość przypisuje się tylko raz.

Np. dla programu:

   a = read();
   b = a + 2;
   a = a + 1;
   f(a, b);

Postać SSA to:

   a1 = read();
   b1 = a1 + 2;
   a2 = a1 + 1;
   f(a2, b1);

Jeśli wartość zmiennej w danym użyciu może pochodzić z kilku różnych przypisań, używa się specjalnej funkcji Φ.

Dla:

   a = 2;
   if (b > 0)
       a = 4;
   f(a);

Postacią SSA jest:

   a1 = 2;
   if (b > 0)
       a2 = 4;
   a3 = Φ(a1, a2);
   f(a3);

Postać SSA ułatwia wiele rodzajów optymalizacji. Najprostszą jest propagacja stałych – jeśli wiemy, że do danej zmiennej SSA została przypisana pewna wartość, możemy zastąpić wszystkie użycia tej zmiennej przez daną stałą, nawet jeśli w oryginalnym programie zmienna ta była używana też do innych celów.

Więzy[edytuj | edytuj kod]

Można też na tej postaci dokonywać łatwych wyliczeń więzów. Np. jeśli wiemy, że w programie jest wykonywany test czy a3 > 0, i że a3 = Φ(a1, a2), możemy sprawdzić czy test ten jest spełniony nawet bez znajomości dokładnej wartości a3.

Np. jeśli mamy kod tablicujący wartości funkcji sinus:

   int rozmiar = 100;
   float tab[rozmiar];
   i = 0;
   while (i < rozmiar)
   {
       tab[i] = sin(i * alpha);
       i = i + 1;
   }

Bezpieczeństwo wymaga, żeby wprowadzić testy, czy używany indeks jest poprawny (powinien to robić automatycznie kompilator; większość kompilatorów C/C++ tego nie robi, choć robią to kompilatory wielu innych języków):

   int rozmiar = 100;
   float tab[rozmiar];
   i = 0;
   while (i < rozmiar)
   {
       if (i < 0)
           raise InvalidArrayIndex;
       if (i >= rozmiar)
           raise InvalidArrayIndex;
       tab[i] = sin(i * alpha);
       i = i + 1;
   }

Po zmianie formy na SSA (blok pre-while oznacza instrukcje wykonywane przed porównaniem, blok taki nie występuje w kodzie źródłowym, jednak kompilator musi go wygenerować, żeby umieścić tam wyliczenia i wywołania funkcji występujące w złożonych warunkach):

   int rozmiar = 100;
   float tab[rozmiar];
   i1 = 0;
   pre-while {
       i3 = Φ(i1, i2);
   }
   while (i3 < rozmiar)
   {
       if (i3 < 0)
           raise InvalidArrayIndex;
       if (i3 >= rozmiar)
           raise InvalidArrayIndex;
       tab[i] = sin(i3 * alpha);
       i2 = i3 + 1;
   }

To że i3 >= rozmiar jest fałszywe kompilator wie z warunku, jaki występuje na początku bloku. Jednak i musi w trakcie wykonywania programu przynajmniej raz złamać to założenie – osiąga przecież wartość rozmiar żeby opuścić pętlę! Bez SSA trudniej byłoby rozważać takie przypadki. Podobnie kompilator może stwierdzić, że i1 >= 0, i że jeśli i3 >= 0, to i2 = i3 + 1 >= 0 (jeśli zajmie się też rozpatrywaniem kwestii przepełnienia).