EQTDDataStructures是poolalloc包里另一个实现DSA算法的遍。这个遍完整实现了ChrisLatter在其论文中提到的算法,因此不像BasicDataStructure,这是一个有实际意义的处理。这个类从TDDataStructures派生而来。
394 class EQTDDataStructures: public TDDataStructures {
395 public:
396 static charID;
397 EQTDDataStructures()
398 :TDDataStructures(ID, "eqtd.",true)
399 {}
400 ~EQTDDataStructures();
401 };
而TDDataStructures派生自DataStructures。EQTDDataStructures与TDDataStructures在代码中都进行了注册,因此可以通过getAnalysis来援引它们的实例。
31 RegisterPass<TDDataStructures> // Register thepass
32 Y("dsa-td", "Top-down DataStructure Analysis");
33
34 RegisterPass<EQTDDataStructures> // Register thepass
35 Z("dsa-eqtd", "EQ Top-downData Structure Analysis");
因此,可以使用选项-dsa-td及-dsa-eqtd指定这两个遍进行分析操作。因为在RegisterPass的过程中,会调用该Pass的构造函数。这个构造函数在上面398行,调用TDDataStructures的构造函数:
363 TDDataStructures(char& CID = ID, const char* printname ="td.", bool useEQ = false)
364 : DataStructures(CID, printname),useEQBU(useEQ) {}
EQTDDataStructures通过TDDataStructures的构造函数指出依赖的Pass,这体现在下面的TDDataStructures::getAnalysisUsage方法中,这些Pass需要在TDDataStructures之前执行。
371 virtual void getAnalysisUsage(AnalysisUsage&AU) const {
372 if (useEQBU) {
373 AU.addRequired<EquivBUDataStructures>();
374 } else {
375 AU.addRequired<BUDataStructures>();
376 AU.addPreserved<BUDataStructures>();
377 }
378 AU.setPreservesAll();
379 }
在EQTDDataStructures构造函数中,它把TDDataStructures的useEQBU设置为true,因此基类TDDataStructures仅依赖于EquivBUDataStructures。另外,这个Pass不改变所分析的llvm程序,在378行调用setPreservesAll来表示之。
而EquivBUDataStructures派生自CompleteBUDataStructures,它有这样的依赖关系:
319 virtual voidgetAnalysisUsage(AnalysisUsage&AU) const {
320 AU.addRequired<CompleteBUDataStructures>();
321 AU.setPreservesAll();
322 }
所依赖的CompleteBUDataStructures则派生自BUDataStructures,而它的依赖关系是:
295 virtual voidgetAnalysisUsage(AnalysisUsage&AU) const {
296 AU.addRequired<BUDataStructures>();
297 AU.setPreservesAll();
298 }
BUDataStructures的基类是DataStructures,但它依赖于StdLibDataStructures:
248 virtual voidgetAnalysisUsage(AnalysisUsage&AU) const {
249 AU.addRequired<StdLibDataStructures>();
250 AU.setPreservesAll();
251 }
StdLibDataStructures也派生自DataStructures,它所依赖的Pass有:
213 virtual voidgetAnalysisUsage(AnalysisUsage&AU) const {
214 AU.addRequired<LocalDataStructures>();
215 AU.addRequired<AllocIdentify>();
216 AU.setPreservesAll();
217 }
LocalDataStructures同样派生自DataStructures,它依赖于:
190 virtual voidgetAnalysisUsage(AnalysisUsage&AU) const {
191 AU.addRequired<DataLayout>();
192 AU.addRequired<AddressTakenAnalysis>();
193 AU.setPreservesAll();
194 }
AddressTakenAnalysis派生自ModulePass,它不依赖任何其它Pass。而AllocIdentify也派生自ModulePass,它依赖于LoopInfo:
191 void AllocIdentify::getAnalysisUsage(AnalysisUsage&AU) const {
192 AU.addRequired<LoopInfo>();
193 AU.setPreservesAll();
194 }
LoopInfo则派生自FunctionPass,它依赖于DominatorTree:
667 void LoopInfo::getAnalysisUsage(AnalysisUsage&AU) const {
668 AU.setPreservesAll();
669 AU.addRequired<DominatorTree>();
670 }
DominatorTree也派生自FunctionPass,它不依赖于其它任何Pass。
我们已经知道,通过AnalysisUsage::addRequired指定的Pass一定会在调用者Pass执行前先得到执行,因此我们现在有这样的依赖关系:
EquivBUDataStructures
à CompleteBUDataStructures |à DataLayout
à BUDataStructures |à LocalDataStructures -+
à StdLibDataStructures -+ |à AddressTakenAnalysis
|à AllocIdentify à LoopInfo à DominatorTree
因此Pass的执行次序是DataLayout à AddressTakenAnalysis à LocalDataStructures à DominatorTree à LoopInfo à AllocIdentify à StdLibDataStructures à BUDataStructures à CompleteBUDataStructures à EquivBUDataStructures。其中DataLayout没有实际的操作,因此,最先得到执行的是AddressTakenAnalysis。
AddressTakenAnalysis完全不修改被分析的程序。它的runOnModule方法遍历指定Module中的Function对象,找出会获取地址的llvm对象。Llvm IR有一个非常重要的特性,就是它必须是SSA形式的。在Chris Lattner以假想的Kaleidoscope语言演示llvm对新语言实现的支持的系列网文里(http://llvm.org/docs/tutorial/index.html),Chris演示了如何让llvm帮我们产生SSA形式的IR(与此同时,产生CFG)。总之,各个Pass所运行的llvm程序必须是SSA形式的,并且生成了对应的CFG及def-use链。
70 bool AddressTakenAnalysis::runOnModule(llvm::Module&M) {
71 for(Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI){
72 if(isAddressTaken(FI)) {
73 addressTakenFunctions.insert(FI);
74 }
75 }
76
77 return false;
78 }
71行的begin方法返回的是Module实例中的FunctionList,即该Module中所声明/定义的函数的集合。Llvm中函数由Function类来抽象,它的基类之一是Value(不是直接基类)。Value类在llvm中地位重要。它是程序计算的、可用于操作数或其它值的所有对象的基类。它是其它重要类,比如Instruction及Function,的超类(superclass)。
在llvm中一个特定的Value可能被使用了许多次。比如,函数的传入参数(Argument类的一个实例)为该函数中每条援引该参数的指令所“使用”。为了记录这个关系,Value类维护一个使用它的User列表(User类是llvm中可以援引Value的所有llvm IR节点类型的一个公共基类)。这个列表是llvm在程序中表示def-use信息的方式,并通过use_*方法访问。在下面38行遍历这个“使用列表”。
37 static bool isAddressTaken(Value* V) {
38 for(Value::use_iterator I = V->use_begin(), E = V->use_end(); I != E; ++I) {
39 User *U = *I;
40 if(isa<StoreInst>(U))
41 returntrue;
42 if (!isa<CallInst>(U) &&!isa<InvokeInst>(U)) {
43 if(U->use_empty())
44 continue;
45 if(isa<GlobalAlias>(U)) {
46 if(isAddressTaken(U))
47 returntrue;
48 } else {
49 if (Constant *C =dyn_cast<Constant>(U)) {
50 if (ConstantExpr *CE =dyn_cast<ConstantExpr>(C)) {
51 if (CE->getOpcode() ==Instruction::BitCast) {
52 return isAddressTaken(CE);
53 }
54 }
55 }
56 returntrue;
57 }
58
59 // FIXME: Canbe more robust here for weak aliases that
60 // are neverused
61 } else {
62 llvm::CallSiteCS(cast<Instruction>(U));
63 if (!CS.isCallee(I))
64 returntrue;
65 }
66 }
67 return false;
68 }
上面只要有一个user是获取地址的,就跳出循环。
40行的StoreInst表示向内存储存的指令,其基类是Instruction。Instruction类是使用llvm指令的类的公共基类。Instruction类本身所记录的主要数据是opcode(指令类型)及该指令所嵌入的父BasicBlock(基本块)。
CallInst类表示一个函数调用,它从Instruction派生,抽象了目标机器的调用惯例;InvokeInst表示在基本块末尾的跳转(llvm要求每个基本块的结尾以跳转结束),它从TerminatorInst派生。
Constant类代表llvm中各种常量的类的一个公共基类,ConstantExpr则是其派生类之一,代表以一个使用其它常量表达式初始化的常量值。其方法getOpcode是该表达式根节点的指令操作码(表达式的IR形式为树),其中操作码BitCast代表从一个类型到另一个类型的空操作转换。其它的转换操作码还包括截断(Trunc)、0扩展、符号扩展、指针到整数等,这些都会涉及临时对象。
62行的llvm::CallSite代表一个调用点,63行的isCallee方法表示U所代表的对象是被调用函数的参数。