顯示具有 LLVM 標籤的文章。 顯示所有文章
顯示具有 LLVM 標籤的文章。 顯示所有文章

2013年4月8日 星期一

llvm analysis notes

refs:
http://163.25.101.87/~yen3/wiki/doku.php?id=llvm:llvm_notes



  • llvm commands



  • Convert C/C++ code to llvm IR
    llvm-g++ -emit-llvm -S test.cpp -o test.ll
  • Convert llvm IR to llvm bitcode
    llvm-as test.ll -o test.bc
  • Print Control Flow Graph
    opt -dot-cfg test.bc
  • Print Dominator Tree Graph
    opt -dot-dom test.bc
  • Conver C++ code to C code
    llvm-g++ -emit-llvm test.cpp -o program.bc -c
    llc -march=c program.bc -o program.c

  • analysis(ADT analysis, rule check, redundant check, frame remap)
    • Alias analysis
      • alias memory/register (immutable/mutable) label 
      • check different two frames when the input arg is depended from another? 
    • CFG analysis
      • domain tree, basic block(entry, exist, loop, if/else), operator, reg/mem location
      • loop (vector)
        • for(i=0;i<10 a1="" a2="" b1="" b2="" c1="" c2="" i="" li="" nbsp="">
        • vector(c1,c2) <= {a1,b1} {a2,b2}
    • register analysis
      • virtual register to physical register(arm r0~r10) , rega, regb, regc
    • memory analysis
      • load/store analysis(store is depended on load?)
      • parallel load/store with different address block in the same BB 
    • dependent analysis
      • (depend memory? load/store check)
        • a[0] = 1; # load from constant input
        • b = a[0]; # b is depend from previous assignment, store to output
      • split iterator
        • for(i=0; i<10 ...="" a="" i="" li="" nbsp="" org="">
        • fcheck each unit memory block dependence

2013年4月2日 星期二

LLVM + ZeroMQ = Remote Compiler flow plan

flow plan

  • portable for different platform (client arm, server x86) balance tasks
    • arm for low power mobile device
    • x86 for high performance server device
  • using zeroMQ to our communication handshaking
    • balance the loading
    • multi protocols(TCP/UDP)
    • distribution communication
    • internal broadcast
  • client part
    • method
      • application to llvm byte code or llvm-IR
      • pull llvm byte code or llvm-IR to server by zeroMQ request
      • get server response when the pull-task is done by zeroMQ response
    • require
      • llvm compiler
  • server part
    • method
      • get llvm byte code or llvm-IR from client request
      • do task reassign based on it's server utility, like 2cores CPU(client) to 64 cores CPU(server)
      • put result to client
    • require
      • listen port to link client request
      • byte / IR ADT remap to physical hardware
      • multi requests/ multi tasks balance timeout issue
      • llvm compiler

2011年4月17日 星期日

LLVM + SystemC = PinaVM

在之前的 POST 中,提及 LLVM + external Library 的技術外, 沒想到居然有 LLVM + SystemC 的 project "PinaVM". 主要改善 SystemC 的模擬時間(Device). 透過 SystemC frond end + LLVM back end 的 model . 不走 SystemC 內的 c++ lib, 而用 LLVM 的 JIT compile 針對 SystemC 內部的 sc_thread(), sc_signal(), sc_wait()...做 LLVM 的 Thread map.利用 LLVM 做 analysis 跟 Device 的 Opt. 產生出 opt 過的 exe. project http://gitorious.org/pinavm/pages/Home notes: 1. sc_module 2 Kernel(module) assign 2. sc_thread 2 Function assign 3. thread methods 2 Basic-Block graph assign 4. thread 2 llvm thread assign ps: 可參考 jserv 所寫的 透過 LLVM 打造 Brainfuck JIT compiler

2011年4月14日 星期四

GpuOcelot overview

底下針對 GpuOcelot project 的 analysis part 來做說明.順便當成 notes,以免以後會忘記, 有興趣的人可以參考看看. Ref: PTX: Parallel Thread Execution ISA Version 2.0 http://code.google.com/p/gpuocelot/wiki/References

2011年4月13日 星期三

Critical Fan-In flipflops with PHI Nodes check

感覺可以用 PHINode 來分析 hardware Architecture 中的 Critical Fan-in Nodes. 透過 LLVM 產生的 IR code 中.會 Check alive-In alive-Out 的彼此關係. 除非有 feedback or loop 的 case 下,才會產生 PHINode, 這也表示在 PHINode 的 Fan-In > 1,於是我們可以得到 max path delay(paths) & min path delay(paths). 之後就可以針對這兩條 path 做 setup/hold time check.來 balance timing root tree...避免real time 時的 timing check error.小弟還沒驗證過拉,只是個從 LLVM 移植來的想法罷了.XD
entry:
   ...

for.body: 
 %indvar = phi i32 [ 0, %entry ], [ %indvar.next, %for.body ]
 ...
 %indvar.next = add i32 %indvar, 1
 ...
PS: 不小心 goole 到 control flow graph 的 lib. 有興趣的人可以玩玩看. The Machine-SUIF Control Flow Graph Library

2011年4月6日 星期三

LLVM + Bus Interface.

在之前的 post 中提及 System On Chip Bus 的概念. 可參考底下的 link. 跟 PPT. Refs: Bus communication with thread emulator @c learning plus: ESL Design Flow Image Scalar 4 ESL Flow HW Algorithm NetWork on Chip @c learning plus: AMBA 4.0 AXI Bus Pt2 ... 假設大家對 Bus 有初步的概念後. 底下我們就來 hack LLVM 的 IR 轉成我們所需要的 IR 格式.可參考之前的 post Parallel IR gen. 藉由改變 Instruction 的 link 來表示每個 Instruction 所在的 cycle 位置. 除此之外. 這邊我們可以加入 Bus 的 cofactor 進來. sample code.
#define ADD_RSHT(a,b) (a+b)>>1

void PP(unsigned int *a, unsigned int *b){
 unsigned int r;

   for(r=0; r<1024; r=r+4){          
       b[r]   = ADD_RSHT(a[r]  ,a[r+1]);          
       b[r+1] = ADD_RSHT(a[r+1],a[r+2]);          
       b[r+2] = ADD_RSHT(a[r+2],a[r+3]);          
       b[r+3] = ADD_RSHT(a[r+3],a[r+4]);      
 }
}
假設 pp Module 中 support 4 個 ADD_RSHT, 且 a[], b[] 為外部的 Memory. 結構大致如下所示. 透過 LLVM 轉出 IR byte-code
%arrayidx = getelementptr i32* %a, i32 %tmp
...
%arrayidx6 = getelementptr i32* %a, i32 %tmp
...
%tmp6872 = or i32 %tmp, 3
%arrayidx36 = getelementptr i32* %a, i32 %tmp6872
%arrayidx59 = getelementptr i32* %b, i32 %tmp6872
%tmp69 = add i32 %tmp, 4
%arrayidx52 = getelementptr i32* %a, i32 %tmp69

%tmp3 = load i32* %arrayidx, align 4, !tbaa !0
%tmp7 = load i32* %arrayidx6, align 4, !tbaa !0
...
可以發現 load/store 的 value index 為 arrayidx(x). 這邊我們就把這樣的case 視為moudle PP 透過 bus 來 access external memory.如果把 a[r],a[r+1]... 視為連續的 Memory address.是不是就可以用 bus 的 Burst mode 來簡化. ex:
   a[r]           @ address 0x00000000
 a[r+1]     =>  @ burst 4
 a[r+2]         @ memory read
 a[r+3]         @ decoder/encoder...
有上面的概念後.下就hack LLVM 的 IR 產生我們所需要的 Bus IR.除此之外還可以加入點分析 Model進來,做到 co-analysis 的動作. Results:
External.Mem.Load:                                ; preds = %for.body
 %External_Mem_op15 = shl i32 %indvar, 2
 %External_Mem_arr14 = getelementptr i32* %a, i32 %External_Mem_op15
 %External_Mem_op13 = or i32 %External_Mem_op15, 1
 %External_Mem_arr12 = getelementptr i32* %a, i32 %External_Mem_op13
 %External_Mem_op11 = or i32 %External_Mem_op15, 2
 %External_Mem_arr10 = getelementptr i32* %a, i32 %External_Mem_op11
...
 %External_Mem_ld7 = load i32* %External_Mem_arr14
 %External_Mem_ld6 = load i32* %External_Mem_arr12
 %External_Mem_ld5 = load i32* %External_Mem_arr12
...
 br label %Exe

Exe:                                              ; preds = %External.Mem.Load
 %Exe_op26 = add i32 %External_Mem_ld6, %External_Mem_ld7
 %Exe_op25 = lshr i32 %Exe_op26, 1
 %Exe_op24 = add i32 %External_Mem_ld4, %External_Mem_ld5
 %Exe_op23 = lshr i32 %Exe_op24, 1
 %Exe_op22 = add i32 %External_Mem_ld2, %External_Mem_ld3
 %Exe_op21 = lshr i32 %Exe_op22, 1
 %Exe_op20 = add i32 %External_Mem_ld, %External_Mem_ld1
 %Exe_op = lshr i32 %Exe_op20, 1
 br label %External.Mem.Store

External.Mem.Store:                               ; preds = %Exe
 %External_Mem_arr19 = getelementptr i32* %b, i32 %External_Mem_op15
 %External_Mem_arr18 = getelementptr i32* %b, i32 %External_Mem_op13
 %External_Mem_arr17 = getelementptr i32* %b, i32 %External_Mem_op11
Project download https://sites.google.com/site/funningboy/c/llvm_bus.tar.gz?attredirects=0&d=1 ps: 可以參考 learning plus: C to Verilog notes 中的 reduce map 方式,把相同的 Instruction 做 reduce. ex: Old
 %External_Mem_ld6 = load i32* %External_Mem_arr12
 %External_Mem_ld5 = load i32* %External_Mem_arr12
ex: New
 %External_Mem_ld6 = load i32* %External_Mem_arr12
...

2011年4月5日 星期二

LLVM Training && graph theories

最近在練習寫 LLVM 的 IR code gen 時,遇到了一些阻礙. trace lib code 後.發現如果要建立起一個新的 BasicBlock 不但要 update "Preprocessor", "Successor", 還要 check Dominator tree 這樣才能確保 PHI Nodes 是否正確.流程大概是如此. 1. get terminator inst BranchInst *BInst = dyn_cast<BranchInst>(BB->getTerminator()) 2. get BranchInst type check BInst->isConditional() 3. get successor target BasicBlock BasicBlock *SuccBB = BC->getTerminator()->getSuccessor(0); 4. Create NewBB // BasicBlock *NewBB_1 = BasicBlock::Create(SuccBB->getContext(),"_NewBB_",SuccBB->getParent()); 5. move all PHINodes to NewBB // for(BasicBlock::iterator IT = (*SuccBB).begin(); dyn_cast(IT); IT++){ ... 6.remove Preprocessor pointer SuccBB->removePredecessor(BC,false); 7. create new pointer Value *val = BInst->getCondition(); BranchInst *BInst_1 = BranchInst::Create(SuccBB,NewBB_1); BranchInst *BInst_2 = BranchInst::Create(NewBB_1,ElsBB,,BC); 最後結果 = fail. 因為要 check 的東西實在是太多了. 不如乾脆先把 PHINodes 獨立成一組 BasicBlock, 剩下的 Instructions 為另一組. 就可以免去 dominator tree check 時,每個 PHINode 的 Value 都要被 define 到. ex: error for.body: ; preds = %for.body.for.body_crit_edge, %entry, %_gg_ %tmp14 = phi i32 [ %tmp14.pre, %for.body.for.body_crit_edge ], [ %tmp1, %entry ] 要定義 %tmp14 的 input value 要有 _gg_ BasicBlock 的 value. 不過 LLVM 提供了一個快速的 API func, "splitBasicBlock".利用底下的指令, 就可以把上面的問題解決摟...XD... 害我整整花了一天的時間在 Debug. BasicBlock::iterator IT = (*SuccBB).getFirstNonPHI(); BasicBlock *NewBB_1 = SuccBB->splitBasicBlock(IT, "_gg_"); Graph theories http://www.cs.utah.edu/~mhall/cs6963s11/ http://courses.engr.illinois.edu/ece498/al/Syllabus.html

2011年3月30日 星期三

LLVM oIR code gen

在現今的 CPU 架構中都有 support SIMD(single instruction multi data) 的概念,來做到hardware 加速.底下小弟就舉個最簡單的範例吧. 假設底下 ex(1) 為原始的 hardware code. ex(1)
void SCALAR(unsigned int a, unsigned int b, unsigned int *c){

    unsigned d = (a+b)>>1;
             d = d+2;
            *c = d;
}
ex(2) 為 clang compile 後的 LLVM IR. 在ex(2)中,我們發現 hardware 要先做 "add",在繼續做 "lshr" 的動作.所以要消耗兩個 cycle 的時間.今天假設我們的硬體support "add+lshr" 的指令.是不是就只需要一個 cycle 的時間就可完成.如ex(3)所示.這樣我們就可以減少 hardware Instruction counts.相對的效能就可以提升.有點類似 CUDA/ARM 裡面的特殊指令集拉... ex(2)
define void @SCALAR(i32 %a, i32 %b, i32* nocapture %c) nounwind {
entry:
 %add = add i32 %b, %a
 %shr = lshr i32 %add, 1
 %add3 = add i32 %shr, 2
 store i32 %add3, i32* %c, align 4, !tbaa !0
 ret void
}
ex(3)
define void @SCALAR(i32 %a, i32 %b, i32* nocapture %c) nounwind {
entry:
 %shr = add_lshr i32 %b, %a, 1
 %add3 = add i32 %shr, 2
 store i32 %add3, i32* %c, align 4, !tbaa !0
 ret void
}
sample code @ llvm

#define DEBUG_TYPE "oIRPass"

#include "llvm/Pass.h"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/Support/CFG.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/Dominators.h"

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <map>

using namespace llvm;
using namespace std;

 
  class oIRPass : public FunctionPass {

        public:

            static char ID;

            oIRPass() : FunctionPass(ID) {}

           ~oIRPass(){ 
                       } 

            virtual void getAnalysisUsage(AnalysisUsage &AU) const {
                  AU.setPreservesCFG();
            }

            bool runOnFunction(Function &F);

            void setInstAddLshrSucc(Instruction *Inst);

            void chkInstAddLshrSucc();

            std::string getInstAddLshrToString(Instruction *iPreInst,Instruction *iCutInst);

            std::string getResultToString(Instruction *Inst);

       private:
           std::vector<Instruction*> oIRInstSuccVec;
           std::map<Instruction*,std::string> oIRInstMap;
   };


  // get <Result>
  std::string oIRPass::getResultToString(Instruction *Inst){

  Instruction *IT = dyn_cast<Instruction>(Inst);
  assert( IT!=NULL && "getResultToString Error");
 
  bool pass = false;

  // @ successor Instruction::xx check
  for (Instruction::use_iterator uit = IT->use_begin(); uit!= IT->use_end(); ++uit) {
       Instruction *NIT = dyn_cast<Instruction>(*uit);
       assert( NIT!=NULL && "getResultToString Error");    

       for(unsigned i=0; i<NIT->getNumOperands(); ++i){
           Value *val = NIT->getOperand(i);

         if(dyn_cast<Instruction>(val)){
          Instruction *PIT = dyn_cast<Instruction>(val);

          if( PIT==IT ){
          pass = true;
          return val->getNameStr();
          }
       }

      }
    }

   assert( pass==true && "getResultToString Error");
  }





  // Instruction 2 string
  // ex: %tmp22 = addlshr i32 %i,%j, 1 <<---
  std::string oIRPass::getInstAddLshrToString(Instruction *iPreInst,Instruction *iCutInst){

  Instruction *PreInst = dyn_cast<Instruction>(iPreInst);
  Instruction *CutInst = dyn_cast<Instruction>(iCutInst);

  BinaryOperator *prebin = dyn_cast<BinaryOperator>(PreInst);
  assert( PreInst!=NULL && prebin!=NULL && prebin->getOpcode()== Instruction::Add && "getInstAddLshrToString Error");

  BinaryOperator *cutbin = dyn_cast<BinaryOperator>(CutInst);
  assert( CutInst!=NULL && cutbin!=NULL && cutbin->getOpcode()== Instruction::LShr && "getInstAddLshrToString Error");

  // @ const cehck
  Value *val2 = cutbin->getOperand(1);
  ConstantInt *c2 = dyn_cast<ConstantInt>(val2);
  assert( c2!=NULL && "getInstAddLshrToString Error");
  
  Value *val0 = prebin->getOperand(0);
  Value *val1 = prebin->getOperand(1);

  unsigned int prebinWidth = cast<IntegerType>(prebin->getType())->getBitWidth();
  unsigned int cutbinWidth = cast<IntegerType>(cutbin->getType())->getBitWidth();
 
  // @ width check
  assert( cutbinWidth<=prebinWidth && prebinWidth==32 && "getInstAddLshrToString Error");

  stringstream sb;
  sb << c2->getZExtValue();
  std::string st = "%"+getResultToString(CutInst)+" = addlshr i32 "+"%"+val0->getNameStr()+", "+"%"+val1->getNameStr()+", "+ sb.str();
 
  return st;
 }







  // set Instruction successor match Add->LSHR?
  void oIRPass::setInstAddLshrSucc(Instruction *Inst){

  Instruction *IT = dyn_cast<Instruction>(Inst);
  assert( IT!=NULL && "setInstructionSucc Error");

  oIRInstSuccVec.clear();

  if(dyn_cast<BinaryOperator>(IT)){
     BinaryOperator *bin = dyn_cast<BinaryOperator>(IT);

     // @ Instruction::A 
     if(bin->getOpcode() == Instruction::Add){
     oIRInstSuccVec.push_back(IT);

     // @ successor Instruction::Add check
     for (Instruction::use_iterator uit = IT->use_begin(); uit!= IT->use_end(); ++uit) {
          Instruction *NIT = dyn_cast<Instruction>(*uit);
  
         if(dyn_cast<BinaryOperator>(NIT)){
             BinaryOperator *nbin = dyn_cast<BinaryOperator>(NIT);
         
             if(nbin->getOpcode() == Instruction::LShr)
             oIRInstSuccVec.push_back(NIT);

          }    
       }
     }

   }
  }







 // check Instruction successor match Add->LShr?
 void oIRPass::chkInstAddLshrSucc(){

      typedef std::vector<Instruction*>::iterator IT_iterator;

      // @ Instruction::Add token
      IT_iterator ITB = oIRInstSuccVec.begin();

      Instruction    *PreIT  = dyn_cast<Instruction>(*ITB);
      BinaryOperator *prebin = dyn_cast<BinaryOperator>(PreIT);
      assert( PreIT!=NULL && prebin!=NULL && prebin->getOpcode()== Instruction::Add && "chkInstructionSucc Error");
   
     // @ Instruction::LShr token 
  for(IT_iterator IB = oIRInstSuccVec.begin(), IE = oIRInstSuccVec.end(); IB != IE; ++IB) { 

      Instruction    *CutIT  = dyn_cast<Instruction>(*IB);
      BinaryOperator *cutbin = dyn_cast<BinaryOperator>(CutIT);
      assert( CutIT!=NULL && cutbin!=NULL && "chkInstructionSucc Error");

      Value *val1 = cutbin->getOperand(1);

      if(cutbin->getOpcode()== Instruction::LShr && dyn_cast<ConstantInt>(val1))
      errs() << "oIR found :: " << getInstAddLshrToString(PreIT, CutIT) << "\n";
                            

    } //end for
 }






  bool oIRPass::runOnFunction(Function &F) {
 
        for (Function::iterator BB = F.begin(), BE = F.end(); BB != BE; ++BB) {
                   BasicBlock  *BC = dyn_cast<BasicBlock>(BB);

             for (BasicBlock::iterator IB = BB->begin(), IE = BB->end(); IB != IE; ++IB) {  
                          Instruction *IT = dyn_cast<Instruction>(IB);

                      setInstAddLshrSucc(IT);
                      chkInstAddLshrSucc(); 
          } 
        } 

       return true;
    }

    char oIRPass::ID = 0;
    RegisterPass<oIRPass> PXX("oIR_pass", "oIR lib test",false,false);

Results: oIR found :: %shr = addlshr i32 %b, %a, 1 refs: LLVM + BOOST = LMBOOST.... Sampe Pass @ LLVM LLVM 2.8 env set && pass manager set

2011年3月27日 星期日

CUDA + LLVM = GpuOcelot

小弟最近在跟 GpuOcelot 的 project.底下先就 "build" 跟 "graph" 的一些觀念介紹. Build Part how to install GpuOcelot? Step1. external lib build 可參考 svn 的方式.http://code.google.com/p/gpuocelot/wiki/Installation,把所需要的lib 依序 build 起來. Refs: LLVM 2.8 env set && pass manager set Boost 1.4.X ps: default prefix 路徑在 /usr/local/bin, /usr/local/lib 下, 且記得加入到 $PATH 下.
% export PATH=$PATH:/usr/local/bin:/usr/local/lib
% $PATH
Step2. hack source code. 2-1. boost env command 在 1.4 版之後 -lboost_filesystem-mt 改成 -lboost_filesystem.所以要改 ./scripts/build_environment.py 內 boost set.
//@ original
env.Replace(EXTRA_LIBS=['-lboost_system-mt',
                     '-lboost_filesystem-mt', \
                     '-lboost_thread-mt', '-lGLEW'])

//@ new
env.Replace(EXTRA_LIBS=['-lboost_system',
                     '-lboost_filesystem', \
                     '-lboost_thread', '-lGLEW'])
2-2. string 2 char* ps : 好像在之前的版本沒有這個問題說...
// @ original
//      std::ofstream file(_path + "prof." + _kernelName + "_controlFlow.dot");

// @ new
std::string path =  _path + "prof." + _kernelName + "_controlFlow.dot";
std::ofstream file(path.c_str());
2-3. enum of class 在./gpuocelot/ocelot/ocelot/ir/interface/PTXOperand.h 中定義了 PTXOperand 的 Layer 如ex(1). 所以要存取 Register時, 應該是 ir::PTXOperand::Register, 而不是 ir::PTXOperand::AddressMode::Register,如果硬要把 AddressMode 加入的話.應該要改成 ex(2), 不過改成 ex(2) 後跟 design sink 不起來.所以還是把 AddressMode 拿掉如 ex(3). ex(1)
// @ original
namespace ir {
...
class PTXOperand {
      enum AddressMode {
                         Register,
      }
...
}
ex(2)
// @ new
namespace ir {
...
class PTXOperand {
   class AddressMode {
      enum AddressMode {
                         Register,
      }
   }
...

ex(3)
// @ original                         
if (ptxInstruction->a.addressMode == ir::PTXOperand::AddressMode::Special) {
                        
// @ new
if (ptxInstruction->a.addressMode == ir::PTXOperand::Special) {
2-4 lib include
#include "boost/array.hpp"
...
Step3. run & build test
//@ build a& install
% ./build.py -i

//@ build test all
% ./build.py -t full

cd .release_build/
Graph theories Part 可以先從 "Advanced Compiler Design and Implementation" by Steven Muchnick 這本書看起. Ocelot 裡面有很多 Analysis 的方式是來自這本書的...當然小弟也是在進修中..XD Dominator (graph theory) SSA Graph Tree traversal

2011年3月25日 星期五

C to Verilog notes

底下列出目前在 C to Verilog 所看到的 features. @ Pre-Synthesis part 1. Parallel support Ref : parallel synthesis @ llvm 2. reduce redundant nodes
ex: original
a = 3+4;        
b = b+a;        
ex: new
// cut instruction "a = 3+4" & move the new point to next instruction
b = b+7
3. reduce redundant bit width
ex: original
int32_t a,c;
a =3;
c = 32'b(a)+32'b(1);
ex: new
int8_t a;
int9_t c;
a =3;
c = 8'b(a)+8'b(1);
4. remove Allocation
ex: original

void A(){
...
B (a,b,&c);
}

void B(int a,int b, int *c){
*c = a+b;
}
ex: new
void A(){
...
c = a+b;
}
5. instruction priority
c=a+b; instruction 1
d=c+1; instruction 2
priority 1 > 2
... 
6. PHI node ignore. @ constrain at each Basic Block the data had already done.
ps: @ preprocessor Basic Block the PHI in-alive Nodes had already done.
ex: 
%i.08 = phi i32 [ 0, %entry ], [ %inc, %popCnt.exit ]
%arrayidx = getelementptr i32* %B, i32 %i.08
...

//0    @ Basic block entry
//%inc @ Basic block popCnt.exit & replace it be %i.08 
7. reduce redundant instructions
@ PHI part
%i.06.i = phi i32 [ 0, %for.body ], [ %inc.i, %for.body.i ]
%i.07.i = phi i32 [ 0, %for.body ], [ %inc.i, %for.body.i ]

// cut %i.07.i instruction

@ Operand   
%add.01.i = add i32 %and.i, %sum.07.i
%add.02.i = add i32 %and.i, %sum.07.i

// cut %add.02.i instruction
8. loop with hardware resource constrain "2" Add.
ex : original
for(int i=0; i<10; i++)
   a[i] = b[i]+1;

...
ex : new
for(int i=0; i<10; i=i+2){
    a[i]   = b[i]+1;
    a[i+1] = b[i+1]+1;
}

...
@ Synthesis part 1. Global Value @ In Verilog Module not support the local value
ex : @ Verilog
 
module xx(...);
input ...
output ...
reg ...
wire ...

...

always@(posedge clk)begin
...
end

...
endmodule
2. Schedule list external memory support. split the memory load instruction into two Basic blocks @ Memory request(address/mode)block , Memory (wait the require data)read block. ps: each Basic Block should be done at one cycle clock.
ex: original
for.body:
// @ address/mode phase
%arrayidx = getelementptr i32* %B, i32 %i.08

// @ data phase
%tmp3 = load i32* %arrayidx, align 4, !tbaa !0
...

ex: new

for.body.phase1:
%arrayidx = getelementptr i32* %B, i32 %i.08
br label %for.body.phase2

for,body.phase2:
%tmp3 = load i32* %arrayidx, align 4, !tbaa !0

@ Verilog view
module(...)
output [31:0] mem_address;
output [0 :0] mem_mode;
output [31:0] mem_store_data;
input  [31:0] mem_load_data;

reg [3 :0] cur_state,nxt_state;
reg [31:0] a;
....

case(cur_state)
for_body_phase1 : begin 
                        mem_address = 0x00000000; 
                        mem_mode    = read;
  
                        nxt_state = for_body_phase2; 
                  end

for_body_phase2 : begin 
                        a = mem_load_data;
                        
                        nxt_state = alu_phase;
                  end

alu_phase       : 
...

...
endcase
...

cur_state = nxt_state;
3. pipeline support unroll the loop & pipeline insert. @ example case
ex: original

%cat test.c

static inline unsigned int popCnt(unsigned int input) { 
    unsigned int sum = 0; 
    for (int i = 0; i < 32; i++) {
        sum += (input) & 1; 
        input = input/2; 
    } 
    return sum; 
} 
how to unroll loop ?
// step1. gen IR bytecode
%clang -O3 -emit-llvm test.c -e -o test.bc

// step2. unroll the loop in bytecode
// you can check the opt by "opt -help | grep loop"
%opt -loop-unroll -unroll-count 20 test.bc -debug | llvm-dis

// step3. if step2 pass, then output new bytecode
%opt -loop-unroll -unroll-count 20 test.bc > opt_test.bc 
un-pipeline IR & view

%llvm-dis opt_test.bc > opt_test.ll

%cat opt_test.ll

for.body.i.1: ...Instruction A   br label %for.body.i.2
for.body.i.2: ...Instruction B   br label %for.body.i.3
for.body.i.3: ...Instruction C   br label %for.body.i.4
for.body.i.4: ...Instruction D   br label %for.body.i.5
...
pipeline it
for.body.i.1 : ...Instruction A         br label %for.body.i.2
for.body.i.2 : ...Instruction B,A       br label %for.body.i.3
for.body.i.3 : ...Instruction C,B,A     br label %for.body.i.4
for.body.i.4 : ...Instruction D,C,B,A   br label %for.body.i.5
for.body.i.6 : ...Instruction   D,C,B   br label %for.body.i.7
for.body.i.8 : ...Instruction     D,C   br label %for.body.i.9
for.body.i.10: ...Instruction       D   br label %for.exit.i.0

for.exit.i.0 :
...

2011年3月24日 星期四

LLVM + BOOST = LMBOOST....

底下就把 LLVM + Boost c++ 做簡單的結合. 有 Lib 就是個 "爽"

#define DEBUG_TYPE "BoostPass"

#include "llvm/Pass.h"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/Support/CFG.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/Dominators.h"

#include <boost/graph/graph_traits.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>


#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <map>

using namespace llvm;
using namespace boost;
using namespace std;

 
    template<class T>
    class BoostEdge {
     
       public:
         BoostEdge(T iEdgeFm,
                   T iEdgeTo) : InstructionEdgeFm(iEdgeFm),
                                InstructionEdgeTo(iEdgeTo) {}
        ~BoostEdge(){} 

         T GetInstructionEdgeFm(){ return InstructionEdgeFm; }
         T GetInstructionEdgeTo(){ return InstructionEdgeTo; }

       private:
        T InstructionEdgeFm;
        T InstructionEdgeTo;
    };

    class BoostPass : public FunctionPass {

        public:

            static char ID;

            BoostPass() : FunctionPass(ID) { LLVM2BoostInx=0; }

           ~BoostPass(){ 
                         BoostEdgeList.clear();
                         BoostNodeList.clear();
                         LLVM2BoostMapList.clear();
                         Boost2LLVMMapList.clear(); 
                       } 

            virtual void getAnalysisUsage(AnalysisUsage &AU) const {
                  AU.setPreservesCFG();
            }

            bool runOnFunction(Function &F);

            void setInstructionLLVM2Boost(Instruction *inst);

            int  getIndexLLVM2Boost(Instruction *inst);


       private:
          std::vector<BoostEdge<int>*>  BoostEdgeList;          
          std::vector<Instruction*>     BoostNodeList;

          std::map<Instruction*,int>    LLVM2BoostMapList;
          std::map<int,Instruction*>    Boost2LLVMMapList;

          int LLVM2BoostInx;
    };


  void  BoostPass::setInstructionLLVM2Boost(Instruction *inst){

        if(std::find(BoostNodeList.begin(),BoostNodeList.end(), inst) == BoostNodeList.end()){
           BoostNodeList.push_back(inst);
           LLVM2BoostMapList[inst] = LLVM2BoostInx++; 
        } 
  }

  int  BoostPass::getIndexLLVM2Boost(Instruction *inst){

       std::map<Instruction*,int>::iterator it = LLVM2BoostMapList.find(inst);

       if(it!=LLVM2BoostMapList.end())
       return it->second;

       return -1;
  }

  bool BoostPass::runOnFunction(Function &F) {
 
        for (Function::iterator BB = F.begin(), BE = F.end(); BB != BE; ++BB) {
                   BasicBlock  *BC = dyn_cast<BasicBlock>(BB);

             for (BasicBlock::iterator IB = BB->begin(), IE = BB->end(); IB != IE; ++IB) {  
                          Instruction *IT = dyn_cast<Instruction>(IB);

                  // @ Instruction::Add , Sub ...
                  if(dyn_cast<BinaryOperator>(IT)){

                    for (Instruction::use_iterator uit = IT->use_begin(); uit!= IT->use_end(); ++uit) {

                        if(dyn_cast<BinaryOperator>(*uit)){
                           Instruction *NT = dyn_cast<Instruction>(*uit);

                           //@ same basic block 
                           if( NT->getParent()==BC ){
                               errs() << "LLVM->From Instruction :: " << *IT << "\n";
                               errs() << "LLVM->To   Instruction :: " << *NT << "\n";
                               errs() << "\n";

                               setInstructionLLVM2Boost(IT);
                               setInstructionLLVM2Boost(NT);

                               int it = getIndexLLVM2Boost(IT); 
                               int nt = getIndexLLVM2Boost(NT);
                               
                               assert( it!=-1 && nt!=-1 && "LLVM2Boost Error<1>");                               

                               BoostEdge<int> *BtEdgePtr = new BoostEdge<int>(it,nt);
                               BoostEdgeList.push_back(BtEdgePtr); 
                          }
                        }
                    }
                 }
          
          } //end Instruction iterator

          // @ LLVM 2 Boost Graph
          const unsigned int NodeSize = BoostNodeList.size()+1;  
          const unsigned int EdgeSize = BoostEdgeList.size();

          if( NodeSize>1 && EdgeSize>0 ){

          typedef adjacency_list<vecS, vecS, bidirectionalS> Graph;

          typedef std::pair<int, int> Edge;

          Graph g(NodeSize);

          for(std::vector<BoostEdge<int>*>::iterator IE = BoostEdgeList.begin(); IE != BoostEdgeList.end(); ++IE)
              boost::add_edge((*IE)->GetInstructionEdgeFm(), (*IE)->GetInstructionEdgeTo(), g);

          
          // get the property map for vertex indices
          typedef property_map<Graph, vertex_index_t>::type IndexMap;
          IndexMap index = get(vertex_index, g);

          std::cout << "vertices(g) = ";
          typedef graph_traits<Graph>::vertex_iterator vertex_iter;
          std::pair<vertex_iter, vertex_iter> vp;
          for (vp = vertices(g); vp.first != vp.second; ++vp.first)
            std::cout << index[*vp.first] <<  " ";
            std::cout << std::endl;

          // @ Boost Graph Algorithm
          // ....


          // @ Boost Graph Result 2 LLVM 
          // ...
 
         }

        BoostNodeList.clear();
        BoostEdgeList.clear();

        LLVM2BoostMapList.clear();
        Boost2LLVMMapList.clear();

        LLVM2BoostInx =0;
   
        } // end basic block iterator

       return true;
    }

    char BoostPass::ID = 0;
    RegisterPass<BoostPass> PXX("Boost_pass", "Boost c++ lib test",false,false);
     
compile
opt -load Debug+Asserts/lib/Boost_t.so  -Boost_pass test/scalar.bc
ps: 後來發現可以用 std::pair 來取代 Edge class..XD Table of Contents: the Boost Graph Library

2011年3月22日 星期二

Sample Pass @ LLVM

寫了個簡單的 sample @ LLVM, 就當練習練習...XD. 不過沒有考慮 function 的正確性.
#define DEBUG_TYPE "SamplePass"

#include "llvm/Pass.h"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/Instructions.h"
#include "llvm/Support/CFG.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/Dominators.h"

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <map>

using namespace llvm;

using std::set;
using std::map;
using std::vector;
using std::pair;
using std::string;

    class SamplePass : public FunctionPass {

        public:

            static char ID;

            SamplePass() : FunctionPass(ID) {}

            virtual void getAnalysisUsage(AnalysisUsage &AU) const {

                  AU.setPreservesCFG();
            }

            bool runOnFunction(Function &F);

    };


  bool SamplePass::runOnFunction(Function &F) {
 
        for (Function::iterator BB = F.begin(), BE = F.end(); BB != BE; ++BB) {
                   BasicBlock  *BC = dyn_cast<BasicBlock>(BB);

             std::vector<Instruction*> RemoveList;
                                       RemoveList.clear();
  
             for (BasicBlock::iterator IB = BB->begin(), IE = BB->end(); IB != IE; ++IB) {  
                          Instruction *IT = dyn_cast<Instruction>(IB);

                // @ get Array Load instruction 
                 if (dyn_cast<GetElementPtrInst>(IB)) {

                    for (Instruction::use_iterator uit = IT->use_begin(); uit!= IT->use_end(); ++uit) {
                        if(dyn_cast<LoadInst>(*uit)){
                           
                           Instruction *NT = dyn_cast<Instruction>(*uit);
                         
                           errs() << "getelementptr ::" << *IT << "\n";
                           errs() << "load          ::" << *NT << "\n";
                           errs() << "\n";
                        }
                    }
                 }

              // @ get ALL PHI values && same basic block check        
                if(dyn_cast<PHINode>(IT)){
                   PHINode *phi = dyn_cast<PHINode>(IT);

                   unsigned incs = phi->getNumIncomingValues(); 
                   for(unsigned i =0; i<incs; i++){ 

                      BasicBlock  *INB = phi->getIncomingBlock(i);
                      Value       *val = phi->getIncomingValue(i);

                      if( INB==BC ){
                         errs() << "Instruct  ::" << *IT << "\n";
                         errs() << "PHI value ::" << *val << "\n";
                         errs() << "\n";
                      }  
                   }
                }

              // @ get Binary Operator Instruction::Add
              if(dyn_cast<BinaryOperator>(IT)){
                 BinaryOperator* bin = dyn_cast<BinaryOperator>(IT);

                if (bin->getOpcode() == Instruction::Add) {
                    Value *val0 = IT->getOperand(0);
                    Value *val1 = IT->getOperand(1);

                    errs() << "Add Value0 ::" << *val0 << "\n";
                    errs() << "Add Value1 ::" << *val1 << "\n"; 
                }
              }


            // @ insert Sub Instruction after Add Instruction
            if(dyn_cast<BinaryOperator>(IT)){ 
               BinaryOperator* bin = dyn_cast<BinaryOperator>(IT);

               if (bin->getOpcode() == Instruction::Add){
                   Value *val0 = IT->getOperand(0);
                   Value *val1 = IT->getOperand(1);

                   BinaryOperator::Create(Instruction::Sub, val0, val1, "SUB", IT);
      
               }
            }

            // @ remove ALL Instruction::Add && link the Value0 at next connect
            if(dyn_cast<BinaryOperator>(IT)){
               BinaryOperator* bin = dyn_cast<BinaryOperator>(IT);

                if (bin->getOpcode() == Instruction::Add) {

                       Value *val0 = IT->getOperand(0);

                       IT->replaceAllUsesWith(val0);
                       RemoveList.push_back(IT);
             }
           }

           
          } //end Instruction iterator
  
         // @ remove ALL Instruction::Add 
         for(std::vector<Instruction*>::iterator rmIt  = RemoveList.begin();
                                                 rmIt != RemoveList.end();   ++rmIt){

                   Instruction *It = dyn_cast<Instruction>(*rmIt);
                                It->eraseFromParent();
          }

          RemoveList.clear();


        } // end basic block iterator

        return true;
    }

    char SamplePass::ID = 0;
    RegisterPass<SamplePass> PXX("Sample_pass", "extract Sample DFG",false,false);

compile
opt -load Debug+Asserts/lib/Parallel_t.so  -Sample_pass test/opt_scalar.bc | llvm-dis | grep sub

2011年3月20日 星期日

parallel synthesis @ llvm

我們在machine code 上看到的指令是cycle by cycle 的形式,但實際在硬體上卻可以透過多個 hardware resource 做到 parallel 的方式.如在 Verilog 的語法上用 '(' ')' 來 assign hardware. 底下就用 C 2 Verilog 內的例子來講解 ps: 不管 RHS, LHS 的相關性.這邊只考慮同 alu type 的 instruction. 且至少連續3個 ALU 以上的 instruction. 假設一個sample 的 hardware IP named test. ex : sample c code
void test(int a,int b, int c, int d, int e, int *o){
     *o = a+b+c+d+e;
}
透過 GCC compile 之後的 machine code 可以看出是 cycle by cycle 的型態.
test:
        movl    8(%esp), %eax
        addl    4(%esp), %eax
        addl    12(%esp), %eax
        addl    16(%esp), %eax
        addl    20(%esp), %eax
        movl    24(%esp), %ecx
        movl    %eax, (%ecx)
        ret
在 llvm IR 中也是如此.
define void @test(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32* nocapture %o) nounwind {
entry:
  %add = add i32 %b, %a
  %add3 = add i32 %add, %c
  %add5 = add i32 %add3, %d
  %add7 = add i32 %add5, %e
  store i32 %add7, i32* %o, align 4, !tbaa !0
  ret void
}
轉成 Graph 來看.大致是如此 但是從 hardware 的角度來看,不管 hardware resource constrains 可以長成這樣子 這邊我們只要用到3個clock cycle 跟兩個 ALU IP ADDs 就可以完成.有了以上的觀念後. 其實最後目標就是改變 llvm 的 IR 型態來符合我們的假設. 改變後的 IR, 可發現出 parallel 的方式.
define void @test(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32* nocapture %o) nounwind {
entry:
  %lowlevel = add i32 %b, %a
  %lowlevel1 = add i32 %c, %d
  %lowlevelOdd = add i32 %e, %lowlevel1
  %headNode = add i32 %lowlevel, %lowlevelOdd
  store i32 %headNode, i32* %o, align 4, !tbaa !0
  ret void
}
ps: 其實在 verilog 中,可以用 (a+b)+(c+d)+e 的方式來做出 parallel 的合成.

2011年2月26日 星期六

synthesis flow @ LLVM 2.8 pt1

最近花了點時間研究 LLVM 跟後端的 Hardware Synthesis flow. 感覺有滿多東西可以做的. 如 pipeline DFG, parallel DFG, memory partition, bus interface... 底下就先用"最最最"簡單的例子來講解一下如何透過 LLVM 當前端的 Input ,跟後端的 Hardware Synthesis 是如何結合的. Step1. LLVM environment set 假設 LLVM 的環境都已經 ready. 沒有 ready 可以參考 LLVM 2.8 env set && pass manager set step2. sample hardware code @ c 一個 Image scaling 最常用到的 Hardware sample IP. 就 o_c = (in_a + in_b)>>1;而 (*) pointer 表示從外部的 memory load/store 進來到loacl 的 IP.
//
//#include <iostream>
//
//using namespace std;
//
void test(int *i_a,int *i_b, int *o_c){
 *o_c = (*i_a+*i_b)>>1;
}
//int main(){
//int a=1, b=1;
//int c;
// test(&a,&b,&c);
// cout 
step3. compile && LLVM IR gen 利用 clnag compile 出 LLVM 的 byte code, 之後再轉成有點像 machine code 的ll format.
% clang -O3 -emit-llvm test.c -c -o test.bc
% llvm-dis test.bc
在 test.ll 中,可以發現 load/store 的 instructions. 跟我們之前的假設相同. IR format 就請參考 LLVM Language Reference Manual
define void @test(i32* nocapture %i_a, i32* nocapture %i_b, i32* nocapture %o_c) nounwind {
entry:
  %tmp1 = load i32* %i_a, align 4, !tbaa !0
  %tmp3 = load i32* %i_b, align 4, !tbaa !0
  %add = add nsw i32 %tmp3, %tmp1
  %shr = ashr i32 %add, 1
  store i32 %shr, i32* %o_c, align 4, !tbaa !0
  ret void
}
step4. LLVM IR Get && sample Graph Gen 利用 share lib 的方式依序取的 instruction type 跟 Data.建立起我們後端的sample Graph. ps: Graph 中可以定義每個 instruction 的 Delay 跟 hardware resource constrain ..., 再透過 Schedule Algorithm 來決定每個 Node 所在的 Cycle. 假設無 hardware, Memory constrain , Run Schedule Algorithm 後得到以下的結果.分別表示 Memory load 需要 2 個 clock cycle, Memory Store 需要 3 個 clock cycle, 而 Internal IP 只需要 1 個 clock cycle 就可完成. 假設只有一個 Memory 的情況下,變成要分成兩次 memory Load.導致total 的 cycle time 增加. resource sharing. 減少 Hardware cost. 如果是 for loop 的 case 也可以猜成 pipeline 的型態. ps: 應該過不久就可以把sample 寫好放上來跟大家一起研究研究摟. Loop optimization Ref: CFG definition http://en.wikipedia.org/wiki/Control_flow_graph Analysis and Representation of Loops Trident: From High-Level Language to Hardware Circuitry - LLVM

LLVM 2.8 env set && pass manager set

1. LLVM 2.8 environment build 這邊我比較偷懶用 git clone 的方式
git clone http://llvm.org/git/llvm.git
2. clang: a C language family front end for LLVM
cd llvm/tools
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
3. Build LLVM and Clang:
cd build
../llvm/configure
make
如果還要加入 C++ 的 compiler 請參考 Clang C++ support ps: 如果你懶的話.其實用 apt-get install 就可以找到相對應的套件.
apt-get install llvm-gcc-4.2
ps: 不過這只適用於 LLVM 2.7, 因為 GCC 4.3 之後是 GPL3 授權,所以 LLVM 2.8 把llvm-gcc 拿掉,變成要用外卦的 clang 當 front end 來解析 c/c++ 的語法.或者是自行把 GCC 4.2 include 進去. 假設 env path 跟 clang 都已經set好, 底下就用 LLVM 2.8 + pass manager 建立起一個基於 LLVM env 的 back end project.
export PATH=$PATH:/your_llvm_loc/llvm/obj_root/Debug+Asserts/bin
4. project build 可參考 Writing an LLVM Pass跟Creating an LLVM Project, 裡面有詳細的說明. 請記得在 makefile 中加入 LOADABLE_MODULE =1 來產生 share lib.
# Make the shared library become a loadable module so the tools can
# dlopen/dlsym on the resulting library.
LOADABLE_MODULE = 1
不過 Writing an LLVM Pass 的 example 是乎有些 bug, 可以用下面的 patch update.
--- oldHello.cpp        2011-02-26 13:16:52.872094173 +0800
+++ PassHello.cpp       2011-02-26 11:31:27.639147310 +0800
@@ -1,6 +1,7 @@
#include "llvm/Pass.h"
#include "llvm/Function.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/Statistic.h"

using namespace llvm;

@@ -17,6 +18,6 @@
};

char Hello::ID = 0;
-  static RegisterPass X("hello", "Hello World Pass", false, false);
+  static RegisterPass<Hello> X("hello", "Hello World Pass", false, false);
}
後續就請參考 Writing an LLVM Pass 有詳細的說明. ps: 可以參考 llvm/lib/Transform/Hello/ 有比較完整的說明,不然弄半天還弄不出來還真的很XXX.最後記得在 opt -load 時候要把 *.so 的路徑指到所在的 project 下的 Debug+Asserts/lib.不然直接 load 會造成底下的 Error
Error opening 'LLVMHellocc.so': LLVMHellocc.so: cannot open shared object file: No such file or directory
  -load request ignored.
感覺 Debug+Asserts 被 LLVM 的 key word tied 住.要用底下的 command 才會 work
opt -load Debug+Asserts/lib/LLVMHellocc.so -help | grep hello
Ref http://article.gmane.org/gmane.comp.compilers.llvm.devel/31132/match=hello+so ref: http://dcreager.net/2010/02/17/llvm-lto-karmic/ http://llvm.org/docs/doxygen/html/Hello_8cpp.html