Differences

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

Link to this comparison view

cpl:labs:05 [2016/10/11 13:58]
bogdan.nitulescu [BONUS]
cpl:labs:05 [2017/11/01 06:59] (current)
bogdan.nitulescu [Exerciții de laborator (10p)]
Line 1: Line 1:
 ===== 05. LLVM IR - C++ API ===== ===== 05. LLVM IR - C++ API =====
- 
-**TO BE UPDATED SOON** 
-<note warning>​Exemplele de mai jos folosesc LLVM 3.6 - codul va fi ușor diferit pentru a folosi noile facilități din 3.8</​note>​ 
  
 În [[http://​ocw.cs.pub.ro/​courses/​cpl/​labs/​04|laboratorul trecut]] ne-am folosit de reprezentarea textuală a codului intermediar LLVM pentru a înțelege mai bine forma și structura acestuia. Laboratorul curent își propune să vă familiarizeze cu API-ul C++ cu care lucrează bibliotecile LLVM-ului pentru a reprezenta în memorie instrucțiuni. În [[http://​ocw.cs.pub.ro/​courses/​cpl/​labs/​04|laboratorul trecut]] ne-am folosit de reprezentarea textuală a codului intermediar LLVM pentru a înțelege mai bine forma și structura acestuia. Laboratorul curent își propune să vă familiarizeze cu API-ul C++ cu care lucrează bibliotecile LLVM-ului pentru a reprezenta în memorie instrucțiuni.
Line 26: Line 23:
 <code LLVM test.ll> <code LLVM test.ll>
 ; ModuleID = '​test.ll'​ ; ModuleID = '​test.ll'​
-target datalayout = "​e-m:​e-p:​32:​32-f64:​32:64-f80:32-n8:​16:​32-S128"​ +target datalayout = "​e-m:​e-i64:64-f80:128-n8:16:32:64-S128"​ 
-target triple = "i386-pc-linux-gnu"​+target triple = "x86_64-pc-linux-gnu"​
  
-; Function Attrs: nounwind+; Function Attrs: nounwind ​uwtable
 define i32 @sum(i32 %x, i32 %y) #0 { define i32 @sum(i32 %x, i32 %y) #0 {
-entry: +  %1 = alloca i32, align 4 
-  %add add nsw i32 %x, %y +  %alloca i32, align 4 
-  ​ret i32 %add+  store i32 %x, i32* %1, align 4 
 +  store i32 %y, i32* %2, align 4 
 +  ​%3 = load i32, i32* %1, align 4 
 +  %4 = load i32, i32* %2, align 4 
 +  %5 = add nsw i32 %3, %4 
 +  ret i32 %5
 } }
  
-attributes #0 = { nounwind "​less-precise-fpmad"​="​false"​ "​no-frame-pointer-elim"​="​true"​ "​no-frame-pointer-elim-non-leaf"​ "​no-infs-fp-math"​="​false"​ "​no-nans-fp-math"​="​false"​ "​stack-protector-buffer-size"​="​8"​ "​unsafe-fp-math"​="​false"​ "​use-soft-float"​="​false"​ }+attributes #0 = { nounwind ​uwtable "​disable-tail-calls"​="​false" ​"​less-precise-fpmad"​="​false"​ "​no-frame-pointer-elim"​="​true"​ "​no-frame-pointer-elim-non-leaf"​ "​no-infs-fp-math"​="​false"​ "​no-nans-fp-math"​="​false"​ "​stack-protector-buffer-size"​="​8" "​target-cpu"​="​x86-64"​ "​target-features"​="​+fxsr,​+mmx,​+sse,​+sse2" "​unsafe-fp-math"​="​false"​ "​use-soft-float"​="​false"​ }
  
 !llvm.ident = !{!0} !llvm.ident = !{!0}
  
-!0 = !{!"​clang version 3.6.(tags/RELEASE_362/​final)"​}+!0 = !{!"​clang version 3.8.1-12ubuntu1 ​(tags/RELEASE_381/​final)"​} 
 </​code>​ </​code>​
  
