Differences

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

Link to this comparison view

cpl:labs:08 [2015/11/23 15:59]
cristina.ciocan [Basic blocks]
cpl:labs:08 [2017/11/21 06:52] (current)
bogdan.nitulescu [Exercițiul 3]
Line 1: Line 1:
-====== 08. Control Flow Analysis ======+===== 08. LLVM backend ​=====
  
-Înainte de a putea optimiza un program, unui compilator îi sunt necesare componente care sa îl ajute sa "​înțeleagă"​ cum folosește programul respectiv resursele disponibile.+===== Nice to read =====
  
-Astfel, compilatorul trebuie sa fie capabil sa descrie+  * [[http://​llvm.org/​docs/​CodeGenerator.html|The LLVM Target-Independent Code Generator]] 
-  * caracteristicile fluxului de control al programelor +  * [[https://​www.google.com/​url?​sa=t&​rct=j&​q=&​esrc=s&​source=web&​cd=1&​cad=rja&​uact=8&​ved=0CB0QFjAAahUKEwjzt_fS3tbIAhWn_XIKHbiXBkM&​url=http%3A%2F%2Fllvm.org%2Fdevmtg%2F2014-04%2FPDFs%2FTalks%2FBuilding%2520an%2520LLVM%2520backend.pdf&​usg=AFQjCNFzRa244bFKgvM2bPNFE0SHbubtyA&​sig2=CB3kPXTrEvUbHhJ2-jMaBw|Building an LLVM backend]] 
-  * cum sunt manipulate datele +  * [[http://​llvm.org/​devmtg/​2009-10/​Korobeynikov_BackendTutorial.pdf|Building a  
-Astfel, mecanismele mai generale, dar mai puțin eficiente, pot fi înlocuite cu mecanisme mai specializate și mai eficiente.+backend ​in 24 hours]] 
 +  * [[http://​jonathan2251.github.io/​lbd/​|Creating an LLVM Backend for the Cpu0 Architecture]] 
 +  * [[http://​llvm.org/​docs/​TableGen/​|TableGen]]
  
-===== Abordări ale analizei fluxului de control ===== 
  
-Orice analiza a fluxului ​de control al unei rutine începe cu determinarea **blocurilor de bază(basic blocks)**. Acest pas constituie rutina ​și construcția **Control Flow Graph (CFG)**.+===== Introducere ===== 
 +Backend-ul ​de LLVM primește cod scris în limbajul intermediar ​și generează asamblare. 
 +Binarul este llc.
  
-Abordarea descrisă în continuare folosește **dominatorii** pentru ​descoperi buclele și a le memoraîn vederea optimizării ulterioare. Această abordare ​este suficientă optimizoarelor care analizează fluxul de date în mod iterativ.+Pentru ​face trecerea de la codul IR la asamblare, este nevoie de 4 transformări:
  
-O abordare mai sofisticată este **analiza intervalelor (Interval Analysis)**. Aceasta include o serie de metode de a analiza structura unei rutine și de a o descompune în regiuni imbricate denumite intervale. Această structură de regiuni imbricate este descrisă printr-un arbore (denumit arbore de control) care este foarte util în structurarea și îmbunătățirea analizei fluxului de date. Această abordare este mai complexă și nu va fi discutată aici.+LLVM IR -> SelectionDAG -> MachineDAG -> MachineInstr -> MCInst
  
-==== Exemplu ​====+==== SelectionDAG ​==== 
 +Reprezintă un graf în care operațiile sunt reprezentate ca noduri. 
 +Majoritatea nodurilor sunt independente de arhitectură,​ dar pot exista și noduri custom. 
 +Fiecare nod are un opcode, care este definit (pentru nodurile independente de arhitectură) în enum-ul [[http://​llvm.org/​docs/​doxygen/​html/​namespacellvm_1_1ISD.html|ISD::​NodeType]] 
 +Dacă este nevoie de noduri specifice pentru o arhitectură,​ ele se definesc începând cu ISD::​BUILTIN_OP_END. 
 +Se poate observa ca există mai multe opcode-uri decât instrucțiuni llvm.
  
-Înainte de a descrie tehnici formale folosite în analiza fluxului de control și în analiza fluxului de date, să prezentăm un exemplu simplu+Arcele din graf reprezintă dependințe
-Fie rutina C de mai joscare calculează termenul ''​m''​ din șirul lui Fibonacci (''​m ≥ 0''​). Lângă secvența de cod C, am dat șposibilă descriere într-un limbaj intermediar a acestei funcții.+Ele pot fi: 
 +  - de dateo operație folosește un rezultat produs de altă operație;​ 
 +  ​chain, ordonează operații
 +  - glue, operațiile sunt împreună.
  
-^ Cod sursă ^ Pseudocod generat ^ +Valorile din acest graf au tipuri ​(e.g. i32).
-|<code c> +
-unsigned int fib(unsigned int m+
-    unsigned int f0 = 0; +
-    unsigned int f1 = 1; +
-    unsigned int f2, i;+
  
-    if (m <1) +==== MachineDAG ​==== 
-        return m; +Este un graf foarte asemănător cu SelectionDAG,​ în care nodurile sunt înlocuite cu instrucțiuni. ​
-    else { +
-        for(i 2; i <m; i++) { +
-            f2 f0 + f1; +
-            f0 f1; +
-            f1 f2; +
-        } +
-        return f2; +
-    } +
-}</​code>​ | <code asm> +
-1      receive m +
-2      f0 <- 0 +
-3      f1 <- 1 +
-4      if m <1 goto L3 +
-5      i <- 2 +
-6  L1: if i <m goto L2 +
-7      return f2 +
-8  L2: f2 <- f0+f1 +
-9      f0 <- f1 +
-10     f1 <- f2 +
-11     i <- i+1 +
-12     goto L1 +
-13 L3: return m+
  
 +==== MachineInstr ====
 +MachineDAG-ul este transformat într-o listă de instrucțiuni dependente de arhitectură,​ cu câteva excepții (e.g. instrucțiunea COPY).
  
-  +==== MCInst ==== 
-</​code>​ |+Este o reprezentare simplificată,​ care conține un opcode și niște operanzi. ​
  
-Primul pas este de a descoperi structura de control a programului. Această structură poate fi evidentă în codul sursă (''​if-then-else'',​ cu o buclă pe partea cu ''​else''​),​ însă, aceasta nu mai este la fel de evidentă în limbajul intermediar. Mai mult, chiar și în Cexistă situații mult mai complexe (ex. un ''​for''​ ar fi putut fi format din instrucțiuni ''​if'' ​și ''​goto''​)+===== Structura ​de fișiere ===== 
-Pentru a facilita înțelegerea metodei de analiză a fluxul de control, am reprezentat în figura de mai jos codul intermediar într-o formă "​vizuală",​ un ''​flowchart''​.+Pentru a înregistra un backendtrebuie modificate urmatoarele ​fișiere: 
 +  CMakeLists.txt 
 +  ​autoconf/​config.sub 
 +  autoconf/​configure.ac 
 +  configure 
 +  include/​llvm/​ADT/​Triple.h 
 +  include/​llvm/​Object/​ELFObjectFile.h 
 +  include/​llvm/​Support/​ELF.h 
 +  lib/​Support/​Triple.cpp 
 +  lib/​Target/​LLVMBuild.txt
  
-{{ :cpl:​labs:​laborator-08-fibonacci-flowchart.gif |Flowchart pentru fib()}} +HintCăutați în aceste fișiere apariții corespunzătoare unui target existent ​(ex Hexagon).
-===== Basic blocks =====+
  
-Un ** block de bază (basic block)** reprezintă o secvență de instrucțiuni ​care se execută întotdeauna una după alta+Implementarea se face în directorul lib/​Target/​X,​ care trebuie să conțină, printre altele și următoarele fișiere: 
-Formalun **basic block** (bbeste secvență maximală de instrucțiuni consecutive în care se poate intra doar prin intermediul primei ​instrucțiuni și din care se poate ieși doar prin intermediul ultimei instrucțiuni.+^ Nume           ^ Semnificație ​  ^ 
 +| XTargetMachine | Implementarea unei clase care extinde LLVMTargetMachine. Conține ​**XPassConfig** care adaugă pașii de transformare. Cel mai important pas este **createXISelDag** | 
 +| XSubtarget ​    | Extinde ​clasă generată de tablegen, care definiește un subtarget. | 
 +| XISelLowering ​ | Definește modul in care se face lowering la un SelectionDAG legal| 
 +| XISelDAGToDAG ​ | Este responsabil cu trecerea de la SelectionDAG la MachineDAG | 
 +| XRegisterInfo ​ | Conține informații despre regiștrii (regștrii rezervațispeciali, salvați de calling convention). Metoda care trebuie implementată este **eliminateFrameIndex**, care inlocuiește accese abstracte la stivă ​(e.g. FI -3cu adresă concretă (e.g. -20 (SP)) | 
 +| XInstrInfo ​    | Informații și transformări pe instrucțiuni. Cele mai importante metode care trebuiesc implementate sunt **copyPhysReg**,​ **storeRegToStackSlot**,​ **loadRegFromStackSlot**,​ **expandPostRAPseudo** | 
 +| XFrameLowering | Responsabil cu calcularea stivei folosite ​și modificarea ei la intrarea (**emitPrologue**) și ieșirile (**emitEpilogue**) ​din funcție. | 
 +| XAsmPrinter ​   | Emiterea codului ​și datelor. Deși conține Asm în nume, dumparea se face la un stream abstract care poate fi text (pentru asamblare) sau obiect (dacă se generează direct elf)|
  
-** Determinare basic blocks** +===== TableGen ===== 
-  - Prima instrucțiune a unui basic block (denumită **lider**) poate fi+Informațiile dependente de arhitectură sunt definite în fișiere speciale cu extensia ``.td`` într-o manieră declarativă.
-    * punctul de intrare în rutină +
-    * ținta unei instrucțiuni de salt +
-    * instrucțiunea imediat următoare unei instrucțiuni de salt +
-  ​Pentru a determina bb-urile care formează ​rutina întâi se identifică toți liderii și apoi, pentru fiecare lider, includem în  ''​bb''​ toate instrucțiunile de la lider pana la următorul lider sau până la sfârșitul rutinei.+
  
-<​note>​ Un caz special ​de instrucțiune în construcția unui ''​bb''​ sunt **apelurile de funcții**.+Aceste fișiere sunt procesate ​de utilitarul ​**table-gen**, care generează cod C++ pe baza acestor fișiere.
  
-În general, nu este nevoie ca acestea să fie tratate ca instrucțiuni de salt, ceea ce ar rezulta în ''​bb''​-uri mai puține ​și mai lungi(lucru benefic pentru optimizare). Însă, în unele cazuri (cum ar fi, în C, un apel ''​setjump()''​) este important ca apelul sa fie considerat drept limita de ''​bb''​ (de fapt, aici, problema se complică deoarece orice apel de funcție s-ar putea întoarce nu la instrucțiunea imediat următoare, ci la ''​setjump'',​ dacă funcția apelată folosește ''​longjump''​).+Cele 2 concepte importante din limbaj sunt clasele ​și definițiile.
  
-În practică acest caz este tratat prin inserarea de arce de la toate apelurile de funcții către toate apelurile ​de ''​setjump''​Evident abordarea este pesimistădar în lipsa unor informații suplimentare, nu există altă soluție.</​note>​+**Clasele** sunt folosite pentru a grupa caracteristici comune ale resurselor. O clasă poate să extindă una sau mai multe clase. Cu excepția unor clase de baza (Instruction,​ Register, CallingConv,​ CalleeSavedRegs,​ ș.a.)care definesc semantica ​unor resurseclasele ​nu apar in fișierele C++ generate.
  
-Pe flowchart-ul de mai sus se vede că putem identifica ​cu ușurință blocurile de bază: +Pentru a defini un registru R0, extindem clasa **Register**:​ 
-  * nodurile 1-4 formează un basic block (notat B1) +<​code>​ 
-  * nodurile 8-11 formează ​alt basic block (notat B6) +// Clasa CplReg extinde clasa Register. 
-  ​* Toate celelalte noduri formează singure câte un basic block +// Parametrii sunt transmiși printr-o manieră care seamana ​cu template-urile din C++. 
-    * nodul 12 -> B2 +class CplReg<​bits<​5>​ num, string n, list<​string> ​alt = [], 
-    * nodul -B3 +                   ​list<​Register>​ alias = []> : Register<​n>​ { 
-    * nodul 6 -> B4 +  ​let Namespace = "​Cpl"; ​    // Namespace este un câmp din Register în care 
-    * nodul 7 -> B5 +                             // vor fi grupate registrele în codul generat. 
-===== Graful fluxului de control (CFG) =====+  field bits<5> Num;         // Field al clasei curente în care salvăm numărul registrului 
 +  let Aliases = alias; ​      // Listă de alias-uri pentru registru 
 +  let HWEncoding{4-0} = num; // Codificarea registrului 
 +}
  
-{{:​cpl:​labs:​laboratorul08-fibonacci-domination-1.gif? |Graful fluxului de control pentru fib (CFG)}} +// Ri 32-bit integer registers
-{{:​cpl:​labs:​laborator-08-fibonacci-cfg-llvm.png?​|Graful fluxului de control pentru fib (CFG) generate cu LLVM}}+// Această clasă nu se reflectă în fișierul generat. 
 +class Ri<​bits<​5>​ num, string n, list<​string>​ alt = []> : CplReg<​num,​ n, alt> ​{ 
 +  let Num = num; 
 +}
  
-În prima figură se pot observape lângă blocurile de bază definite mai susau fost adăugate două blocuri speciale+// Definim R0R1 și R2 prin intermediul claselor Ri și DwarfRegNum 
-  * entry +def R0 : Ri<0"​r0">, ​ DwarfRegNum<​[0]>;​ 
-  ​* exit +def R1 Ri<1, "​r1">, ​ DwarfRegNum<​[1]>;​ 
-Motivele pentru care se introduc cele două blocuri speciale vor deveni mai evidente la analiza fluxului de date.+def R2 : Ri<2, "​r2">, ​ DwarfRegNum<​[2]>;​ 
 +//... 
 +</​code>​ 
 + 
 +Pentru a defini o instrucțiune,​ extindem clasa **Instruction**:​ 
 +<​code>​ 
 +class InstCpl<​dag outs, dag ins, string asmstr> 
 +  ​: Instruction { 
 +  let Namespace = "​Cpl";​
  
-A doua figură (ce conţine cod intermediar LLVM) a fost obţinută executând următoarele comenzi: +  dag OutOperandList = outs;  // lista operanzilor destinație 
-<code bash> +  dag InOperandList = ins;    // lista operanzilor sursă 
-sudo apt-get install clang +  let AsmString = asmstr; ​    // string-ul corespunzător sintaxei 
-clang -emit-llvm fibo.c -c -o fibo.bc+  let Size = 4;               // dimensiunea în octeți (pentru generare obiect)
  
-sudo apt-get install dot2tex+  bits<​32>​ Inst = 0;          // codificarea binară a instrucțiunii 
 +  bits<​32>​ SoftFail; 
 +}
  
-opt -dot-cfg fibo.bc ; dot -Tpdf cfg.fib.dot > cfg.fib.pdf ​ +// Instrucțiunea va fi identificată prin enum-ul Cpl::ADD în codul generat
-SAU +def ADD : InstCpl< 
-opt-3.0 -f -dot-cfg fibo.bc ​dot -Tpdf cfg.fib.dot > cfg.fib.pdf ​+     (outs IntRegs:​$rc),​ 
 +     (ins IntRegs:​$ra,​ IntRegs:​$rb),​ 
 +     "​add $rc, $ra, $rb" 
 +>;
 </​code>​ </​code>​
  
-În graf, există ​**arc** (muchie orientată) de la un nod la altul dacă există posibilitatea ca, imediat după execuția ultimei instrucțiuni ​din ''​bb''​-ul din primul nod, să urmeze spre execuție prima instrucțiune din ''​bb''​-ul din cel de-al doilea nod. +Generarea de cod se face prin intermediul clasei ​**Pat** care mapează noduri ​din **SelectionDAG** în **MachineDAG**
-Există arce de la nodul //entry// la fiecare nod care reprezintă un **basic block de intrare** (liderul său poate fi prima instrucțiune executată la apelul rutinei). Analog, există arce de la fiecare nod ce reprezinta un **basic block de ieșire** (ultima instrucțiunea ​''​bb''​-ului poate fi ultima instrucțiune executată la ieșirea din funcție) la nodul //exit//.+<​code>​ 
 +// Nu avem nevoie de nume pentru pattern, ​așa că îl definim anonim
  
-<note>În generalexista un singur basic block de intraredarîn unele limbaje ​(ex. FORTRAN), pot exista mai multe puncte de intrare într-o rutina).</note>+// add -> ADD 
 +def : Pat<(add IntRegs:​$ra,​ IntRegs:​$rb),​ (ADD IntRegs:​$ra,​ IntRegs:​$rb)>
 +// ineg -> 
 +//   t = EOR rara    @ t is zero 
 +//   res = SUB tra   @ res is zero - ra 
 +def : Pat<​(inot IntRegs:​$ra), (SUB (EOR IntRegs:​$ra,​ IntRegs:$ra), IntRegs:$ra)>; 
 +</code>
  
-În continuare, definim: +Clasa **Pat** extinde clasa **Pattern** și ne permite să definim pattern-uri într-un mod mai simplu. In versiunea ​de LLVM 3.6, aceasta clasa este definita ca:  
-  ​* **Succ(b)** - setul de succesori ai unui basic block +<​code>​ 
-  ​* **Pred(b)** ​setul de predecesori ai unui basic block+ class Pat<dag pattern, dag result> ​Pattern<​pattern,​ [result]>;​ 
-Tipuri de basic blocks+</​code>​ 
-  * **Branch** - blocul are mai mult de un succesor +**Calling convention**-ul se definește tot prin intermediul fișierelor ​**.td**. Calling convention-ul este un contract prin intermediul căruia se specifică modalitatea ​de transmitere a parametrilor către funcții și întoarcerea rezultatelor de către acestea (folosind registre, stiva sau ambele). ​
-  ​* **Join** - blocul are mai mult de un predecesor+
  
-===== Dominatori ​și postdominatori =====+Se pot defini mai multe calling convention-uri,​ folosing clasa **CallingConvention**. Această clasă primește ca parametru o listă de acținui (**CCAction**) care este folosită pentru a determina modul de transmitere a parametrilor. ​
  
-==== Relația de dominare ==== +Pentru lista tuturor acțiunilorinspectați fișierul ​**include/​llvm/​Target/​TargetCallingConv.td**. Mai jos sunt prezentate câteva din cele mai comune acțiuni, din acest fișier
-Pentru a determina **buclele într-un CFG**definim întâi o relație binară pe nodurile grafului, numită ​**dominare**. +<​code>​ 
-Nodul d **domină** nodul i (**d dom i**) dacă orice drum posibil de la nodul //entry// la nodul //i// include nodul //d//. +/// CCIfType - If the current argument is one of the specified types, apply 
-Nodul d **domină strict** nodul i (**d sdom i**) dacă ''​d dom i''​ și ''​d !i''​. +/// Action A
-{{ :​cpl:​labs:​laborator-08-dominare.jpeg?​ |d dom i}}+class CCIfType<​list<​ValueType>​ vts, CCAction A> : CCPredicateAction<​A>​ { 
 +  list<​ValueType>​ VTs vts; 
 +}
  
-Proprietăți ale relației de dominare+/// CCPromoteToType - If applied, this promotes the specified current value to 
-  ​* **Reflexivitate** - un nod este propriul său dominator +/// the specified type. 
-  * **Antisimetrie** - dacă A domină B și B domina A atunci A B +class CCPromoteToType<​ValueType destTy> ​CCAction { 
-  * **Tranzitivitate** - dacă A domină B și B domină C atunci A domină C+  ​ValueType DestTy ​destTy; 
 +}
  
-==== Relația de dominare imediată ==== +/// CCAssignToReg ​This action matches if there is register in the specified 
-Nodul a este în relație de **dominare imediată (a idom b)** ''​dacă și numai dacă'':​ +/// list that is still available. ​ If so, it assigns the value to the first 
-//pentru ''​a != b'',​ ''​a idom b <=> a dom b''​ și nu există ''​c''​ astfel încât ''​c != a''​ și ''​c != b''​ și ''​a dom c''​ și ''​c dom b''//​. +/// available register and succeeds. 
-Există un singur **dominator imediat** pentru fiecare nod; îl vom nota cu **idom (nod)**. Relația de //dominare imediată// formează un arbore cu nodurile CFG-ului cărui rădăcină este nodul //entry// și ale cărui arcuri reprezintă relații de //dominare imediată//. Drumurile în acest arbore arată toate relațiile de dominare din CFG (vezi figura de mai sus).+class CCAssignToReg<​list<​Register>​ regList> : CCAction { 
 +  list<​Register>​ RegList = regList; 
 +}
  
-==== Relația de post-dominare ==== +/// CCAssignToStack - This action always matches: it assigns the value to a 
-Nodul p **post-domină** nodul i (**p pdom i**) dacă orice drum posibil de la nodul //p// la nodul //exit// include nodul //i//. +/// stack slot of the specified size and alignment on the stack. ​ If size is 
-{{ :​cpl:​labs:​laborator-08-post-dominare.jpeg?​ |p pdom i}}+/// zero then the ABI size is used; if align is zero then the ABI alignment 
 +/// is used - these may depend on the target or subtarget
 +class CCAssignToStack<​int size, int align> : CCAction ​{ 
 +  int Size = size; 
 +  int Align = align; 
 +} 
 +</​code>​
  
-==== Arbori ​de dominare și post-dominare ==== +Este important ​de menționat că determinarea modului ​de transmitere a unui parametru se face parcurgând lista până la prima acțiune de assign care se execută.
-Setul de dominatori pentru funcția fib: +
-^ Nodul i ^ Domin(i) ^ +
-| entry | {entry} | +
-| B1 | {entry,B1} | +
-| B2 | {entry,​B1,​B2} | +
-| B3 | {entry,​B1,​B3} | +
-| B4 | {entry,​B1,​B3,​B4} | +
-| B5 | {entry,​B1,​B3,​B4,​B5} | +
-| B6 | {entry,​B1,​B3,​B4,​B6} | +
-| exit | {entry,​B1,​exit} |+
  
-**Arborii de dominare(graf si LLVM IR)** pentru programul Fibanacci. +Pentru a defini un calling convention care primește toți parametrii pe stivăputem folosi:
-{{:cpl:​labs:​laborator-08-fibonacci-domination-2.gif?​|Arbore de dominare pentru fib()}} +
-{{:​cpl:​labs:​laborator-08-fibodom.png?​|Arbore de dominare pentru fib() LLVM IR}}+
  
-**Arborele de postdominare** pentru programul Fibanacci. +<​code>​ 
-{{:cpl:​labs:​laborator-08-fibopostdom.png?​|Arbore de postdominare pentru fib()}} +def CC_Stack ​CallingConv<​[ 
-===== Bucle naturale și componente tare conexe =====+  // Promote values to i32 
 +  ​CCIfType<​[i1,​ i8, i16], CCPromoteToType<​i32>>,​
  
-==== Arc înapoi ==== +  ​// Use the stack (always works) 
-Un **arc înapoi** în CFG este //un arc a cărui destinatie este întalnită înaintea nodului sursă intr-o parcurgere DFS//. +  ​CCAsignToStack<​0,​ 0> 
-{{ :​cpl:​labs:​laborator-08-arc-inapoi.jpeg?​ |Arc înapoi}}+]>; 
 +</​code>​
  
-==== Buclă naturală ​==== +===== Setul de instrucțiuni ===== 
-**Buclă naturală** determinată de **arcul înapoi ''​v->​u''​** este definită ca fiind subgraful definit prin: +În cadrul acestui laborator se va folosi o arhitectură care definește un subset al instrucțiunilor ​de **ARMv3**.
-  * mulțimea de noduri - formată din nodul u și toate nodurile din care poate fi atins nodul v fără a trece prin u +
-  * mulțimea de arce - toate arcele conectând nodurile din mulțime.+
  
-**Atenție!** Pentru a avea buclă naturală ''​u dom v''​ și, deci, nu există noduri din care v poate fi atins fără a trece prin u și care să nu fie la rândul lor dominate ​de uAstfelnodul u este **header**-ul buclei. +Arhitectura dispune de un set de 16 registre de 32 biți (R0-R15) ​și un registru ​de stare (CPSR)Registrele R13-R15 sunt rezervate pentru stack pointer SP (R13)link register LR (R14) și program counter PC (R15)
-{{ :​cpl:​labs:​laborator-08-bucla-naturala.jpeg? |Buclă naturală}}+
  
-<​note>​O buclă naturală ​este o [[http://​en.wikipedia.org/​wiki/​Strongly_connected_component|componentă tare conexă]] cu o singură intrare.</​note>​+Registrul de stare este scris de către instrucțiunile de comparație și citit de către instrucțiunile de salt condiționat.
  
-==== Introducerea nodului PRE-HEADER ==== +Setul de instrucțiuni cuprinde: ​
-Multe optimizari constau în mutarea codului din interiorul buclei imediat înaintea header-ului buclei. Pentru a garanta că avem un astfel ​de loc disponibil, introducem conceptul de **pre-header**,​ care este **un nou basic block, inițal gol, plasat imediat înaintea header-ului buclei**. Astfel, toate arcele care înainte intrau în header, venind din afara buclei, acum vor intra în pre-header. În plus, există un singur arc de la pre-header la header. Figura de mai jos arată rezultatul introducerii unui pre-header pentru o buclă.+
  
-Înainte de adăugarea nodului de preheader ​După adăugarea nodului de preheader ​+Tip Sintaxă ^ Efect 
-{{:cpl:labs:laborator-08-bucla-fara-preheader.gif?​|Înainte de adăugarea nodului de preheader}}{{:cpl:labs:laborator-08-bucla-cu-preheader.gif?​|După adăugarea nodului ​de preheader}} |+ALU | add rd, rs1, rs2 | rd = rs1 + rs2 | 
 +::: | add rd, rs1, i8 | rd = rs1 + i8 | 
 +| ::: | sub rd, rs1, rs2 | rd = rs1 rs2 | 
 +| ::: | sub rd, rs1, i8 | rd = rs1 i8 | 
 +| ::: | mul rd, rs1, rs2 | rd = rs1 * rs2 | 
 +| ::: | and rd, rs1, rs2 | rd = rs1 AND rs2 | 
 +| ::: | and rd, rs1, i8 | rd = rs1 AND i8 | 
 +| ::: | orr rd, rs1, rs2 | rd = rs1 OR rs2 | 
 +| ::: | orr rd, rs1, i8 | rd = rs1 OR i8 | 
 +| ::: | eor rd, rs1, rs2 | rd = rs1 XOR rs2 | 
 +| ::: | eor rd, rs1, i8 | rd = rs1 XOR i8 | 
 +| ::: | mul rd, rs1, rs2 | rd = rs1 * rs2 | 
 +| Transfer | mov rd, rs | rd = rs | 
 +| ::: | mvn rd, rs | rd = NOT rs | 
 +| Salt | b i24 | pc = address | 
 +| Salt condiționat | beq i24 | if eq then pc = address | 
 +| ::: | bne i24 | if ne then pc = address | 
 +| ::: | bge i24 | if ge then pc = address | 
 +| ::: | bgt i24 | if gt then pc = address | 
 +| ::: | ble i24 | if le then pc = address | 
 +| ::: | blt i24 | if lt then pc = address | 
 +| Apel funcție | bl i24 | lr = next_address;​ pc = address | 
 +| Memorie | ldr rd, [ra, i12] | rd = [ra + i12] | 
 +| ::: | ldrb rd, [ra, i12] | rd = zero_extend [ra + i12] | 
 +| ::: | str rs, [ra, i12] | [ra + i12] = rs | 
 +| ::: | strb rs, [ra, i12] | [ra + i12] = rs AND 0xFF | 
 +===== Compilare și rulare ===== 
 +Următoarele pachete sunt necesare: 
 +<​code>​ 
 +gcc-arm-linux-gnueabi # cross compiler de arm 
 +libc-dev-armel-cross # embedded GNU C library pentru cross-compiling 
 +qemu-user # pt qemu-arm 
 +android-tools-adb # adb 
 +openjdk-7-jre # sau alta varianta ​de java 
 +lib32z1 lib32ncurses5 lib32stdc++6 # lib-uri pe 32 de biti (daca e cazul) 
 +</​code>​
  
-==== Bucle imbricate ==== +De asemenea, ​este necesar [[http://​dl.google.com/​android/​android-sdk_r24.4.1-linux.tgz|SDK-ul ​de Android]]. 
-Nu este greu de observat că dacă două bucle au header-e diferite, ele sunt fie **disjuncte**,​ fie **imbricate**Pe de altă partedacă două bucle au același header (ca în cazul din figura de mai jos), nu este foarte clar dacă una este imbricată în cealaltă (și care este imbricată în care) sau dacă ele formeaza împreună o singură buclă. +Acesta trebuie dezarhivatiar calea către directorul obținut ​în urma dezarhivării trebuie setată în cadrul variabilei de mediu ANDROID_SDK_ROOT. 
-{{ :​cpl:​labs:​laborator-08-bucla-dubla.gif?​ |Bucle cu header comun}}+De asemenea trebuie adăugată în PATH calea către directorul $ANDROID_SDK_ROOT/​tools.
  
-Pornind de la faptul ca, fără mai multe detalii despre codul sursa, nu se poate distinge între aceste două situații, astfel de cazuri vor fi tratate ca fiind o singură buclă (există o modalitate de analiză ​fluxului de control denumită **analiză structurală** care poate face acestă distincție).+==== Device pentru android ==== 
 +Pentru ​instala doar target-ul necesar: 
 +<​code>​ 
 +# Recomandam instalarea celor cu API 22 
 +android list sdk
  
-==== Regiuni improprii ====+android update sdk --no-ui --filter 4,​25,​38,​tool,​platform-tool 
 +# Numerele pot diferi in functie de output-ul comenzii de mai sus. In cazul nostru: 
 +# 4 - SDK Platform Android 5.1.1, API 22, revision 2 
 +# 25 - Google APIs, Android API 22, revision 1 
 +# 38 - Google APIs ARM EABI v7a System Image, Google Inc. API 22, revision 1
  
-O buclă naturală este doar un tip particular de componentă tare conexă. În practică, deși rar, pot apărea și alte structuri de tip buclă cu mai multe puncte de intrare (în special în programele nestructurate). Deși asemenea structuri sunt rare, faptul că ele totuși apar, ne obligă să le luăm în considerare. Cea mai generală structură de tip buclă care poate să apară este o componentă tare conexă a CFG-ului.+android list targets
  
-CFG-ul este **bine-structurat** dacă acesta conține **doar** bucle naturale. +android create avd -n myandroid22 \ 
-Formal, un CFG ''​G=<​N,​E>''​ este **bine-structurat** dacă mulțimea arcelor poate fi partitionată în două mulțimi: +    -"Google Inc.:Google APIs:22" \ 
-  * mulțimea arcelor înainte - acele arce care formează un graf aciclic orientat (DAG) în care fiecare nod poate fi atins din nodul //entry// +    ​--abi google_apis/​armeabi-v7a 
-  * multimea arcelor înapoi - acele arce care sunt conforme cu definiția de mai sus, adică nodul destinație domină nodul sursă +</​code>​
-Anumite șabloane de flux de control pot face un CFG prost-structurat. Astfel de sabloane se numesc regiuni improprii și în general sunt componente tare conexe cu mai multe intări. Un exemplu de regiune improprie este dat și în figura de mai jos (stanga). +
-^ Regiune improprie ^ După "node-splitting"​ ^ +
-| {{:​cpl:​labs:​laborator-08-regiune-improrie.gif?​|Regiune improprie}} | {{:cpl:labs:​laborator-08-regiune-improrie-reparata.gif?​|După "​node-splitting"​}} |+
  
-Regiunile improprii sunt un impediment în special în analiza fluxului ​de date. Există mai multe metode de a trata problema regiunilor improprii dintr-un CFG dintre care menționăm:​ +==== Compilare ==== 
-  * **analiza iterativă** a fluxului de date +Pentru a compila ​un fișer ''​C''​ vom folosi cross-compiler-ul ​de ARM ''​arm-linux-gnueabi-gcc''​. 
-  * **node-splitting** ​pentru ​transformarea ​unui CFG ireductibil într-unul reductibil ​ +<note important>​ 
-(vezi exemplul de mai sus (figura din dreapta) în care tehnica a fost aplicată ​pentru ​nodul B3).+Pentru temă vom genera cod pentru ​ARM v3. 
 +Datorită ​unui bug în gcc, vom rula cu opțiunea ''​-march=armv4''​. 
 +</​note>​ 
 +Opțiunile sunt similare cu cele date pentru ​gcc. 
 +Arhitectura dorită va fi specificată cu ''​-march=armv4''​.
  
-===== Opțional de citit =====+Pentru a obține asamblarea, folosiți ''​-S''​.
  
-* Basic Blocks and Flow Graphsp528, [[http://​en.wikipedia.org/​wiki/​Compilers:​_Principles,​_Techniques,​_and_Tools|Dragon book]], 2nd edition +Pentru a putea rula executabilele generate folosind emulatoareletrebuie ca acestea să fie link-ate static (prin transmiterea opțiunii ''​-static''​ către ''​arm-linux-gnueabi-gcc''​)
-* [[http://​en.wikipedia.org/​wiki/​Control_flow_analysis|Analiza fluxului de control]]+
  
-====== ​Exerciții de laborator (11p) =======+==== Emulare ​==== 
 +Pentru testare, vom folosi emulatorul de Android și qemu. 
 +=== Emulatorul de Android === 
 +<​code>​ 
 +emulator -avd myandroid22 & # porniti emulatorul de android 
 +adb push binar /​data/​binar ​ # copiati binarul pe device 
 +adb shell /​data/​binar ​      # rulati binarul 
 +</​code>​ 
 +Pentru a putea rula aceste comenzi, trebuie adăugată în ''​PATH''​ calea către directorul ''​$ANDROID_SDK_ROOT/​tools''​. 
 +În cazul în care se folosește un sistem de operare pe 32 biți, este nevoie de forțarea folosirii binarelor pe 32 de biți prin exportarea variabilei ​de mediu ANDROID_EMULATOR_FORCE_32BIT=true. 
 +=== Qemu === 
 +<​code>​ 
 +qemu-arm binar              # rulati binarul 
 +</​code>​
  
-===== Exercițiul 1 - hands-on, pe foaie (2p) =====+===== Exerciții ===== 
 +{{:​cpl:​labs:​lab07.zip|Arhiva}} laboratorului. 
 +==== Exercițiul 1 ==== 
 +Scrieți un program ''​C''​ care printează ''"​Hello World!"''​. Compilați programul și obțineți fișierul .s. Asamblați și link-ați fișierul pentru a obține un fișier ELF. Rulați ELF-ul folosind cele 2 emulatoare ​(Android și qemu)
 +==== Exercițiul 2 ==== 
 +Completați funcția ''​sum''​ din fișierul **ex2.s** pentru a întoarce suma elementelor vectorului dat ca parametru. Adresa vectorului este transmisă prin registrul ''​R0'',​ iar numărul elementelor este transmis prin registrul ''​R1''​. Rezultatul trebuie intors prin registrul ''​R0''​. Link-ați **ex2.s** împreună cu **ex2.c** și rulați executabilul.<​note important>​ 
 +Funcția nu trebuie sa suprascrie niciun registru rezervat pentru program counter, return address și stack pointer. 
 +</​note>​ 
 +==== Exercițiul 3 ==== 
 +Copiați directorul cpl din arhiva în directorul lib/Target al dristribuției de LLVM de mașinile din laborator. 
 +Înregistrați target-ul Cpl în cadrul framework-ului de LLVM. 
 +După ce ați modificat toate fișierele, compilați **llc** cu suport pentru noul target:
  
-Pentru următorul CFG: +   cd ~/​packages/​llvm-3.8.0/​build 
-{{ :​cpl:​labs:​laborator-08-bucle.jpeg? |CFG}} +   cmake -DLLVM_TARGETS_TO_BUILD=Cpl path_to_llvm_src 
-  * găsiți arcele inapoi +   ​make ​-j2 llc 
-  * găsiți nodurile dominate ​de basic blockurile 2 și 4+   ​ 
-  * desenați arborele de dominare +Codul sursa LLVM poate fi descărcat de pe [[http://​llvm.org/​releases/​3.6.0/​llvm-3.6.0.src.tar.xz|llvm.org]] 
-  * determinați nodurile de tip branch ​și cele de tip join +   ​ 
-  * găsiți buclele naturale și nenaturale +Pentru a testa, compilați fișierul ex3.a.ll folosind opțiunea ''​-filetype=null''​ pentru llc. 
-  * găsiți buclele imbricate +<note warning>​ 
-===== Exercițiul ​2 - no loops (1p) ===== +Compilarea llc va dura în jur de 20 minute. 
-Este posibil ca o funcție care conține unul din cuvintele cheie ''​for/​while/​do while''​ să nu conțină bucle? Dacă da, în ce condiții ?+</​note>​ 
 +==== Exercițiul ​==== 
 +Completați în target-ul de Cpl pentru a genera cod pentru următoarele ​funcții echivalente în ''​C''​. 
 +Funcțiile se află în fișierele ​''​ex4_<​no>​.ll''​
 +=== f === 
 +Va trebui ​să implementați funcția ''​CplInstPrinter::​printOperand''​. 
 +<​code>​ 
 +void f() {} 
 +</​code>​
  
-Scrieți o funcție în C și desenați CFG-ul corespunzător folosind toolchain-ul LLVM. +=== ident === 
-===== Exercițiul 3 - no for/​while/​do while (1p) ===== +Va trebui să completați fisierul ​''​CplCallingConv.td''​ 
-Dați exemplu de o funcție care nu conține cuvintele cheie ''​for/​while/​do while''​, dar conține bucle în CFG.+<​code>​ 
 +int ident(int a) { 
 +  return a; 
 +
 +</​code>​
  
-Scrieți o funcție în C și desenați CFG-ul corespunzător folosind toolchain-ul LLVM. +=== or === 
-===== Exercițiul 4 - goto (1p) ===== +Va trebui să adăugați instructiunea ​''​or'' ​în fișierul ​''​CplInstrInfo.td''​ și un pattern pentru nodul ''​or''​ în fișierul ''​CplPatterns.td''​ 
-Dați exemplu de o funcție care nu conține ​''​for/​while/​do while''​, conține maxim o instrucțiune ​''​goto''​ și are o buclă nenaturală.+<​code>​ 
 +int or(int a, int b) { 
 +  return a | b; 
 +
 +</​code>​
  
-Scrieți o funcție în C și desenați CFG-ul corespunzător folosind toolchain-ul LLVM. +=== second ​=== 
-===== Exercițiul 5 - transform (3p) ===== +Va trebui să implementați funcția ''​CplInstrInfo::copyPhysReg''​
-Transformați următoarea regiune improprie într-o buclă naturală plecând de la următorul cod : +<​code>​ 
-{{ :cpl:labs:laborator-08-exsplit.png? |Exemplu de split]}} +int second(int a, int b) { 
-<​code ​cpp+  ​return b
-int (int x, int y) { +
-    x = x + 1; +</​code>​
-    switch(y) { +
-    case 2: +
-        do +
-        { +
-        L2: +
-            x = x - 2; +
-            if (x > 0) goto L4; +
-    case 3: +
-            x = x * 3; +
-            if (x <= 6) { +
-                x -= 5+
-            +
-            else { +
-    default: +
-        L4: +
-                x <<= 4; +
-                if (x != 16) goto L2; +
-                x = y + 6; +
-            } +
-            y = x + 7; +
-        } while(y < x); +
-    } +
-    return x; +
-}</​code> ​ +
- +
-===== Exercițiul 6 - LLVM pass (3p) ===== +
- +
-Scrieți un LLVM pass pentru a afișa: +
-  * toate basic block-urile unui program +
-  * pentru fiecare basic-block:​ +
-    * basic block-urile predecesoare +
-    * basic block-urile următoare+
  
  
-Detalii despre [[http://​ocw.cs.pub.ro/​courses/​cpl/​labs/​llvm-pass|LLVM passes]]. 
cpl/labs/08.1448287171.txt.gz · Last modified: 2015/11/23 15:59 by cristina.ciocan
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