This is an old revision of the document!


10. Optimisations with LLVM

Introducere

În LLVM optimizările sunt implementate sub formă de Pass-uri care traversează programul pentru a-l analiza și a-l transforma. Obținerea de informații despre program prin analiza fluxului de date sau de control constituie un pas important în implementarea optimizărilor.

Pentru a aplica o anumită selecție de optimizări se poate folosi tool-ul opt prezentat și 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 sistemul de build din llvm și pașii descriși în exemplul de 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:

opt -hello -debug-only=hello hello.bc

SSA

Un program este reprezentat în forma SSA dacă fiecărei variabile i se atribuie o valoare doar o singură dată şi fiecare folosire a variabilei este dominată de definiţia ei.

Un program poate fi convertit în forma 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.

Exemple

Program inițial Program în forma SSA
V = 4;
  = V + 5;
V = 6;
  = V + 7;
V0 = 4;
   = V0 + 5;
V1 = 6;
   = V1 + 7;
if (...)
    X = 5;
else
    X = 3;
 
Y = X;
if (...)
    X0 = 5;
else
    X1 = 3;
X2 = O(X0, X1);
Y0 = X2;
j = 1;
while (j < X)
    ++j;
N = j;

care se mai poate scrie şi

    j = 1;
    if (j >= X)
        goto E;
S:
    j = j + 1;
    if (j < X)
        goto S;
E:
    N = j;
    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;
 
 
 
 
 

Într-un basic block B având N predecesori P1, P2, …, PN, prin X = O(V1, V2, …, Vn) se înțelege că variabila X va avea valoarea Vj dacă fluxul de control intră în blocul B din blocul Pj, 1⇐j⇐N.

Ierarhia de clase în LLVM

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.

%c = add i32 %a, %b

Aici este un exemplu de cum pot fi parcurşi toţi utilizatorii unei variabile.

Aici este un exemplu de cum pot fi parcurse toate basic block-urile dintr-o funcţie.

Aici este un exemplu de cum pot fi parcurse toate instrucţiunile dintr-un basic block.

Aici este un exemplu de cum pot fi parcurse toate instrucţiunile dintr-o funcţie.

Aici este un exemplu de cum pot fi parcurşi toţi predecesorii şi succesorii unui basic block.

Aici este un exemplu de cast folosit la exerciţiul 4.

Aici este o scurtă descriere a structurii de date ValueMap folosită la exerciţiul 4.

Aici este o scurtă descriere a structurii de date BitVector folosită la exerciţiul 4.

Exerciții de laborator (10p)

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 aici.

Exercițiul 1

Generaţi reprezentarea intermediară în forma SSA pentru sursa C de mai jos.

  • obţineţi reprezentarea intermediară neoptimizată şi observaţi structura codului generat:
clang -S -emit-llvm t.c
  • transformaţi programul în format bytecode utilizând ghidul de comenzi LLVM
  • aplicaţi optimizarea mem2reg pentru a obţine o reprezentare în forma SSA cu noduri phi
  • transformaţi înapoi programul optimizarea din forma bytecode în format lizibil şi observaţi structura codului intermediar
  • test.c
    #include <stdlib.h>
     
    int test(int X, int Y) {
      int Z = 1;
      if (X == Y) Z = Z + 1;
      else Z = Z + 2;
      Z = Z + 3;
      return Z;
    }
     
    int main(int argc, char **argv) {
      test(atoi(argv[1]), atoi(argv[2]));
    }

Exercițiul 2

Folosind codul din fişierul Hello2.cpp din archiva laboratorului, urmăriţi modul în care pot fi implementată simple constant propagation în LLVM.

  • cum se obţine numele unui basic block?
  • 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?
  • realizaţi un fişier de test (urmând paşii de la exerciţiul 1) pentru a ilustra beneficiile acestui pas. Rulaţi acest pas pe exemplul ales.

Exercițiul 3

Implementaţi analiza live variables. Algoritmul este explicat clar şi concis în Dragon book dar şi în slide-urile de aici. Puteţi pleca de la fişierul Hello3.cpp din arhiva laboratorului. Pentru implementare, urmăriţi şi instrucţiunile din cod.

  • ce reprezintă gen(B)/def(B)? Dar kill/use?
  • explicaţi ecuaţia de flux pentru in(B)
  • cum arată laticea? Indexaţi instrucţiunile pentru a putea reprezenta un element din latice ca vector de biţi (1)
  • implementaţi calculul def (2) şi use (3)
  • rulaţi pe fişierul de intrare generat la exerciţiul 1
  • implementaţi algoritmul Killdal (4)
cpl/labs/10.1449526245.txt.gz · Last modified: 2015/12/08 00:10 by irina.presa
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