Line 49: Line 52:
 <code bash>llc -march=cpp test.bc -o llvm-ir-gen-test.cpp</​code>​ <code bash>llc -march=cpp test.bc -o llvm-ir-gen-test.cpp</​code>​
 Rezultatul este [[http://​pastebin.com/​mEX2avQk|aici]]. Rezultatul este [[http://​pastebin.com/​mEX2avQk|aici]].
 +**Later Edit 2016: Aceasta metoda nu mai functioneaza incepand cu LLVM 3.8.**
  
 Dacă vreți să compilați codul respectiv, folosiți [[http://​pastebin.com/​gmvmcTFx|Makefile]] și înlocuiți linile: Dacă vreți să compilați codul respectiv, folosiți [[http://​pastebin.com/​gmvmcTFx|Makefile]] și înlocuiți linile:
Line 64: Line 68:
 ==== Introducere în API-ul de C++ ==== ==== Introducere în API-ul de C++ ====
  
-Pentru a obține reprezentarea în memorie a IR-ului din exemplu puteți folosi codul de [[http://​pastebin.com/​H3DG6yM5|aici]] cu [[http://pastebin.com/​btuRGYub|acest]] ''​Makefile''​. După cum se poate observa, pentru a putea folosi API-ul C++ vor fi necesare ​includerea mai multor headere. În plus, ar fi bine să folosiți și namespace-ul llvm:+Pentru a obține reprezentarea în memorie a IR-ului din exemplu puteți folosi codul din arhiva ​de sarcini {{:cpl:labs:​lab5_cpl.zip|}}. După cum se poate observa, pentru a putea folosi API-ul C++ va fi necesară ​includerea mai multor headere. În plus, ar fi bine să folosiți și namespace-ul llvm:
 <code cpp>​using namespace llvm;</​code>​ <code cpp>​using namespace llvm;</​code>​
  
Line 78: Line 82:
 Înainte de a declara o funcție, trebuie definit tipul acesteia (adică antetul funcției): Înainte de a declara o funcție, trebuie definit tipul acesteia (adică antetul funcției):
 <code cpp> <code cpp>
-std::vector<​Type *> FuncTy_0_args;​ +//Create IntType 
-FuncTy_0_args.push_back(IntegerType::​get(mod->​getContext(), 32)); +auto IntType = llvm::​IntegerType::​getInt32Ty(getGlobalContext()); 
-FuncTy_0_args.push_back(IntegerType::get(mod->getContext()32))+ 
-FunctionType *FuncTy_0 ​= FunctionType::​get( +//Create sum's function prototype ​    
-    /*Result=*/IntegerType::​get(mod->​getContext(),​ 32)+std::vector<​Type*sum_args = { IntTypeIntType }
-    /*Params=*/FuncTy_0_args+FunctionType* ​sum_func_type ​= FunctionType::​get( 
-    /*isVarArg=*/false);+/*Return Type*/ IntType
 +/​*Params*/ ​sum_args
 +/​*isVarArg*/​ false);
 </​code>​ </​code>​
  
 În exemplul de mai sus: În exemplul de mai sus:
-  * ''​FuncTy_0''​ este o variabilă ce definește antetul unei funcții cu următoarele proprietăți:​+  * ''​sum_func_type''​ este o variabilă ce definește antetul unei funcții cu următoarele proprietăți:​
     * rezultatul funcției este un întreg pe 32 de biți     * rezultatul funcției este un întreg pe 32 de biți
-    * parametrii funcției sunt specificați prin vectorul ''​FuncTy_0_args''​+    * parametrii funcției sunt specificați prin vectorul ''​sum_args''​
     * nu are număr variabil de argumente     * nu are număr variabil de argumente
-  * ''​FuncTy_0_args''​ este un vector ce definește tipurile argumentelor unei funcții:+  * ''​sum_args''​ este un vector ce definește tipurile argumentelor unei funcții:
     * primul parametru este un întreg pe 32 de biți     * primul parametru este un întreg pe 32 de biți
     * al doilea parametru este un întreg pe 32 de biți      * al doilea parametru este un întreg pe 32 de biți 
Line 98: Line 104:
 Următorul pas este declararea funcției: Următorul pas este declararea funcției:
 <code cpp> <code cpp>
-Function *func_sum = mod->​getFunction("​sum"); +//​Declare ​sum 
-if (!func_sum) { +Function* ​sum_func ​llvm::cast<​llvm::​Function>​(Mod->​getOrInsertFunction("​sum", ​sum_func_type));
-  func_sum = Function::Create( +sum_func->​setCallingConv(CallingConv::​C);​
-    /*Type=*/​FuncTy_0,​ +
-    /​*Linkage=*/​GlobalValue::ExternalLinkage,​ +
-    /*Name=*/"​sum", ​mod); +
-  ​func_sum->​setCallingConv(CallingConv::​C);​ +
-}+
 </​code>​ </​code>​
-În codul de mai sus, se adaugă la modul (dacă nu există deja) funcția ''​sum''​ cu tipul definit anterior ​și cu vizibilitate externă (poate fi accesată din alte module). Pe obiectul de tip ''​Function''​ returnat se pot apela diverse metode, de exemplu cea pentru setarea calling convention-ului.+În codul de mai sus, se adaugă la modul (dacă nu există deja) funcția ''​sum''​ cu tipul definit anterior. Pe obiectul de tip ''​Function''​ returnat se pot apela diverse metode, de exemplu cea pentru setarea calling convention-ului.
  
 Urmează definirea efectivă a funcției. Primul pas este adăugarea argumentelor:​ Urmează definirea efectivă a funcției. Primul pas este adăugarea argumentelor:​
 <code cpp> <code cpp>
-Function::​arg_iterator args = func_sum->​arg_begin();​ +Function::​arg_iterator args = sum_func->​arg_begin();​ 
-Value* int32_x = args++;+Argument* int32_x = &(*args++);
 int32_x->​setName("​x"​);​ int32_x->​setName("​x"​);​
-Value* int32_y = args++;+Argument* int32_y = &(*args++);
 int32_y->​setName("​y"​);​ int32_y->​setName("​y"​);​
 </​code>​ </​code>​
Line 120: Line 121:
 Următorul pas este adăugarea primului BasicBlock din cadrul funcției. În mod uzual acesta va purta numele ''​entry'',​ însă poate avea și un alt nume. Următorul pas este adăugarea primului BasicBlock din cadrul funcției. În mod uzual acesta va purta numele ''​entry'',​ însă poate avea și un alt nume.
 <code cpp> <code cpp>
-BasicBlock* ​label_entry ​= BasicBlock::​Create(mod->​getContext(), "​entry",​func_sum,0);+BasicBlock* ​entry = BasicBlock::​Create(getGlobalContext(), "​entry", ​sum_func);
 </​code>​ </​code>​
  
 În cadrul BasicBlock-ului creat anterior se vor adăuga instrucțiunile,​ așa cum le vedem în reprezentarea fișierului ''​test.ll'':​ În cadrul BasicBlock-ului creat anterior se vor adăuga instrucțiunile,​ așa cum le vedem în reprezentarea fișierului ''​test.ll'':​
-<code cpp> 
-// Block entry (label_entry) 
-BinaryOperator *int32_add = BinaryOperator::​Create( 
-      Instruction::​Add,​ int32_x, int32_y, "​add",​ label_entry);​ 
-ReturnInst::​Create(mod->​getContext(),​ int32_add, label_entry);​ 
-</​code>​ 
  
 <note important>​ <note important>​
-Documentația pentru cea mai recentă versiune de LLVM se găsește [[http://​llvm.org/​doxygen/​classllvm_1_1Instruction.html|pe net]] (căutați "llvm instruction"​ sau orice altă clasă vă interesează). La laborator folosim versiunea 3.6, așa că pot exista schimbări de API față de ce găsiți online, dar puteți măcar să vă orientați in linii mari. +Documentația pentru cea mai recentă versiune de LLVM se găsește [[http://​llvm.org/docs/doxygen/html/​classllvm_1_1IRBuilder.html|pe net]] (căutați "llvm instruction", "llvm IRBuilder" sau orice altă clasă vă interesează). La laborator folosim versiunea 3.8, așa că pot exista schimbări de API față de ce găsiți online, dar puteți măcar să vă orientați in linii mari. 
-Pe mașina virtuală de la laborator găsiți header-ele LLVM-ului în ''​~/​llvm-3.6.2/​src/​include/​llvm''​. Pentru acest laborator sunt importante mai ales header-ele din directorul ''​IR''​.+Pe mașina virtuală de la laborator găsiți header-ele LLVM-ului în ''​~/cpl/​llvm/​src/​include/​llvm''​. Pentru acest laborator sunt importante mai ales header-ele din directorul ''​IR''​.
 </​note>​ </​note>​
  
 +}
 ==== Tipuri importante pentru lucrul cu API-ul de C++ ==== ==== Tipuri importante pentru lucrul cu API-ul de C++ ====
  
Line 156: Line 152:
   * basic block-urile etc.   * basic block-urile etc.
  
-{{ :cpl:labs:classllvm_1_1instruction_inherit_graph_cropped.png?1000 |}} +{{:​cpl:​labs:​classllvm_1_1instruction_inherit_graph.png?800|}} 
-{{ :cpl:labs:classllvm_1_1constant_inherit_graph.png?900 |}}+{{:​cpl:​labs:​classllvm_1_1value_inherit_graph.png?800|}}
  
-Exemple de constante remarcabile sunt 
-  * funcțiile (''​Function''​),​ 
-  * variabilele globale (''​GlobalVariable''​),​ 
-  * literalii de diverse feluri (''​ConstantInt'',​ ''​ConstantDataArray'',​ ''​ConstantStruct''​ etc),  
-  * expresiile constante (''​BinaryConstantExpr'',​ ''​GetElementPtrConstantExpr''​ etc). 
  
 În general, expresiile constante sunt folosite pentru valori care vor fi constante în executabilul final, dar nu pot fi calculate încă (de exemplu pentru că se bazează pe adresa unei variabile globale, care este stabilită mult mai târziu în cadrul procesului de compilare). În general, expresiile constante sunt folosite pentru valori care vor fi constante în executabilul final, dar nu pot fi calculate încă (de exemplu pentru că se bazează pe adresa unei variabile globale, care este stabilită mult mai târziu în cadrul procesului de compilare).
  
-De regulă subclasele lui ''​Value''​ pun la dispoziție una sau mai multe metode statice ''​Create''​ care se pot folosi pentru a obține obiecte de acel tip. Există însă și excepții, pentru care va trebui să folosiți ​direct constructorul. ​În general ​e recomandat să folosiți ​ceva care primește ca parametru fie o instrucțiune ''​InsertBefore''​ fie un basic block ''​InsertAtEnd'',​ pentru a vă asigura că instrucțiunea a fost inserată acolo unde dorițiAveți grijă mai ales la instrucțiunile care derivă din ''​TerminatorInst'',​ care trebuie întotdeauna să se afle la sfârșitul basic block-ului din care fac parte, și la instrucțiunile ''​PHINode'',​ care trebuie întotdeauna să se afle la începutul basic block-ului din care fac parte (dacă basic block-ul conține deja cel puțin o instrucțiune non-phi, puteți insera noduri ''​phi''​ înaintea instrucțiunii întoarse de metoda ''​getFirstNonPHI''​ a basic block-ului).+De regulă subclasele lui ''​Value''​ pun la dispoziție una sau mai multe metode statice ''​Create''​ care se pot folosi pentru a obține obiecte de acel tip. Există însă și excepții, pentru care trebuie folosit ​direct constructorul. ​Pentru un API uniform ​e recomandat să folosiți ​clasa [[http://​llvm.org/​doxygen/​classllvm_1_1IRBuilder.html | IRBuilder]].
  
 Ierarhia de valori ușurează lucrul cu orice combinații de instrucțiuni sau constante. De exemplu, o instrucțiune de adunare poate folosi ca operanzi fie un argument al funcției și o constantă, fie o constantă și rezultatul unei instrucțiuni anterioare, fie rezultatele a două instrucțiuni anterioare, și așa mai departe. Pentru a folosi rezultatul unei instrucțiuni ca operand, pur și simplu folosiți instanța acelei instrucțiuni:​ Ierarhia de valori ușurează lucrul cu orice combinații de instrucțiuni sau constante. De exemplu, o instrucțiune de adunare poate folosi ca operanzi fie un argument al funcției și o constantă, fie o constantă și rezultatul unei instrucțiuni anterioare, fie rezultatele a două instrucțiuni anterioare, și așa mai departe. Pentru a folosi rezultatul unei instrucțiuni ca operand, pur și simplu folosiți instanța acelei instrucțiuni:​
Line 173: Line 164:
 <code cpp> <code cpp>
 // Add 3 numbers // Add 3 numbers
-BinaryOperator ​*int32_x_plus_y = BinaryOperator::​Create+Value *int32_x_plus_y = Builder.CreateAdd
-  ​Instruction::​Add, ​int32_x, int32_y, "add", label_entry); +  int32_x, int32_y, "x_plus_y"); 
-BinaryOperator ​*int32_x_plus_y_plus_z = BinaryOperator::​Create+Value *int32_x_plus_y_plus_z = Builder.CreateAdd
-  ​Instruction::​Add, ​int32_x_plus_y,​ int32_z, "add", label_entry);+  int32_x_plus_y,​ int32_z, "x_plus_y_plus_z");
 </​code>​ </​code>​
 ==== Exerciții de laborator (10p) ==== ==== Exerciții de laborator (10p) ====
 +
 +În rezolvarea laboratorului folosiți arhiva de sarcini {{:​cpl:​labs:​lab5_cpl.zip|}}.
  
 === Exercițiul 1 - first dump (1p) === === Exercițiul 1 - first dump (1p) ===
Line 208: Line 201:
 Puteți să generați fișierul în forma textuală folosind ''​clang'',​ pentru a înțelege exact ce trebuie să generați: Puteți să generați fișierul în forma textuală folosind ''​clang'',​ pentru a înțelege exact ce trebuie să generați:
  
-''​clang -S -emit-llvm main.c ​-o - | opt -S -mem2reg ​-o main.ll''​+''​clang -S -emit-llvm main.c -o main.ll''​
  
 Ca prim pas, generați o funcție main care întoarce 0. Ca prim pas, generați o funcție main care întoarce 0.
Line 214: Line 207:
 === Exercițiul 3 - function call (2p) === === Exercițiul 3 - function call (2p) ===
  
-Adăugați în corpul funcției ''​main''​ un apel către funcția ''​sum'',​ cu argumentele din exemplu. Pentru simplitate, puteți folosi direct constante ​(''​ConstantInt''​), nu e nevoie să alocați spațiu pe stivă.+Adăugați în corpul funcției ''​main''​ un apel către funcția ''​sum'',​ cu argumentele din exemplu. Pentru simplitate, puteți folosi direct constante ​de tip int32, nu e nevoie să alocați spațiu pe stivă.
  
 === Exercițiul 4 - printf (2p) === === Exercițiul 4 - printf (2p) ===
Line 221: Line 214:
 Întrucat este o funcție de bibliotecă,​ nu trebuie definită de către voi. Întrucat este o funcție de bibliotecă,​ nu trebuie definită de către voi.
  
-=== Exercițiul 5 - variabile globale ​(2p) ===+=== Exercițiul 5 - string global ​(2p) ===
  
-Pentru apelarea functiei ''​printf'',​ avem nevoie de o variabilă ​globală care să conțină argumentul de tip format. Pentru aceasta va trebui să generați un tip de date de tip array de ''​i8''​ cu dimensiunea egală cu numărul de caractere din șir + 1, o constantă cu acest tip și cu valoarea ​"sum of %d and %d is %d\x0A" (ultimul caracter este reprezentarea în hexa a sfârșitului de linie), și o variabilă globală inițializată cu constanta precedentă.+Pentru apelarea functiei ''​printf'',​ avem nevoie de un string ​global care să conțină argumentul de tip format ​("sum of %d and %d is %d\n"). **(Hint: llvm::​IRBuilder::​CreateGlobalStringPtr)**
  
 === Exercițiul 6 - apel printf (2p) === === Exercițiul 6 - apel printf (2p) ===
  
-Apelul funcției printf va primi 4 argumente: formatul + cele 3 valori care trebuie printate (2 constante și rezultatului apelului functiei ''​sum''​). Pentru a transmite ca parametru formatul, ​va trebui să îi calculați adresa folosind o constantă ​de tip GEP (''​ConstantExpr::getGetElementPtr''​). +Apelul funcției printf va primi 4 argumente: formatul + cele 3 valori care trebuie printate (2 constante și rezultatului apelului functiei ''​sum''​). Pentru a transmite ca parametru formatul, ​veti folosi referinta intoarsa ​de llvm::IRBuilder::​CreateGlobalStringPtr,​ din exercitiul anterior.
 ===== BONUS ===== ===== BONUS =====
 === contains === === contains ===
cpl/labs/05.1476183493.txt.gz · Last modified: 2016/10/11 13:58 by bogdan.nitulescu
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