LLVM学习之基础编程+过程间分析

本文主要介绍LLVM的基本用法,如遍历函数、基本块、指令;指令类型判断与转换;增减代码;编写pass、在pass中调用别的pass;如何进行过程间分析。

1.简介

官网:http://llvm.org/

LLVM有很多自带工具$> cd llvm/Debug+Asserts/bin

  • 编译成bc文件:$> clang -c -emit-llvm identity.c -o identity.bc

  • opt优化工具:$> opt --help 优化bc中间代码,如-die死代码消除;-reg2mem将值存在栈上。$> opt -mem2reg identity.bc -o identity.opt.bc

  • lli直接执行bc文件:$> lli t.bc

  • llc将bc文件翻译成机器码(arm/mips/x86):$> llc -march=x86 identity.opt.bc -o identity.x86

安装:参考http://llvm.org/releases/3.4/docs/GettingStarted.html

# 获取LLVM
$> svn co http://llvm.org/svn/llvm-project/llvm/tags/RELEASE_34/final llvm!
$> cd llvm/tools!
$> svn co http://llvm.org/svn/llvm-project/cfe/tags/RELEASE_34/final clang!
$> cd ../projects/!
$> svn co http://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_34/final
compiler-rt!
$> cd ../tools/clang/tools/!
$> svn co http://llvm.org/svn/llvm-project/clang-tools-extra/tags/RELEASE_34/
final extra!
#安装llvm
$> cd ~/Programs/llvm # that's where I have downloaded it.!
$> mkdir build!
$> ../configure!
$> make -j16          # Assuming you have more than 1 core.!

优化级别-O0(默认),-O1-O2-O3。每个级别对应不同的优化。


2.编程

编程手册参考:http://llvm.org/docs/ProgrammersManual.html

API查询:http://llvm.org/doxygen/

学习方法:最好的学习方法是去读源码,用好grep

$> cd llvm/lib/Analysis !
$~Programs/llvm/lib/Analysis> grep -r inst_iterator  *

2.1 案例分析

问题:打印phi指令。

// pass
#include "llvm/IR/Instructions.h" 
#include "llvm/Support/InstIterator.h" 
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm; 
namespace 
{
	struct Count_Phis : public FunctionPass 
  { 
    static char ID;
		Count_Phis() : FunctionPass(ID) {}
		virtual bool runOnFunction(Function &F) 
    {
			errs() << "Function " << F.getName() << '\n';
			for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I)  // 遍历函数F中的指令
      {
  			if (isa(*I)) //是Phi指令则打印出来
        	errs() << *I << "\n";
			}
			return false; 
  	}
	}; 
}
char Count_Phis::ID = 0;
static RegisterPass X("countphis","Counts phi-instructions per function");
// 测试程序
int foo(int n, int m) {
  int sum = 0;
  int c0;
  for (c0 = n; c0 > 0; c0--) 
  {
    int c1 = m;
    for (; c1 > 0; c1--) 
    {
      sum += c0 > c1 ? 1 : 0;
    }
	}
  return sum;
}

LLVM学习之基础编程+过程间分析_第1张图片

# 运行pass
$> clang -c -emit-llvm c.c -o c.bc
$> opt -mem2reg c.bc -o c.rbc
$> opt -load dcc888.dylib -countphis -disable-output c.rbc 
Function foo!
  %sum.0 = phi i32 [ 0, %entry ], [ %sum.1, %for.inc5 ]
  %c0.0 = phi i32 [ %n, %entry ], [ %dec6, %for.inc5 ]
  %sum.1 = phi i32 [ %sum.0, %for.body ], [ %add, %for.inc ]
  %c1.0 = phi i32 [ %m, %for.body ], [ %dec, %for.inc ]

2.2 API

(1)遍历指令的两种方法

// 方法1:inst_iterator
virtual bool runOnFunction(Function &F) {
  for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I)
    if (isa(*I)) errs() << *I << "\n";
	return false; 
}
// 方法2:BasicBlock::iterator
for(Function::iterator bb = F.begin(), e = F.end(); bb != e; ++bb) 
  for(BasicBlock::iterator i = bb->begin(), e = bb->end(); i != e; ++i)
Instruction* inst = i;

(2)类型推断(runtime type inference —RTTI)

// 1. isa(V)若值V是T类型,则返回true,否则返回false
if (isa(*I))
	errs() << *I << "\n";
// 2. cast(V) 强制类型转换,如果类型错误则导致assertion failure。Eg,cast(*I)。(inst_iterator I)
// 3. V' = dyn_cast(V) 要么将V转化为V',要么返回NULL。Eg,PHINode *PN = dyn_cast(&*I)。
// 4. cast_or_null 可处理null指针,不常用
// 5. dyn_cast_or_null<> 可处理null指针,不常用

示例

// 示例一: cast<>
virtual bool runOnFunction(Function &F) 
{
	errs() << "FuncFon " << F.getName() << '\n';
	for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) 
  {
		if (isa(*I)) 
    { 
      errs() << *I << "\n"; 
      errs()<<" -has"<< cast(*I).getNumIncomingValues() << " arguments.\n";  // 对指令*I使用cast进行类型转换,才能获取参数,因为inst_iterators类不含getNumIncomingValues()调用。
    }
	}
	return false; 
}
/* 输出示例
$> clang -c -emit-llvm c.c -o c.bc!
$> opt -mem2reg c.bc -o c.rbc!
$> opt -load dcc888.dylib -countphis -disable-output c.rbc
Function foo!
  %sum.0 = phi i32 [ 0, %entry ], [ %sum.1, %for.inc5 ]
  - has 2 arguments.
  %c0.0 = phi i32 [ %n, %entry ], [ %dec6, %for.inc5 ]
  - has 2 arguments.
  %sum.1 = phi i32 [ %sum.0, %for.body ], [ %add, %for.inc ]
  - has 2 arguments.
  %c1.0 = phi i32 [ %m, %for.body ], [ %dec, %for.inc ]
  - has 2 arguments.
*/

// 示例二: dyn_cast<>
virtual bool runOnFunction(Function &F) 
{
	errs() << "FuncFon " << F.getName() << '\n';
	for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) 
  {
		if (PHINode *PN = dyn_cast(&*I))  // 用dyn_cast<>就不需要判断指令I是不是Phi指令了,如果不是Phi,则直接返回null。
    { 
      errs() << *PN << "\n";
			int numArgs = PN->getNumIncomingValues(); 
      errs() << " - has " << numArgs << " parameters\n"; 
      for (int arg = 0; arg < numArgs; arg++) 
      {
				errs() << " Argument " << arg << ":\n";
				errs() << " " << PN->getIncomingBlock(arg)->getName() << ": " << *(PN->getIncomingValue(arg)) << "\n"; 
      }
		} 
  }
	return false; 
}
/* 输出
Function foo!
  %sum.0 = phi i32 [ 0, %entry ], [ %sum.1, %for.inc5 ]!
  - has 2 parameters!
    Argument 0:!
    entry: i32 0!
    Argument 1:!
    for.inc5:   %sum.1 = phi i32 [ %sum.0, %for.body ], [ %add, %for.inc ]!
  %c0.0 = phi i32 [ %n, %entry ], [ %dec6, %for.inc5 ]!
  - has 2 parameters ...!
*/

(3)转换代码——改变CFG

功能:增减指令,增减基本块,增减函数。

示例目标:若Phi的两个参数相同,则将Phi指令替换为一个参数。

int main(int argc, char** argv) { 
  int x = 0;
	if (argc % 2) {
		x = 0; 
  }
	return x; 
}

LLVM学习之基础编程+过程间分析_第2张图片

问题:将c代码编译成.bc文件时会自动优化并去掉phi指令,所以可以手动写LLVM代码(ll文件)并编译成.bc文件。

# play.ll
target triple = "i386-apple-macosx10.5.0"
define i32 @main(i32 %argc, i8** %argv) #0 { 
entry:
	%tobool = icmp eq i32 %argc, 2
	br i1 %tobool, label %if.then, label %if.end
	
if.then:
	br label %if.end

if.end:
	%x.0 = phi i32 [ 1, %entry ], [ 1, %if.then ] 
	ret i32 %x.0
}
# 编译命令
$> clang -c -emit-llvm play.ll -o play.bc
$> opt -view-cfg play.bc
$> clang play.ll ; ./a.out ; echo $?
1

代码

struct Count_Phis : public FunctionPass 
{ 
  static char ID;
	Count_Phis () : FunctionPass(ID) {}
	virtual bool runOnFunction(Function &F) 
  { 
    bool cutInstruction = false;
		errs() << "Function " << F.getName() << '\n';
    SmallVector Worklist; // 保存待清除的PHI指令
		for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) 
    { 
      if (PHINode *PN = dyn_cast(&*I)) 
      {
 				if (PN->hasConstantValue()) 
        {
					errs() << *PN << " has constant value.\n"; 
    // 1. 先保存指令,之后再清除
          Worklist.push_back(PN);
          cutInstruction = true;
        } 
      } 
    }
    // 2. 清除指令 
    while (!Worklist.empty()) 
    {
      PHINode* PN = Worklist.pop_back_val(); 
      PN->replaceAllUsesWith(PN->getIncomingValue(0)); // 替换API
      PN->eraseFromParent();
    }
    return cutInstrucFon; 
  }
};

测试

$> opt -load dcc888.dylib -countphis  play.bc -o play2.bc
$> clang play2.ll ; ./a.out ; echo $?
1

优化:PHI指令一般只位于基本块的开头,所以可以提高效率。

		for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) 
    { 
      if (PHINode *PN = dyn_cast(&*I)) 
// 改为 ->   遍历基本块,取第1条指令
        for (Function::iterator B = F.begin(), EB = F.end(); B != EB; ++B) { 
          for (BasicBlock::iterator I = B->begin(), EI = B->end(); I != EI; ++I) {
            if (PHINode *PN = dyn_cast(I)) {

3.Pass编写

(1)简单pass

问题:计算给定函数的操作码类型和指令条数。

代码

#define DEBUG_TYPE "opCounter"
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include 
using namespace llvm;
namespace 
{
  struct CountOp : public FunctionPass 
  { 
    std::map opCounter;
    static char ID;
    CountOp() : FunctionPass(ID) {} // 只对每个function遍历一次,所以用FunctionPass;如果想看到全程序,则使用ModulePass
    virtual bool runOnFunction(Function &F) 
    {
      errs() << "Function " << F.getName() << '\n';
      for (Function::iterator bb = F.begin(), e = F.end(); bb != e; ++bb) 
        for (BasicBlock::iterator i = bb->begin(), e = bb->end(); i != e; ++i) 
        { // 遍历指令
          if(opCounter.find(i->getOpcodeName()) == opCounter.end())
            opCounter[i->getOpcodeName()] = 1;
          else
            opCounter[i->getOpcodeName()] += 1;
        } 
      std::map ::iterator i = opCounter.begin();
      std::map ::iterator e = opCounter.end();
      while (i != e)
      {
        errs() << i->first << ": " << i->second << "\n";
        i++;
      }
      errs() << "\n";
      opCounter.clear();
      return false;
    } 
  };
}
char CountOp::ID = 0;
static RegisterPass X("opCounter", "Counts opcodes per functions"); // 注册pass,呈现给用户的字符串说明。可用命令查看:$> opt -load CountOp.dylib -help

说明Module::iterator——遍历函数;Function::iterator——遍历基本块;BasicBlock::iterator——遍历指令;User::op_iterator——遍历指令中的操作数。

测试

int bbLoop(int n, int m) 
{
  int sum = 0;
  int c0;
  for (c0 = n; c0 > 0; c0--) 
  {
    int c1 = m;
    for (; c1 > 0; c1--)
      sum += c0 > c1 ? 1 : 0;
  }
  return sum;
}

编译pass

  1. 可将pass放入目录llvm/lib/Transforms/DirectoryNameDirectoryName是自定义,如CountOp

  2. 用Makefile更方便。

    # Path to top level of LLVM hierarchy
    LEVEL = ../../..
    
    # Name of the library to build
    LIBRARYNAME = CountOp
    
    # Make the shared library become a
    # loadable module so the tools can
    # dlopen/dlsym on the resul)ng library. 
    LOADABLE_MODULE = 1
    
    # Include the makefile implementa)on
    include $(LEVEL)/Makefile.common
    

运行pass:现在我们的pass被编译成共享库,位于llvm/Debug/lib。注意,mac中文件后缀是.dylib,linux下为.so

$> clang –c –emit-llvm file.c –o file.bc!
$> opt -load CountOp.dylib -opCounter -disable-output t.bc 
# 查看pass运行时间: -time-passes
$> opt -load CountOp.dylib -opCounter -disable-output -time-passes f.bc 

(2)pass中调用别的pass

问题:计算循环中的基本块个数,需要调用LoopInfoWrapperPass来识别循环。

代码

namespace 
{
  struct BBinLoops : public FunctionPass  // struct和class差不多
  {
    static char ID;
    BBinLoops() : FunctionPass(ID) {}
    
    void getAnalysisUsage(AnalysisUsage &AU) const 
    { // 告诉LLVM需要调用哪些pass
      AU.addRequired(); 
      AU.setPreservesAll ();
    }
    
    virtual bool runOnFunction(Function &F) 
    {
      LoopInfo &LI = getAnalysis().getLoopInfo(); // 利用getAnalysis函数获取该pass的一个指针。
      int loopCounter = 0;
      errs() << F.getName() + "\n";
      for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; ++i) 
      {  // 遍历循环 LoopInfo::iterator 返回循环的集合。只能处理一层循环
        Loop *L = *i;
        int bbCounter = 0;
        loopCounter++;
        for(Loop::block_iterator bb = L->block_begin(); bb != L->block_end(); ++bb)    // 遍历循环中的基本块 Loop::block_iterator 返回基本块的集合
          bbCounter+=1; 
        errs() << "Loop "; 
        errs() << loopCounter; 
        errs() << ": #BBs = "; 
        errs() << bbCounter; 
        errs() << "\n";
      }
      return(false); // 本pass不改变原代码,所以返回false。
    }
  }; 
}
char BBinLoops::ID = 0;
static RegisterPass X("bbloop", "Count the number of BBs inside each loop");

测试

int main(int argc, char **argv) 
{ 
  int i, j, t = 0;
  for(i = 0; i < 10; i++) {
    for(j = 0; j < 10; j++) 
    { 
      if((i + j) % 7 == 0)
        break;
      else 
        t++;
    } 
  }
  printf("%d\n", t);
  return 0; 
}

运行

$> clang –c –emit-llvm file.c –o file.bc
$> opt -load DCC888.dylib -bbloop -disable-output file.bc
Function main!
Loop 1: #BBs = 10!

(3)改进

问题:以上代码只能输出一层循环,如果循环中包含循环,如何处理?

测试程序

// 嵌套循环
int main(int argc, char **argv) 
{ 
  int i, j, k, t = 0;
  for(i = 0; i < 10; i++) 
  {
    for(j = 0; j < 10; j++) 
    { 
      for(k = 0; k < 10; k++) 
        t++;
    }
    for(j = 0; j < 10; j++)
      t++;
  }
  for(i = 0; i < 20; i++) 
  {
    for(j = 0; j < 20; j++)
      t++;
    for(j = 0; j < 20; j++) 
      t++;
  }
return t; 
}

改进代码

void countBlocksInLoop(Loop *L, unsigned nesting)  // nesting——嵌套层数
{ 
  unsigned numBlocks = 0;
  Loop::block_iterator bb;
  for(bb = L->block_begin(); bb != L->block_end();++bb) // 遍历基本块
    numBlocks++;
  errs() << "Loop level " << nesting << " has " << numBlocks << " blocks\n";
  vector subLoops = L->getSubLoops(); // 使用getSubLoops()获取嵌套循环
  Loop::iterator j, f;
  for (j = subLoops.begin(), f = subLoops.end(); j != f; ++j) // 遍历循环中的子循环
    countBlocksInLoop(*j, nesting + 1); 
}

virtual bool runOnFunction(Function &F) 
{
  LoopInfo &LI = getAnalysis();
  errs() << "Func)on " << F.getName() + "\n";
  for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; ++i)
    countBlocksInLoop(*i, 0); 
  return(false);
}

LLVM学习之基础编程+过程间分析_第3张图片


4.过程间分析

工具:Call graphs、Module passes、大量处理functions的API。

Call graphs使用示例

$> clang -c -emit-llvm file.c -o file.bc
$> opt -view-callgraph file.bc

(1)问题分析

问题:LLVM IR提供了noalias标签,表明该参数之间没有别名,我们可以用来标记函数的参数。

define void @sum_vectors( 
	i32* noalias %src1,
	i32* noalias %src2,
	i32* noalias %d
	est, i32 %n) #0 {
  ... 
}

代码:本代码采用的是继承ModulePass,其实也可以用FunctionPass。

namespace 
{    
	struct Add_No_Alias: public ModulePass 
  {      
    static char ID;      
    Add_No_Alias(): ModulePass(ID) {} 
    virtual bool runOnModule(Module &M)  
    {       
      for (Module::iterator F=M.begin(), E=M.end(); F!=E; ++F)  
      {           
        if (!F‐>isDeclaration())  //
        {               
          Function::arg_iterator Arg = F‐>arg_begin(), ArgEnd = F‐>arg_end();  // 遍历函数的参数             
          while (Arg != ArgEnd)  
          {  
            if (Arg‐>getType()‐>isPointerTy())  // 如果参数是指针,再看有没有别名
            {                       
              AttrBuilder noalias(Attribute::get(Arg‐>getContext(), Attribute::NoAlias));  // 创建标签noalias
              int argNo = Arg‐>getArgNo() + 1; // argNo——第几个参数?                 
              Arg‐>addAttr(AttributeSet::get(Arg‐>getContext(), argNo, noalias));
            }
            ++Arg;
          }
        }
      }
      return  true;
    }
  };
}
char Add_No_Alias::ID = 0;  
static RegisterPass X("addnoalias", "Add no alias to function attributes"); 
# 输出
$> clang -c -emit-llvm file.c -o file.bc
$> opt -load dcc888.dylib -addnoalias file.bc -o file.na.bc
$> llvm-dis < file.bc -o file.ll
$> llvm-dis < file.na.bc -o file.na.ll
$> diff file.ll file.na.ll
10c10
< define void @sum_vectors(i32* %src1 ...
---
> define void @sum_vectors(i32* noalias %src1 ...

(2)改进

改进一:原先代码给所有函数的指针参数(形参)加了noalias标签,很不准确,万一调用点的实参有别名怎么办?

    1. 首先对函数f(a0, …, an),若有2个或2个以上形参是指针,则加入到candidates集合;
    1. 只要存在调用点f(p0, …, pn),pi和pj是别名,则从candidates集合移除f;
    1. 对剩下的candidates集合,形参加上noalias标签。
#ifndef CALLSITEALIAS_H_  
#define CALLSITEALIAS_H_ 
using namespace llvm;  
class Collect_Args_No_Alias: public ModulePass 
{ 
  public:
  	static char ID;
  	Collect_Args_No_Alias() : ModulePass(ID) {}      
  	~Collect_Args_No_Alias() {}      
  	virtual bool runOnModule(Module &M); // 主函数,找到不含别名参数的函数,并标记noalias     
  	virtual void getAnalysisUsage(AnalysisUsage &AU) const; // 获取AliasAnalysis分析引擎 
  private:
  	AliasAnalysis* AA; // LLVM自带的简单的别名分析 
  	bool argsMayAlias(const CallInst* CI) const; // 判断call指令的实参是否别名    
  	bool isCandidate(const CallInst* CI) const;  // 判断函数是否有2个及以上的指针形参  
  	void addNoAlias(Function* F) const; // 将函数的指针形参标记为noaliass
  
  bool Collect_Args_No_Alias::argsMayAlias(const CallInst* CI) const
  { // 遍历调用指令的参数,只要其中两个参数是别名,则返回true
    unsigned n_operands = CI‐>getNumArgOperands();
    bool  mayAlias  =  false;
    for (unsigned i = 0; i < n_operands ‐ 1; ++i) 
    {
      const Value *pi = CI‐>getArgOperand(i);
      for (unsigned j = i+1; j < n_operands; ++j)  
      {
        const Value *pj = CI‐>getArgOperand(j); 
        if (AA‐>alias(pi, pj) != AliasAnalysis::NoAlias) // AA‐>alias(pi, pj)判断是否别名。返回值有四种:NoAlias / MustAlias / ParAalAlias / MayAlias。 
          mayAlias = true;
       }  
    }
    return mayAlias;
  }
  
	void Collect_Args_No_Alias::getAnalysisUsage(AnalysisUsage &AU) const 
  { // 获取AliasAnalysis分析引擎
    // AU.setPreservesAll(); 
    AU.addRequired();
  } 
  
  bool Collect_Args_No_Alias::isCandidate(const CallInst* CI) const 
  { // 判断函数是否有2个及以上的指针形参
    unsigned n_operands = CI‐>getNumArgOperands();    
    unsigned numPointerArgs = 0;    
    for (unsigned i = 0; i < n_operands; ++i)    
      if (CI‐>getArgOperand(i)‐>getType()‐>isPointerTy())     
        numPointerArgs++;  
    return numPointerArgs > 1; 
  } 
  
  void Collect_Args_No_Alias::addNoAlias(Function* F) const 
  { // 将函数的指针形参标记为noalias
    Function::arg_iterator Arg, ArgEnd;    
    for (Arg= F‐>arg_begin(), ArgEnd = F‐>arg_end(); Arg != ArgEnd; ++Arg) 
      if (Arg‐>getType()‐>isPointerTy()) 
      {        
        AttrBuilder noalias(Attribute::get(Arg‐>getContext(), Attribute::NoAlias));  
        int argNo = Arg‐>getArgNo() + 1; 
        Arg‐>addAttr(AttributeSet::get(Arg‐>getContext(), argNo, noalias)); 
      }  
  }
  
  bool Collect_Args_No_Alias::runOnModule(Module &M) 
  {
    AA = &getAnalysis(); 
// (1) 找到所有存在别名参数的函数,存放在mayAliasCalls中(只处理被调用过的函数,没被调用就不管)
    SmallPtrSet candidateCalls;  // 记录
    SmallPtrSet mayAliasCalls;  
    // 遍历call指令 
    for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) 
    {     
      if (!F‐>isDeclaration()) 
      {       
        for (inst_iterator I = inst_begin(&*F), E = inst_end(&*F); I != E; ++I) 
        {
          if (const CallInst *CI = dyn_cast(&*I)) 
          {           
            if (isCandidate(CI))   // 缺点:未判断是否重复处理,应判断是否出现在mayAliasCalls中。
            {             
              candidateCalls.insert(CI‐>getCalledFunction());            
              if (argsMayAlias(CI)) // 判断是否有别名实参
                mayAliasCalls.insert(CI‐>getCalledFunction());
            }         
          }       
        }     
      }  
    } 
// (2) 将被调用过的 且 不含别名的函数,形参标记为noalias
    bool wasModified = false;  
    for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I) 
    {     
      if (!I‐>isDeclaration()) 
      {       
        if (candidateCalls.count(I) > 0 && mayAliasCalls.count(I) == 0) // 如果被调用过 且 不含有别名,则对该函数的形参标记noalias
        {         
          addNoAlias(I);  // 
          wasModified = true; 
        }  
      }  
    } 
    return wasModified;  // 若原程序被修改,则必须返回true
  }
}; 
#endif

别名分析:别名分析返回值有四种,分别是NoAlias / MustAlias / ParAalAlias / MayAlias;AA‐>alias(v1, v2)接口用到了四种pass:-basicaa -scev-aa -globalsmodref-aa -tbaa

如何使用AliasAnalysis:先在getAnalysisUsage()函数中声明要使用AliasAnalysis,这样就得到一个指向AliasAnalysis对象的指针。再AA = &getAnalysis();即可使用。

(3)测试

测试用例

// Fast Fourier Transform
void Fft (int n, float z[], float w[], float e[]) 
{    
  int i, j, k, l, m, index; 
  m = n / 2; 
  l = 1; 
  do 
  { 
    k = 0; 
    j = l; 
    i = 1; 
    do 
    { 
      do 
      { 
        w[i + k] = z[i] + z[m + i];          
        w[i + j] = e[k + 1] * (z[i] ‐ z[i + m]) ‐ e[k + 1] * (z[i] ‐ z[i + m]);          
        w[i + j] = e[k + 1] * (z[i] ‐ z[i + m]) + e[k + 1] * (z[i] ‐ z[i + m]);          
        i = i + 1; 
      } while (i <= j);        
      k = j;        
      j = k + l;      
    } while (j <= m);      
    l++;    
  } while (l <= m);  
}
      
float* genVec(unsigned n) 
{    
  float* f = (float*)malloc(n * sizeof(float));    
  int i; 
  for (i = 0; i < n; i++)
    f[i] = 2.5 + i;
  return f; 
} 

int main(int argc, char** argv) 
{    
  int n = atoi(argv[1]);    
  float* z = genVec(n);    
  float* w = genVec(n);    
  float* e = genVec(n);    
  Fft(n, z, w, e); 
  return 0;  
} 

运行结果

# 对测试程序 标记noalias
$> clang ‐c ‐emit‐llvm fft.c ‐o file.bc  
$> opt ‐mem2reg ‐instnamer file.bc ‐o file.rbc  
$> opt ‐load dcc888.dylib ‐pointerdis file.rbc ‐o file.na.rbc  
$> llvm‐dis < file.rbc ‐o file.ll  
$> llvm‐dis < file.na.rbc ‐o file.na.ll 
$> diff file.ll file.na.ll 
6c6 
< define void @FP(i32 %n, float* %z, ... 
‐‐‐  > define void @FP(i32 %n, float* noalias %z, ...  # 运行过后多了noalias标签
# 运行未标记noalias的程序
$> clang ‐c ‐emit‐llvm file.ll ‐o file.rbc  
$> clang ‐c ‐emit‐llvm file.na.ll ‐o file.na.rbc  
$> opt ‐O2 file.rbc ‐o file.opt.rbc  
$> opt ‐O2 file.na.rbc ‐o file.opt.na.rbc  
$> llc file.opt.rbc ‐o file.opt.s  
$> gcc file.opt.s ‐o file.opt.exe  
$> time ./file.opt.exe 30000 
real  0m1.054s 
user  0m1.039s  #
sys  0m0.006s  
# 运行标记noalias后的程序
$> llc file.opt.na.rbc ‐o file.opt.na.s  
$> gcc file.opt.na.s ‐o file.opt.na.exe  
$> time ./file.opt.na.exe 30000 
real  0m0.680s 
user  0m0.668s # 标记noalias后的程序运行时间明显缩短
sys  0m0.005s 

提速原因:标记noalias之后,使LLVM能进行循环展开,循环展开后更便于对代码进行优化,这样代码执行速度显著加快。

LLVM提供了许多pass,如BasicBlockPassLoopPassFunctionPassModulePassRegionPassCallGraphSCCPass等。ModulePass可以使我们分析跨函数调用。LLVM自带很多过程间分析工具,详细见目录llvm/lib/Transforms/IPO

$> llvm/lib/Transforms/IPO$ ls
ArgumentPromoFon.cpp   
BarrierNoopPass.cpp  
CMakeLists.txt 
ConstantMerge.cpp
DeadArgumentElimination.cpp
Debug+Asserts 
ExtractGV.cpp 
FunctionAttrs.cpp    
GlobalDCE.cpp
GlobalOpt.cpp
Internalize.cpp
InlineSimple.cpp
IPConstantPropagaFon.cpp 
IPO.cpp
InlineAlways.cpp
LLVMBuild.txt
LoopExtractor.cpp
PartialInlining.cpp
PassManagerBuilder.cpp   
PruneEH.cpp   
StripDeadPrototypes.cpp   
StripSymbols.cpp

你可能感兴趣的:(静态分析,软件分析,程序分析)