EGCS 1.1
                                  -=-=-=-=-=

          V minulém čísle Výhně jsem psal o novém  projektu EGCS (Extended
      GNU Compiler  System), který má za úkol otestovat otevřený  vývojový
      model na GNU C Compileru,  zrychlit jeho vývoj, a vytvořit prostředí
      pro testování  nových  nápadů a složitějších změn. Slíbil jsem také,
      že se časem napíšu, jak si vede.

          Dnes byla  vydána  druhá  major  release  (1.1) a proto  myslím,
      že je vhodný  čas. Co se tedy  podařilo  vyvinout za necelý rok mezi
      1.0 a 1.1?  Pouze  patch z verze  1.0 na  verzi  1.1 je  dlouhý  6MB
      po zabalení  gzipem.  Zdrojové kódy jsou  dlouhé  11MB, což ukazuje,
      že změn není málo. Nejdůležitější jsou:

                   Global CSE/Partial Redundancy Elimination

          Jedna z nejšílenějších  optimalizací,  kterou GCC provadí je CSE
      (Common  Subexpression  Elimination).  Jejím  úkolem je najít stejné
      podvýrazy a použít  výsledek z  předchozího.  Tato  optimalizace  je
      nečekaně efektivní, protoře už jenom kód:

          if (pole[i]<0) pole[i]=-1;

          takovou optimalizaci umožňuje. Bez ní by se adresa prvku pole[i]
      zbytečně počítala dvakrát.

          Pořádně  fungující  CSE je ale  velmi  složité,  ptotože  stejné
      hodnoty lze spočítat mnoha různými způsoby a proto CSE musí provádět
      mnoho  algebraických  úprav.  Proto  je  cse.c  jeden  z  nejdelších
      a nekomplikovanějších zdrojových souborů v GCC.

          Toto CSE má ale jedno velké  omezení. Umí pracovat pouze v rámci
      jednoho  basic bloku (to je blok instrukcí, ve kterém  nejsou  žádné
      skoky a ani do něho žádné  skoky nevedou,  instrukce se tedy provádí
      pokaždé  ve  stejném  pořadí).  Například i v  úvodním  příkladu  if
      generuje skok a tak CSE optimalizaci neprovede.

          Doug  Evans ale  implementoval  globalní  CSE,  které má za úkol
      tento problém  odstranit.  Vytvoří si jakýsi  vývojový  diagram celé
      funkce a udělá  podrobnější  analýzy a díky tomu může detekovat tyto
      komplikovanější případy. Například v následujícím  příkladu odstraní
      druhý výskyt expr1:

                expr1
               /    \
              /      \
           expr2   expr3
             |        |
           expr4   expr2
              \      /
               \    /
                expr1

          Navíc ale implementoval i PRE (Partial  Redundancy Elimination),
      která  dokáže  najít i expr2 a umístit ji před rozvětvení  programu.
      Expr2 se tedy  počítá  pouze  jednou.  PRE  dokonce  dokáže  objevit
      invarianty  ve smyčkách  (výrazy co mají  pokaždé  stejnou  hodnoru)
      a spočítat je vně smyčky. Tato optimalizace je ale vypnuta,  protože
      ji mnohem lépe provádí loop optimizer.

          V současné  implementaci GCSE provádí méně algebraických  úprav,
      než CSE a proto je dokonce  rychlejší.  Spolu se starým  CSE  odvádí
      velmi  dobrou  práci.  Jeho  výsledky se ale projevují  zejména v ne
      příliš optimalizovaných  programech (protože jinak udělá programátor
      práci za něj) a proto nelze  čekat  žádné  větší  zrychlení  interní
      smyčky  Vašeho  programu.  Pomůže ale v případě velmi komplikovaných
      algoritmů.

          Tento  kód  je  také   zajímavý  tím,  že  přináší   základ  pro
      optimalizaci   známou  jako  Lazy  Code  Motion.  Jejím   úkolem  je
      přeházet  kód  tak,  aby se snížil  rozsah,  kde je  nutné  proměnou
      uložit do  paměti  (mezi  jejím  nastavením a použitím).  To by mělo
      ušetřit registry nutné pro uchovávání  proměných, což pomůže zejména
      na  Intelu  a  ostatních  architekturách  s malým  počtem  registrů.
      LCM je zase  základem  pro řadu  dalších  optimalizací - dead  store
      elimination  (pokud se uloží hodnota na ukazatel a potom jiná a mezi
      nejsou  žádné kolidující  přístupy, první uložení se může vynechat),
      shring wrapping, spill code motion a generic load/store motion. Tato
      optimalizace snad vyřeší problem, že většina ostatních  optimalizací
      má tendenci tyto  intervaly  zvětšovat a proto jsou jejich  výsledky
      na Intelu  sporné. Na LCM pracuje na zakázku  firma Cygnus  (podobně
      jako na GCSE apod.) a slibuje její zveřejnění do konce roku.

                              Alias Analysis

          Další  zajímavou  optimalizací  je alias  analysis.  Ta  výchází
      z ANSI C standardu,  který  říká, že není  možné do  stejného  bloku
      paměti uložit dva shorty a později je načíst jako jeden long či jiné
      podobné kombinování typů.

          Pro programy, které odpovídají  tomuto standardu (to by měly být
      všechny) je možné přidat optimalizaci, kde překladač nebude očekávat
      kolizi mezi ukazately s různými typy. Tedy v následujícím kódu:

     int *i;
     short s,d;
     ....
     *i=5;
     printf("%i\n",s);

          překladač  klidně může přehodit  poslední dva řádky,  aniž by se
      obával  toho,  že  ukazatel i ukazuje na  adresu  s. To dává  dalším
      optimalizacím  (jako je loop optimizer,  (G)CSE, či scheduler) větší
      volnost.

          Tuto optimalizaci  vyvinula firma Mark Mitchell  Consulting jako
      součást zakázky pro Los Alamos National  Laboratory. Stejná firma už
      asi před rokem přidala  podobnou  optimalizaci, která si u ukazatelů
      pamatuje,  jestli  ukazují do zásobníku,  haldy či  statických  polí
      a nepředpokládá kolizi mezi těmito bloky paměti.

          Tato  optimalizace  není  standardně  zapnutá  (protože  některé
      programy neodpovídají ANSI C standardům) a je nutné ji ručně zapnout
      pomocí -fstrict-aliasing.

                             Ostatní optimalizace

          Nová verze samozřejmě obsahuje mnoho různých změn do optimizeru.
      Mezi ty největší změny patří:
      - Regmove  optimalizace byla téměř  úplně  přepsána.  (regmove  řeší
        situace, kde instrukce  vyžaduje jiný registr, než dostane - snaží
        se minimalizovat instrukce typu mov registr, registr)
      - Alokace registrů si spočítá přesnější údaje o použití jednotlivých
        proměných a obsahuje některé vylepšení v heruistice na přidělování
        registrů.
      - Exception handling nyní zabírá méně místa a je o něco rychlejší.
      - Register  reloading  produkuje lepší kód (což poměrně hodně pomůže
        na x86).
      - Nový  přepínač -Os pro optimalizaci na velikost  (zatím ale oproti
        -O2 moc nepomůže)
      - Lepší optimalizace pro Inline funkce
      - Architekturově   závislé   optimalizace  pro  i386:  Velké   změny
        v zarovnávání  dat. Pokud  máte nový GAS (2.9.x),  použije se nová
        .p2align   pseudoinstrukce,  která  zarovnává  podle  Intelovského
        manuálu.  Tyto optimalizace na Pentiu  opravdu  znatelně  pomůžou.
        Další  optimalizace   zahrnují   například   conditinal  move  pro
        PentiumPro.

                                 Ostatní změny

          Nejenom  optimalizacemi  živ  je  člověk a proto  Egcs  obsahuje
      i některé další změny. Například přepínač --help apod.

          Mnoho  vývoje  bylo  oděláno na podpoře  C++. GCC  mívalo v době
      2.7.x jednu z nejlepších  podpor C++ vůbec. Ta za dva roky do vydání
      2.8.x  poměrně  zastarala  a proto  je  třeba  udělat  hodně  práce.
      Egcs  1.1  obsahuje  změny  zejména v podpoře  templates,  namespace
      a exception handlingu. Navíc podporuje nové experimentalní ABI.

                                   Stabilita

          Díky otevřenému vývojovému modelu jsou jednotlivé vyvojové verze
      testovány  mnoha  uživately a proto je egcs poměrně  stabilní. Navíc
      GCC obsauje velký počet testů a i u stabilních verzí  některé z nich
      neprojdou.  Autoři se rozhodli,  že egcs  nebude  mít  žádné  chyby,
      které se projeví na několika základních platformách v těchto testech
      a  proto  vypnuli  některé  menší  optimalizace,  které  způsobovaly
      potíže. Navíc egcs testují na kompilaci jádra Linuxu, knihovně glibc
      a všech programech v distribuci RedHat. Proto by tato verze měla být
      opravdu stabilní.

                                   Výsledky

          Osobně používám vývojové verze egcs 1.1 už delší dobu a nesetkal
      jsem se s žádnými  problémy vyjma  kompilace  některých verzí  jádra
      Linuxu, ale v tomto případě se nejedná o chybu egcs.

          Výsledný kód je téměř  vždy  rychlejší a dokonce o něco  kratší,
      než v případě gcc 2.8.x  (delší  oproti gcc 2.7.x).  Zrychlení  jsou
      ale většinou dost malá, jak nicméně  ukazuje i následující  srovnání
      na Alphe:

                  SPEC Benchmark CINT95 Summary

                     Base     1.0.3     1.0.3       1.1       1.1
   Benchmarks    Ref Time  Run Time     Ratio  Run Time     Ratio
   ------------  --------  --------  --------  --------  --------
   099.go            4600       660      6.97       687      6.69
   124.m88ksim       1900       397      4.79       374      5.08
   126.gcc           1700       284      5.98       275      6.18
   129.compress      1800       416      4.33       380      4.74
   130.li            1900       381      4.98       379      5.01
   132.ijpeg         2400       406      5.90       380      6.32
   134.perl          1900       278      6.84       278      6.84
   147.vortex        2700       681      3.97       571      4.73
   SPECint_base95 (Geom. Mean)           5.37                5.64


                  SPEC Benchmark CFP95 Summary

                     Base     1.0.3     1.0.3       1.1       1.1
   Benchmarks    Ref Time  Run Time     Ratio  Run Time     Ratio
   ------------  --------  --------  --------  --------  --------
   101.tomcatv       3700       448      8.26       439      8.42
   102.swim          8600       877      9.80       764     11.3
   103.su2cor        1400       428      3.27       413      3.39
   104.hydro2d       2400       693      3.46       679      3.53
   107.mgrid         2500       438      5.71       430      5.81
   110.applu         2200       472      4.66       473      4.65
   125.turb3d        4100       569      7.20       555      7.38
   141.apsi          2100       341      6.15       341      6.16
   145.fpppp         9600       767     12.5       1075      8.93
   146.wave5         3000       622      4.82       559      5.37
   SPECfp_base95 (Geom. Mean)            6.05                6.06

          Na  Intelu  bych  odhadoval   poněkud   větší   zrychlení   díky
      optimalizacím v alokaci  registrů,  reloadu a zarovnávání dat. Podle
      mých soukromých testů jsou cca 15 až 30%.

          Změny  v  rychlosti   nejsou  příliš  velké,   protože   většina
      změn  v  překladači  je  spíše  začátkem  dalšího  vývoje  a  jejich
      přínos  se  projeví  později.  Často  se  ale  projeví  velké  změny
      u  neoptimalizovaných   programů,   protože   se   překladač   stává
      chytřejším.  Pokud  chcete za každou  cenu  rycheljší  kód, je možné
      použít PGCC,  které ale není tak stabilní a někdy  generuje  výrazně
      horší kód. Většinou ale přináší zrychlení o cca 10% oproti Egcs.

                                  Další vývoj

          Protože už asi  měsíc je verze  1.1 ve  feature  freeze  stadiu,
      je už uděláno  poměrně  hodně  práce na  další  verzi.  David  Miler
      kompletně  přepsal  backend  pro Sparc a proto lze očekávat  výrazně
      lepší kód zejména na Ultrasparcu. Loop  optimizer byl rozšířen  tak,
      aby uměl  unroolovat i některé  smyčky bez  pevného  počtu  průchodu
      apod. Cygnus  dodal nový frontend pro jazyk Chill. Firma Mark Michel
      Computing  vyvinula  další  zajímavou  optimalizaci  pro Los Alamos,
      která  vyndavá  přístupy na ukazatele vně smyček (ve staré  verzi se
      vždy hodnota načetla z ukazatele,  změnila a uložila, nyní překladač
      uchová hodnotu v registru a uloží až za smyčkou).

          Velké  změny  se  plánují  do reload  průchodu.  Ten má za  úkol
      nahrávat   hodnoty  do  registrů   tak,  aby  instrukce   měly  data
      v registrech,  které  podporují.  Tento  průchod  často není  třeba,
      protože se o to normálně  postará  alokátor  registrů.  Někdy se ale
      stane,  že se to  alokátoru  registrů a regmove  průchodu  nepodaří.
      Aktuální  implementace  funguje tak, že jeden z registrů označí jako
      reload  registr a předělá  celou  funkci tak, že registr  nepoužívá.
      Tento registr se potom používá pro reloading.

          Na Intelu  toto často není možné,  protože  registry  mají různé
      speciální  významy. V této situaci je jistá  šance, že reload  změní
      registr v případě,  že je zrovna  použit.  Takové  situace  vznikají
      zejména při  používání  asm  statementů a registrových  konvencí pro
      volání  funkcí (to je důvod,  proč je GCC nepoužívá).  Díky  zmatku,
      které  "odhalení"  této  letité  chyby  způsobilo  na mailing  listu
      kernelu  Linuxu ale už existuje  patch (cca 100Kb),  který  upravuje
      reload  aby si  napřed  zjistil,  které  registry  se kdy  používají
      a pro  reloading  používal  nepoužité  registry.  Výsledkem je lepší
      kód a odstraňuje se riziko  chyby.  Bude  ale  třeba  mnoha  dalších
      zásahů do překladače, aby tento nový algoritmus fungoval  spolehlivě
      (některé další části překladače spoléhají na staré chování reloadu).
      Spolu  s  dalším  malým  patchem  snad  ale  budou  konečně   chodit
      registrové volací konvence.

          Také  se plánuje  lepší  zarovnávání  dat na  Intelu.  Pokud  je
      double  hodnota  zarovnána na adresu dělitelnou osmi, funguje mnohem
      rychleji.  To  je  za  použití  přepínaře  -falign-double   zaručeno
      u struktur a dalších typů. Na zásobník se ale stále ukládají stejně.
      Existuje už ale patch,  který  zaručí  ukládání na adresy  dělitelné
      osmi.  To  je  poměrně  komplikované,  protože  musí  být  zaručeno,
      aby adresa byla  dělitelná už při vstupu do funkce.  Navíc  standard
      pro  volání  funkcí  nepodporuje  toto  zarovnávání a proto je nutné
      udělat  patch tak, aby uměl volat  funkce  "postaru", což není úplně
      jednoduché.

          Plánuje se zamozřejmě mnoho dalších změn, jako už zmiňované Lazy
      Code  Motion.  David  Miler  pracuje na novém  algoritmu pro alokaci
      registrů atd.

          Celkově  myslím, že egcs je velmi  úspešný  projekt a že se máme
      na co těšit.


            výheň