Savijanje konstanti

**Savijanje konstanti** i **širenje konstanti**su povezane optimizacije kompajlera koje mnogi moderni kompajleri.  Napredna forma širenja konstanti, poznata kao proređeno širenje konstanti, može mnogo preciynije da izvrši širenje konstanti i u isto vreme da uklanja mrtav kod.

## Savijanje konstanti

**Savijanje konstanti** je proces prepoynavanja i izračunavanja konstantnih izraza za vreme prevođenja, a ne u vreme izvršavanja programa. Termi u konstantnim izrazima su obično prosti literali, kao na primer celobrojni literal `2`, ali, takođe, to mogu biti i promenljive čija se vrednost zna već za vreme prevođenja. Razmotrimo sledeću naredbu:


```c
i = 320 * 200 * 32;
```


Mnogi napredni prevodioci neće stvarno generisati dve instrukcije prevođenja i prostor za ovu naredbu. Umesto toga, identifikovaće konstrukte poput ovih i zameniće izračunatu vrednost u vreme samog prevođenja.(u ovom slučaju, 2,048,000).  Rezultujući kod će učitati **jednu** već izračunatu vrednost, umesto učitavanja i množenja nekoliko vrednosti.

Savijanje konstanti može čak koristiti i aritmetičke identitete. Kada je `x` celobrojnog tipa, vrednost izraza `0 * x` je nula, čak iako prevodilac ne zna vrednost promenljive `x`.

Savijanje konstanti se ne primenjuje samo na brojeve. Nadovezivanje (konkatenacija) stringovnih(nizova karaktera) literala i konstantnih nizova karaktera može biti savijeno u konstantu.  Kod poput ovog: `"abc" + "def"` može biti zamenjen kodom: `"abcdef"`.

Savijanje konstanti može biti urađeno i u front-endu prevodioca na IR drvetu, koje predstavlja izvorni jezik višeg nivoa, pre nego što je preveden u  adresni kod drveta, ili u back endu, kao dodatak širenju konstanti.

## Savijanje konstanti i cross prevodilac

Pri implementaciji cross prevodioca, mora se obezbediti da se ponašanje aritmetičkih operacija na host arhitekturi odgovara onoj na ciljnoj arhitekturi. U suprotnom, ako dozvolimo savijanje konstanti, možemo, nesmotreno, promeniti ponašanje samog programa. Ovo je od velikog značaja za operacije sa decimalnim zaretom, čija implemetacija može veoma da se razlikuje.

## Širenje konstanti

**Širenje konstanti** proces zamene poznatih vrednosti nekih konstanti za vreme prevođenja. Ove konstante uključuju one definisane iznad, kao i intrinsic funkcije primenjene na konstantne vrednosti. Razmotrimo sledeći pseudokod:


```c
int x = 14;
  int y = 7 - x / 2;
  return y * (28 / x + 2);
```


Širenje(propagacija) promenljive x čeka:


```c
int x = 14;
  int y = 7 - 14 / 2;
  return y * (28 / 14 + 2);
```


Nastavak širenja zaustavlja i sledeći kod (koji bi dalje bio optimizovan putem uklanjanja mrtvog koda promenljivih x i y.)


```c
int x = 14;
  int y = 0;
  return 0;
```


Širenje konstanti je u kompajleru predtsavljeno korišćenjem analiziranja rezultata dostupnih definicija. Ako sve dostupne definicije jedne promenljive predstavljaju istu dodelu tj. dodeljuju istu vrednost konstante promenljivoj, tada promenljiva ima konstantnu vrednost i može biti zamenjena konstantom. 
Širenje konstanti može uzrokovati i pojednostalvjivanje uslovnih grana, njihovom transformacijom u jendu ili više naredbi bez uslova, u slučaju kada uslovni izraz može biti izračunat u vreme prevođenja, kako bi se utvrdila njegova konačna vrednost.

## Optimizacija na delu

Savijanje konstanti i njihovo širenje, najčešće su korišćene zajedno, kako bi se postiglo što više redukcija i uprošćavanja. Iterativno ih koristimo, preklapajući ih, dok ni jedna izmena više ne bude moguća. 
Razmotrimo sledeći pseudokod:


```c
int a = 30;
  int b = 9 - (a / 5);
  int c;

  c = b * 4;
  if (c > 10) {
     c = c - 10;
  }
  return c * (60 / a);
```


Primenjujemo širenje konstanti jednom, zatim savijamo konstante, nakon toga kod izgleda ovako:


```c
int a = 30;
  int b = 3;
  int c;

  c = b * 4;
  if (c > 10) {
     c = c - 10;
  }
  return c * 2;
```


Ponavljamo oba koraka dva puta, sledi rezultat:


```c
int a = 30;
  int b = 3;
  int c;

  c = 12;
  if (true) {
     c = 2;
  }
  return c * 2;
```


Kako su `a` i `b` uprošteni u konstante i njihove vrednosti zamenjene svuda gde su se pojavljivale, prevodilac sada primenjuje uklanjanje mrtvog koda kako bi ih odbacio i time dodatno uprostio kod :


```c
int c;
  c = 12;

  if (true) {
     c = 2;
  }
  return c * 2;
```

U kodu iznad, umesto `True` može stajati broj 1 ili bilo koja Boolean oznaka, zavisno od prevodioca. Tradicionalnim širenjem konstanti, dobijamo samo ovoliku optimizaciju. Ne možemo promeniti strukturu programa.

Postoji još jedna, slična, optimizacija, zvana proređeno širenje konstanti, koja bira odgovarajuću granu na osnovu `if-condition`. . Prevodilac sada može da otkrije da će se `if` naredba izvršiti u true, `c` može biti eliminisana, kod se smanjuje još više:


```c
return 4;
```


Ako bi ovaj pseudokod činio telo funkcije, prevodilac, dalje, može iskoristiti  informaciju o tome da će rezultat izvršavanja biti ceo broj`4` kako bi uklonio nepotrebne pozive funkcije, rezultujući daljim poboljšanjem performansi.

## Povezano
* Control flow graph
* Use-define chain and SSA form
* Copy propagation
* Common subexpression elimination
* Partial evaluation

## Reference

*https://en.wikipedia.org/wiki/Constant_folding

## Dodatna literatura
*