Differences

This shows you the differences between two versions of the page.

Link to this comparison view

cpl:labs:11 [2015/12/26 20:09]
laura.vasilescu
cpl:labs:11 [2016/12/20 01:59] (current)
bogdan.nitulescu [Exercițiul 3]
Line 1: Line 1:
-======= 11. Garbage Collection =======+===== 11. LLVM - transformări de cod =====
  
-Termenul de **Garbage Collection** (gc) se referă la algoritmii de eliberare implicită a memoriei dinamice sau, altfel 
-spus, de colectare a zonelor de memorie devenite inaccesibile. 
  
-Zonele care pot să fie eliberate (garbage) sunt zone de memorie la care nu se mai poate ajunge prin intermediul unui pointer sau eventual a unei succesiuni de pointeri accesibili. Despre aceste zone se spune că sunt inaccesibile spre deosebire de zonele care sunt accesibile şi despre care se spune că sunt în viaţă.+===== Introducere =====
  
-Iniţial aceste tehnici au apărut în legătură cu limbajele ​de tip Lisp pentru ​care alocarea memoriei se face implicit. În prezent +În LLVM optimizările sunt implementate sub formă de [[http://​llvm.org/​docs/​Passes.html|Pass]]-uri ​care traversează programul pentru a-l analiza ​și a-l transformaObținerea de informații despre program prin analiza fluxului de [[cpl:​labs:​09#​tipuri_de_probleme|date]] sau de [[cpl:​labs:​08|control]] constituie un pas important în implementarea optimizărilor.
-se încearcă utilizarea acestor tehnici ​și pentru limbajele care utilizează alocarea explicită ​memoriei dinamice (C, C++)Limbaje mai noi, precum Java, au fost proiectate pentru a putea să utilizeze această tehnică. +
  
-===== Principii de funcționare ​=====+Pentru a aplica o anumită selecție de optimizări se poate folosi tool-ul [[http://​llvm.org/​docs/​CommandGuide/​opt.html|opt]] prezentat și [[cpl:​labs:​llvm-pass|aici]]. Pentru a integra o optimizare nouă în sursele LLVM (fără să mai fim nevoiți să specificăm biblioteca dinamică la runtime) se va folosi [[http://​llvm.org/​docs/​CommandGuide/​llvm-build.html|sistemul de build]] din llvm și pașii descriși în exemplul de [[cpl:​labs:​llvm-pass#​integrarea_unui_pas_in_llvm|aici]]. 
 +===== Afișarea informațiilor de debug ===== 
 +Informațiile de debug, pot fi afișate rulând utilitarul ''​opt''​ cu parametrul ''​-debug''​. Pentru a filtra informațiile de debug doar pentru un anumit pas, se folosește parametrul ''​-debug-only=//<​nume_pass>//''​. De exemplu, pentru a filtra doar log-urile optimizării hello, se rulează comanda: 
 +<code bash> 
 +opt -hello -debug-only=hello hello.bc 
 +</​code>​
  
-**GC** se execută, de regulă, când nu mai este memorie liberă disponibilă. Trebuie să rezolve două probleme: să identifice zonele nefolosite într-un mod conservativ și să elibereze zonele identificate. ​ 
  
-Identificarea zonelor de memorie în viață se face pornind de la variabilele accesibile (mulțime rădăcină) atunci când se execută colectarea memoriei. Mulțimea rădăcină este formată din variabilele globale, variabilele locale din stiva curentă și registre. Pornind de la această mulțime și parcurgând obiectele accesibile prin intermediul unor pointeri se pot identifica obiectele accesibile. Tot ce nu este accesibil în acest fel reprezintă zona inaccesibilă (garbage).+===== SSA ======
  
-Pentru a identifica aceste zone, trebuie să existe ​strategie pentru a răspunde la două întrebări:​ +Un program este reprezentat în forma SSA dacă fiecărei variabile i se atribuie ​valoare doar o singură dată şfiecare folosire a variabilei este dominată de definiţia ei.
-  * dându-se un obiect, acesta conține pointeri? +
-  * dându-se un pointer, unde este începutul șsfârșitul obiectului spre care indică pointerul?+
  
-===== Algoritmi ​de Garbage Collection =====+Un program poate fi convertit în forma [[http://​en.wikipedia.org/​wiki/​Static_single_assignment_form|SSA]] prin: 
 +  * asocierea ​de nume unice variabilelor de fiecare dată când li se atribuie o valoare 
 +  * înlocuirea numelor de variabile cu numele unice atunci când variabilele sunt folosite.
  
-Există mai multe tipuri de astfel de algoritmi: +Exemple 
-  * secvențiali +^ Program inițial ^ Program în forma SSA ^ 
-    * un singur thread ​ +| <code c> 
-  ​* paraleli +V = 4; 
-    * mai multe thread-uriposibilitatea de a rula pe mai multe core-uri simultan +  ​= V + 5; 
-  ​* incrementali +V = 6
-    * în paralel cu execuția programului +  ​= V + 7; 
-    * trebuie să limiteze timpul petrecut într-un pas de GC +</​code>​ | <code c> 
-  * cu compactare/copiere +V0 = 4; 
-    * reduc fragmentarea memoriei +   = V0 + 5; 
-    ​* cresc gradul de localitate a datelor +V1 = 6; 
-    ​* alocare rapidă (incrementare de pointeri)+   = V1 + 7; 
 +</code> | 
 +| <code c> 
 +if (...) 
 +    ​X = 5; 
 +else 
 +    ​X = 3;
  
-==== Algoritmi secvențiali ​====+X; 
 +</​code>​ | <code c> 
 +if (...) 
 +    X0 5; 
 +else 
 +    X1 3; 
 +X2 O(X0, X1); 
 +Y0 X2; 
 +</​code>​ | 
 +| <code c> 
 +1; 
 +while (j < X) 
 +    ++j; 
 +j; 
 +</​code>​ care se mai poate scrie şi <code c> 
 +    j 1; 
 +    if (j >= X) 
 +        goto E; 
 +S: 
 +    j = j + 1; 
 +    if (j < X) 
 +        goto S; 
 +E: 
 +    N = j; 
 +</​code>​ | <code c> 
 +    j0 = 1; 
 +    if (j0 >= X0) 
 +        goto E; 
 +S: 
 +    j1 = O(j0, j2); 
 +    j2 = j1 + 1; 
 +    if (j2 < X0) 
 +        goto S; 
 +E: 
 +    j3 = O(j0, j2); 
 +    N0 = j3;
  
-=== Reference Counting ===+ 
  
-Numără referințele unui obiect. Când un obiect ajunge la zero referințe, acesta este eliberat. 
  
-Este un algoritm simplu, însă dezavantajul este că nu poate elibera structuri ciclice.+  
 +</​code>​ |
  
-=== Mark and Sweep ===+Într-un basic block B având N predecesori P<​sub>​1</​sub>,​ P<​sub>​2</​sub>,​ ..., P<​sub>​N</​sub>,​ prin X O(V<​sub>​1</​sub>,​ V<​sub>​2</​sub>,​ ..., V<​sub>​n</​sub>​) se înțelege că variabila ''​X''​ va avea valoarea V<​sub>​j</​sub>​ dacă fluxul de control intră în blocul B din blocul P<​sub>​j</​sub>,​ 1<=j<=N. 
 + 
 +===== Ierarhia de clase în LLVM ===== 
 +  * clasa [[http://​llvm.org/​docs/​ProgrammersManual.html#​Instruction|Instruction]] este subclasă a 
 +    * clasei [[http://​llvm.org/​docs/​ProgrammersManual.html#​User|User]] care este subclasă a 
 +    * clasei [[http://​llvm.org/​docs/​ProgrammersManual.html#​Value|Value]] 
 +Mai jos este un exemplu de instrucțiune (Instruction). Ea este şi utilizator (User) ale variabilelor (Value) a şi b. În acelaşi timp reprezintă şi definirea variabilei (Value) c. 
 +<code asm> 
 +%c = add i32 %a, %b 
 +</​code>​ 
 + 
 +[[http://​llvm.org/​docs/​ProgrammersManual.html#​iterate-chains|Aici]] este un exemplu de cum pot fi parcurşi toţi utilizatorii unei variabile. 
 + 
 +[[http://​llvm.org/​docs/​ProgrammersManual.html#​iterate-function|Aici]] este un exemplu de cum pot fi parcurse toate basic block-urile dintr-o funcţie. 
 + 
 +[[http://​llvm.org/​docs/​ProgrammersManual.html#​iterate-basicblock|Aici]] este un exemplu de cum pot fi parcurse toate instrucţiunile dintr-un basic block. 
 + 
 +[[http://​llvm.org/​docs/​ProgrammersManual.html#​iterate-institer|Aici]] este un exemplu de cum pot fi parcurse toate instrucţiunile dintr-o funcţie. 
 + 
 +[[http://​llvm.org/​docs/​ProgrammersManual.html#​iterate-preds|Aici]] este un exemplu de cum pot fi parcurşi toţi predecesorii şi succesorii unui basic block. 
 + 
 +[[http://​llvm.org/​docs/​ProgrammersManual.html#​isa|Aici]] este un exemplu de cast folosit la exerciţiul 3. 
 + 
 +[[http://​llvm.org/​docs/​ProgrammersManual.html#​dss-valuemap|Aici]] este o scurtă descriere a structurii de date ValueMap folosită la exerciţiul 3. 
 + 
 +[[http://​llvm.org/​docs/​ProgrammersManual.html#​dss-bitvector|Aici]] este o scurtă descriere a structurii de date BitVector folosită la exerciţiul 3. 
 + 
 +====== Exerciții de laborator (15p) ====== 
 + 
 +<​note>​ 
 +Laboratorul este compus dintr-o exerciții practice care includ analiza formei SSA și implementarea unor optimizări. 
 +Înainte de începerea exercițiilor downloadați arhiva de {{:​cpl:​labs:​lab11_2016.zip|aici}}. 
 + 
 +Compilati exercitiile cu -O0 pentru a nu lasa compilatorul sa aplice optimizari. 
 +</​note>​ 
 + 
 +===== Exercițiul 1 ===== 
 + 
 +Scrieti un Pass de LLVM care sa elimine basic block-urile trivially dead. Hint: Un basic block este trivially dead daca nu exista niciun jump care sa duca catre acesta; cu alte cuvinte nu are predecesori. 
 + 
 +<file c dead.c>​ 
 +#​include<​stdio.h>​ 
 +#​include<​time.h>​ 
 +#​include<​stdlib.h>​ 
 + 
 +void main(void) { 
 +        int a = 1, b =2, c = 3; 
 + 
 +        if (a + b * (rand() % 3) == 3) { 
 +                goto end1; 
 +        } else { 
 +                goto end2; 
 +        } 
 + 
 +dead: 
 +        printf("​Unreachable\n"​);​ 
 + 
 + 
 +end1: 
 +        printf("​Still reachable\n"​);​ 
 +end2: 
 +        printf("​Reachable\n"​);​ 
 +
 + 
 +</​file>​ 
 + 
 +===== Exercițiul 2 ===== 
 + 
 +Scrieti un pass care optimizeaza cazuri de tipul jump to jump. Un caz de jump to jump avem in momentul in care singura intructiune dintr-un basic block este un salt neconditionat intr-un alt bloc. In acest caz toate instructiunile de salt din blocul initial pot fi optimizate pentru a duce direct la blocul destinatie. 
 +Blocul initial este in acest moment dead code si poate fi eliminat cu pass-ul de la exercitiul anterior. 
 + 
 +<file c jump_to_jump.c>​ 
 +#​include<​stdio.h>​ 
 +#​include<​time.h>​ 
 +#​include<​stdlib.h>​ 
 + 
 +void main(void) { 
 +        int x = rand() % 100 - 10; 
 + 
 +        if ( x < 0 ) { 
 +                return; 
 +        } else { 
 +                printf("​Not negative\n"​);​ 
 +        } 
 + 
 +        printf("​End\n"​);​ 
 +
 + 
 +</​file>​ 
 + 
 +===== Exercițiul 3 ===== 
 + 
 +Folosind codul din fişierul ''​Hello.cpp''​ din arhiva laboratorului,​ urmăriţi modul în care poate fi implementată simple constant propagation în LLVM. 
 +  * cum poate fi identificată o instrucţiune inutilă? 
 +  * identificaţi metoda responsabilă pentru constant folding 
 +  * cum se înlocuiesc apariţiile viitoare ale variabilei în cauză cu o constantă?​ 
 +  * de ce se adaugă din nou în worklist unele instrucţiuni?​ 
 +  * Rulaţi acest pas folosind fisierul ''​test.c''​ din arhiva laboratorului.
  
-Parcurge întreaga zonă de memorie dinamică (heap) identificând zonele care nu mai sunt în viață, după care le eliberează. Acest algoritm nu realizează compactarea spațiului disponibil, existând un alt algoritm, Compacting Mark and Sweep, care adaugă un pas separat pentru acest lucru. Compactarea poate fi rapidă existând un vector în care sunt stocați pointerii către obiecte, referințele fiind indecșii în vector. 
  
-=== Copying Collection === 
  
-Algoritmul copiază zonele rămase în viață într-un spațiu nou. Acesta realizează implicit și compactarea spațiului disponibil. 
cpl/labs/11.1451153376.txt.gz · Last modified: 2015/12/26 20:09 by laura.vasilescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0