您的位置:首页 > 其它

基于LLVM 中间表示(IR)分析实例

2017-07-28 23:11 429 查看
LLVM是很优秀的编译器,其支持多源语言,多后台,而且同程序整体和整个生命周期的分析、转换和优化。
LLVM 中间表示(Intermediaterepresentation,简称IR)作为LLVM的中间语言,对LLVM各种特性的支持有非常重要的作用。
有很多时候我们需要对LLVMIR进行分析,LLVM官方有文档:http://llvm.org/docs/WritingAnLLVMPass.html,其介绍了一个基本的步骤,和对其中一些常用的Pass的介绍,其中的实例很简单,只是简单地打印出函数的名字。
因此为了加深对Pass的学习,下面将分析两个实例:
1、写一个Pass打印出Module、Function、BasicBlock、变量的值或者一些一些属性。
1)所有的步骤都参见官方文档,在/llvm/lib/Transforms目录下面新建一个文件夹(以BasicBlockName为例),到该目录下面新建两个文件一个就是需要用到的BasicBlockName.cpp文件,另外一个就是Makefile。还需要将新建的文件夹的名字加到/llvm/lib/Transforms目录下面的Makefile里面PARALLEL_DIRS的后面添加该文件名。这样即可编辑cpp文件。
2)在cpp文件中添加如下代码:

  1#include "llvm/Pass.h"
  2#include "llvm/IR/Function.h"
  3#include "llvm/IR/Module.h"
  4#include "llvm/IR/BasicBlock.h"
  5#include "llvm/IR/Instruction.h"
  6#include "llvm/IR/Instructions.h"
  7#include "llvm/Support/raw_ostream.h"
 8 
  9 usingnamespace llvm;
 10 
 11namespace
 12{
 13  struct BasicBlockName : publicModulePass
 14  {
 15    static char ID;
 16   BasicBlockName():ModulePass(ID){}
 17    virtual boolrunOnModule(Module &M)
 18    {
 19     for(Module::iterator Mit=M.begin(),Mite=M.end();Mit!=Mite;Mit++)
 20     {
 21      errs()<<"Function:"<<Mit->getName()<<"\n";
 22       for(llvm::Function::iterator Fit =Mit->begin(),Fite = Mit->end();Fit!=Fite;Fit++)
 23       {
 24         llvm::BasicBlock* bb =Fit;
 25        errs()<<"----BasicBlock:"<<bb->getName()<<"\n";
 26         if(bb->size()>1)
 27         {

 28          for(BasicBlock::iteratorBit=bb->begin(),Bite=bb->end();Bit!=Bite;Bit++)
 29          {
 30           errs()<<"--------"<<Bit->getOperand(0)->getName()<<"| "
 31             <<Bit->getOperand(0)->getValueName()<<" |"
 32             <<Bit->getOperand(0)->getValueID()<<" |"
 33             <<Bit->getOperand(0)->getNumUses()<<" ** end\n";
 34          }
 35         }
 36       }
 37     }
 38      returnfalse;
 39    }
 40  };
 41}
 42 
 43 charBasicBlockName::ID = 0;
 44 staticRegisterPass Y("BasicBlock","BBName",false,false);
3)对代码中进行解释:
第13行处定义了一个继承自ModulePass的BaiscBlockName结构体,17行是具体实现该函数功能,19、22、28分别针对不同的层次使用一个for循环遍历IR中的对象,代码中getName、getValue等函数得到需要的值然后打印出来即可。
4)需要到编译llvm的build文件夹下面make一下,就会在Release+Asserts/lib下面生成BasicBlockName.so文件,然后按照官方文档使用,opt-load ***/BasicBlockName.so-BasicBlock test.bc > /dev/null 这样即可打印出对应的名字。

2、删除IR中一条未被使用的指令。
1)所有的步骤和上述一致,源代码如下所示:

  1 #include "llvm/Pass.h"
  2 #include "llvm/IR/Function.h"
  3 #include "llvm/IR/Module.h"
  4 #include "llvm/IR/BasicBlock.h"
  5 #include "llvm/IR/Instruction.h"
  6 #include "llvm/IR/Instructions.h"
  7 #include"llvm/Support/raw_ostream.h"
  8 #include"llvm/Transforms/Utils/Local.h"
  9 
 10 using namespace llvm;
 11 
 12 namespace
 13 {
 14   structDeleteInstruction : public BasicBlockPass
 15   {
 16    static char ID;
 17    DeleteInstruction():BasicBlockPass(ID){}
 18 
 19   virtual boolrunOnBasicBlock(BasicBlock &BB)
 20   {
 21    errs()<<"do nothing"<<"\n";
 22    bool change = false;
 23     intsign = 0;
 24     intcount = 0;
 25    errs()<<"Initilized erasecount:"<<count<<"\n";
 26    for(BasicBlock::iterator DI = BB.begin();DI!=BB.end();)
 27    {

 28     Instruction *Inst = DI++;
 29     if(isInstructionTriviallyDead(Inst))
 30      {
 31       Inst->eraseFromParent();
 32       change = true;
 33      }
 34      sign++;
 35      count++;
 36    }
 37    errs()<<"Before erasecount:"<<count<<"\n";
 38    count = 0;
 39    for(BasicBlock::iterator DI =BB.begin();DI!=BB.end();)
 40    {
 41     DI++;
 42      count++;
 43    }
 44    errs()<<"After erasecount:"<<count<<"\n";
 45    return change;
 46    return false;
 47  }
 48  };
 49 }
 50 
 51 charDeleteInstruction::ID = 0;
 52 staticRegisterPass X("DeleteInstruction","DI",false,false);
2)代码解释:
因为对Instruction进行操作,所以只需要继承BasicBlockPass即可,29行对指令是否会被用到进行判断,判断函数来自第8行的头文件里面,31行删除就会删除未被使用的指令,由于目前还不知道怎么将修改后的保存到磁盘,所有在39新加了一个循环对删除后的指令进行计数,前后不一致则表示删除成功。
3)运行结果:

do nothing
Initilized erase count:0
Before erase count:11
After erase count:10

需要注意的是源程序中需要有变量未被初始化和使用,这里才会出现不同。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: