本文主要介绍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<PHINode>(*I)) //是Phi指令则打印出来
errs() << *I << "\n";
}
return false;
}
};
}
char Count_Phis::ID = 0;
static RegisterPass<Count_Phis> 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;
}
# 运行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<PHINode>(*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<T>(V)若值V是T类型,则返回true,否则返回false
if (isa<PHINode>(*I))
errs() << *I << "\n";
// 2. cast<T>(V) 强制类型转换,如果类型错误则导致assertion failure。Eg,cast<PHINode>(*I)。(inst_iterator I)
// 3. V' = dyn_cast<T>(V) 要么将V转化为V',要么返回NULL。Eg,PHINode *PN = dyn_cast<PHINode>(&*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<PHINode>(*I))
{
errs() << *I << "\n";
errs()<<" -has"<< cast<PHINode>(*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<PHINode>(&*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;
}
问题:将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<PHINode*, 16> Worklist; // 保存待清除的PHI指令
for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I)
{
if (PHINode *PN = dyn_cast<PHINode>(&*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<PHINode>(&*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<PHINode>(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 <map>
using namespace llvm;
namespace
{
struct CountOp : public FunctionPass
{
std::map<std::string, int> 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 <std::string, int>::iterator i = opCounter.begin();
std::map <std::string, int>::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<CountOp> 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:
-
可将pass放入目录
llvm/lib/Transforms/DirectoryName
,DirectoryName
是自定义,如CountOp
。 -
用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<LoopInfoWrapperPass>();
AU.setPreservesAll ();
}
virtual bool runOnFunction(Function &F)
{
LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().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<BBinLoops> 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<Loop*> 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<LoopInfo>();
errs() << "Func)on " << F.getName() + "\n";
for (LoopInfo::iterator i = LI.begin(), e = LI.end(); i != e; ++i)
countBlocksInLoop(*i, 0);
return(false);
}
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<Add_No_Alias> 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
标签,很不准确,万一调用点的实参有别名怎么办?
-
- 首先对函数f(a0, …, an),若有2个或2个以上形参是指针,则加入到
candidates
集合;
- 首先对函数f(a0, …, an),若有2个或2个以上形参是指针,则加入到
-
- 只要存在调用点f(p0, …, pn),pi和pj是别名,则从
candidates
集合移除f;
- 只要存在调用点f(p0, …, pn),pi和pj是别名,则从
-
- 对剩下的
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<AliasAnalysis>();
}
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<AliasAnalysis>();
// (1) 找到所有存在别名参数的函数,存放在mayAliasCalls中(只处理被调用过的函数,没被调用就不管)
SmallPtrSet<const Function*, 32> candidateCalls; // 记录
SmallPtrSet<const Function*, 32> 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<CallInst>(&*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<AliasAnalysis>();
即可使用。
(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,如BasicBlockPass
、LoopPass
、FunctionPass
、ModulePass
、RegionPass
、CallGraphSCCPass
等。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