This shows you the differences between two versions of the page.
cpl:labs:05 [2016/10/29 10:50] bogdan.nitulescu [Introducere în API-ul de C++] |
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 71: | 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 130: | Line 127: | ||
<note important> | <note important> | ||
- | 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" 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. | + | 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 ''~/cpl/llvm/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 154: | 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ți. Aveț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 171: | 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 206: | 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 212: | 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 219: | 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 === |