1.3.8. BUDataStructures遍
BUDataStructures中的BU是bottom up的缩写,Chris在他的论文里详细描述了这个过程:自底向上(BU)分析阶段通过从每个函数中的函数调用(callees of each function)整合过程间信息,来提炼每个函数的局部图。BU分析的结果是对每个函数汇集了调用该函数总体效果的图(比如强加的别名,及修改/引用信息),该图不带任何调用上下文信息。通过把所有已知被调用者的BU图克隆入调用者的局部图,合并由相应形参及实参指向的节点,来计算这个图。
通过从被调用者克隆函数图至调用者,并且进行函数图内联,BU分析是计算完整上下文感知分析结果中关键的步骤。为调用图的每条边进行图克隆,通过内联的调用路径隐含地命名对象,直接提供了完整上下文感知结果。克隆节点本质上是一个指数递增过程,但可以通过3个因素来控制:1)联合(unification)合并了大多数的克隆节点(比如通常把列表总结为递归节点),2)在调用者中不可访问的内存对象,不会从被调用者拷贝,3)对应全局变量的节点在内联发生时总是被合并(即在一个被调用者里全局对象G的节点与调用者里G的节点合并,如果那里有G的话),由于第一点,这导致递归合并。指数性的行为虽然在理论上可能,但在实践中这不会发生。
Chris在论文中将BU遍分析划分为3个层次:没有递归的基本分析、没有函数指针的递归、有函数指针的递归。BUDataStructures属于前两个层次,CompleteBUDataStructures属于第三个层次。
1.3.8.1. 初始化
在init方法中,BUDataStructures从StdLibDataStructures拷贝其处理后的结果图。这是下一个遍从上一个遍获取结果的方式。
48 bool BUDataStructures::runOnModule(Module&M) {
49 init(&getAnalysis<StdLibDataStructures>(), true,true, false, false );
50
51 return runOnModuleInternal(M);
52 }
BUDataStructures::runOnModuleInternal从前面产生的函数图构建调用图,执行调用链SCC的查找,及自底向上内联。它还是CompleteDataStructure以及EquivBUDataStructure遍的主要操作。
59 bool BUDataStructures::runOnModuleInternal(Module&M) {
60
61 //
62 // Make sure wehave a DSGraph for all declared functions in the Module.
63 // While we maynot need them in this DSA pass, a later DSA pass may ask us
64 // for theirDSGraphs, and we want to have them if asked.
65 //
66 for(Module::iterator F = M.begin(); F != M.end(); ++F) {
67 if (!(F->isDeclaration())){
68 getOrCreateGraph(F);
69 }
70 }
71
72 //
73 // Do apost-order traversal of the SCC callgraph and do bottom-up inlining.
74 //
75 postOrderInline(M);
1.3.8.2. 后序内联被调用者的DSGraph
1.3.8.2.1.内联全局构造函数
postOrderInline对调用图执行后续遍历,同时对DSGraph进行自底向上内联。下面240行的llvm.global_ctors是个数组,它包含了一组构造函数及其调用优先级。在模块载入时,这个数组所援引的函数以优先级数值的升序调用(即优先级数值最小的,第一个被调用)。
229 void
230 BUDataStructures::postOrderInline (Module & M) {
231 // Variables usedfor Tarjan SCC-finding algorithm. Theseare passed into
232 // the recursivefunction used to find SCCs.
233 std::vector<constFunction*> Stack;
234 std::map<constFunction*, unsigned> ValMap;
235 unsigned NextID = 1;
236
237
238 // Do post ordertraversal on the global ctors. Use this information to update
239 // the globalsgraph.
240 const char*Name = "llvm.global_ctors";
241 GlobalVariable *GV = M.getNamedGlobal(Name);
242 if (GV && !(GV->isDeclaration())&& !(GV->hasLocalLinkage())) {
243 // Should be anarray of '{ int, void ()* }' structs. The first value is
244 // the initpriority, which we ignore.
245 ConstantArray *InitList =dyn_cast<ConstantArray>(GV->getInitializer());
246 if (InitList) {
247 for (unsigned i = 0, e =InitList->getNumOperands(); i != e; ++i)
248 if (ConstantStruct *CS =dyn_cast<ConstantStruct>(InitList->getOperand(i))) {
249 if (CS->getNumOperands() != 2)
250 break;// Not array of 2-element structs.
251 Constant *FP = CS->getOperand(1);
252 if (FP->isNullValue())
253 break; // Found a nullterminator, exit.
254
255 if (ConstantExpr *CE =dyn_cast<ConstantExpr>(FP))
256 if (CE->isCast())
257 FP = CE->getOperand(0);
258 Function *F =dyn_cast<Function>(FP);
259 if (F &&!F->isDeclaration() && !ValMap.count(F)) {
260 calculateGraphs(F, Stack, NextID,ValMap);
261 CloneAuxIntoGlobal(getDSGraph(*F));
262 }
263 }
llvm.global_ctors元素的类型是{i32, void ()* },在245行取得这个数组的内容,在247行开始遍历其中的每个元素,251行又取出其中函数指针(void ()*)部分,在260行为这个函数“绘图”。
1.3.8.2.1.1. 寻找SCC
calculateGraphs的作用就是自底向上从被调用者向调用者内联DSGraph。参数列表中的类型TarjanStack与TarjanMap分别是std::vector<const Function*>及std::map<constFunction*, unsigned>。内联DSGraph的第一步是找出调用链上的最大连通分量(SCC),而出现节点数大于1的SCC,意味着存在递归调用。
362 unsigned
363 BUDataStructures::calculateGraphs (constFunction *F,
364 TarjanStack& Stack,
365 unsigned& NextID,
366 TarjanMap& ValMap) {
367 assert(!ValMap.count(F)&& "Shouldn't revisit functions!");
368 unsigned Min = NextID++, MyID = Min;
369 ValMap[F] = Min;
370 Stack.push_back(F);
371
372 //
373 // FIXME: Thistest should be generalized to be any function that we have
374 // alreadyprocessed in the case when there isn't a main() or there are
375 // unreachablefunctions!
376 //
377 if (F->isDeclaration()) { // sprintf,fprintf, sscanf, etc...
378 // No callees!
379 Stack.pop_back();
380 ValMap[F] = ~0;
381 return Min;
382 }
383
384 //
385 // Get theDSGraph of the current function. Makeone if one doesn't exist.
386 //
387 DSGraph* Graph = getOrCreateGraph(F);
388
389 //
390 // Find allcallee functions. Use the DSGraph forthis (do not use the call
391 // graph(DSCallgraph) as we're still in the process of constructing it).
392 //
393 FuncSet CalleeFunctions;
394 getAllAuxCallees(Graph,CalleeFunctions);
getAllAuxCallees返回该函数图中的调用点处可以解析的函数列表。其实,除了可解析的条件之外,还要“类型”匹配,这些都由下面的函数来保证。
212 void BUDataStructures::
213 getAllAuxCallees (DSGraph* G, FuncSet & Callees) {
214 //
215 // Clear out thelist of callees.
216 //
217 Callees.clear();
218 for(DSGraph::afc_iterator I = G->afc_begin(), E = G->afc_end(); I != E; ++I)
219 getAllCallees(*I,Callees);
220 }
在DSGraph所对应的函数中的调用指令都保存在FunctionCalls及AuxFunctionCalls中,FunctionCalls是由Local遍创建后就不再更改的,这里使用AuxFunctionCalls,在218行的afc_begin得到其迭代器,由getAllCallees处理其中的每个代表函数调用的DSCallSite对象。
167 BUDataStructures::
168 getAllCallees(const DSCallSite&CS, FuncSet &Callees) {
169 //
170 // FIXME: Shouldwe check for the Unknown flag on indirect call sites?
171 //
172 // Direct callsto functions that have bodies are always resolvable.
173 // Indirectfunction calls that are for a complete call site (the analysis
174 // knowseverything about the call site) and do not target external functions
175 // are alsoresolvable.
176 //
177 if (CS.isDirectCall()) {
178 if(!CS.getCalleeFunc()->isDeclaration())
179 Callees.insert(CS.getCalleeFunc());
180 } else if(CS.getCalleeNode()->isCompleteNode()) {
181 // Get allcallees.
182 if(!CS.getCalleeNode()->isExternFuncNode()) {
183 // Get allthe callees for this callsite
184 FuncSet TempCallees;
185 CS.getCalleeNode()->addFullFunctionSet(TempCallees);
186 // Filter outthe ones that are invalid targets with respect
187 // to thisparticular callsite.
188 applyCallsiteFilter(CS,TempCallees);
189 // Insert theremaining callees (legal ones, if we're filtering)
190 // into themaster 'Callees' list
191 Callees.insert(TempCallees.begin(),TempCallees.end());
192 }
193 }
194 }
172行的注释谈到,对有定义的函数的直接调用总是可解析的。而对一个完整调用点(分析知道这个调用点的一切)的间接函数调用,并且它不对准外部函数,也是可解析的。180与182行就体现了间接调用的这些条件。间接调用通常涉及多个函数,185行会把所有涉及的函数都添加入TempCallees中。然后由下面的函数来确定谁留下。
143 void BUDataStructures::
144 applyCallsiteFilter(constDSCallSite &DCS, FuncSet &Callees) {
145
146 if (!filterCallees) return;
147
148 FuncSet::iterator I = Callees.begin();
149 CallSite CS = DCS.getCallSite();
150 while (I != Callees.end()){
151 if (functionIsCallable(CS,*I)) {
152 ++I;
153 } else {
154 I = Callees.erase(I);
155 }
156 }
157 }
在BUDataStructures的缺省构造函数中,146行的filterCallees被设置为true。因此,通过下面的函数确定指定函数是否能被该调用点调用。如果不能调用,就从候选中删除该函数(154行)。
1477 bool
1478 llvm::functionIsCallable (ImmutableCallSite CS, constFunction* F) {
1479 //Which targetsdo we choose?
1480 //Conservative:all of them
1481 //Pretty Safe:same calling convention, otherwise undefined behavior
1482 //Safe on somearchs:
1483 //Safe?: varargcall only calling vararg functions
1484 //Safe?:non-vararg call only calling non-vararg functions
1485 //Safe?: iany/ptrcan't be interchanged in args w/ float/double
1486 //Not so safe:number of args matching
1487 constPointerType* PT =cast<PointerType>(CS.getCalledValue()->getType());
1488 constFunctionType* FT = cast<FunctionType>(PT->getElementType());
1489
1490 //
1491 // If the callingconvention doesn't match, then the function cannot be
1492 // called by thiscall site.
1493 //
1494 if (!noDSACallConv &&CS.getCallingConv() != F->getCallingConv())
1495 returnfalse;
1496
1497 //
1498 // We willconsider the byval parameter attribute to be a part of the calling
1499 //convention. If an actual argument ismarked byval while the formal
1500 // argument isnot (or vice-versa), then the function is not a valid target.
1501 //
1502 if (!noDSACallConv) {
1503 Function::const_arg_iterator farg =F->arg_begin(), fend = F->arg_end();
1504 for(unsigned index = 1; index < (CS.arg_size() + 1) && farg != fend;
1505 ++farg, ++index) {
1506 if (CS.isByValArgument(index) !=farg->hasByValAttr()) {
1507 returnfalse;
1508 }
1509 }
1510 }
1511
1512 //
1513 // If the callerand callee don't agree on whether the target is a vararg
1514 // function, thenthe function is not a valid target.
1515 //
1516 if (!noDSACallVA && FT->isVarArg()!= F->isVarArg())
1517 returnfalse;
1518
1519 //
1520 // If callingthis function from this call site would require an implicit
1521 // integer tofloating point cast (or vice-versa), then don't consider the
1522 // functioncallable from this call site.
1523 //
1524 if (!noDSACallFP) {
1525 unsigned ANumParams =F->getFunctionType()->getNumParams();
1526 unsigned PNumParams =FT->getNumParams();
1527 unsigned NumParams = (ANumParams <PNumParams) ? ANumParams : PNumParams;
1528 for(unsigned index = 0; index < NumParams; ++index) {
1529 Type * AType =F->getFunctionType()->getParamType(index);
1530 Type * PType =FT->getParamType(index);
1531 if ((AType->isFPOrFPVectorTy()&& !PType->isFPOrFPVectorTy())
1532 ||
1533 (!AType->isFPOrFPVectorTy()&& PType->isFPOrFPVectorTy()))
1534 returnfalse;
1535 }
1536 }
1537
1538 if (!noDSACallNumArgs) {
1539 if(CS.arg_size() < F->arg_size()) {
1540 returnfalse;
1541 }
1542 }
1543
1544 //
1545 // We've done allthe checks we've cared to do. Thefunction F can be called
1546 // from this callsite.
1547 //
1548 return true;
1549 }
上面的noDSACallConv、noDSACallVA、noDSACallFP及noDSACallNumArgs都是可以通过命令行选项来设置的。分别对应dsa-no-filter-callcc(不要根据调用规范过滤调用点,缺省是false)、dsa-no-filter-vararg(不要根据vararg出现与否来过滤调用点,缺省是true)、dsa-no-filter-intfp(不要根据隐含的整数与浮点间转换来过滤调用点,缺省是false),dsa-no-filter-numargs(不要根据实参个数来过滤调用点,缺省是false)。
BUDataStructures::calculateGraphs(续)
396 //
397 // Iteratethrough each call target (these are the edges out of the current
398 // node (i.e.,the current function) in Tarjan graph parlance). Find the
399 // minimumassigned ID.
400 //
401 for(FuncSet::iterator I = CalleeFunctions.begin(), E = CalleeFunctions.end();
402 I != E; ++I) {
403 constFunction *Callee = *I;
404 unsigned M;
405 //
406 // If we havenot visited this callee before, visit it now (this is the
407 // post-ordercomponent of the Bottom-Up algorithm). Otherwise, look up
408 // the assignedID value from the Tarjan Value Map.
409 //
410 TarjanMap::iterator It =ValMap.find(Callee);
411 if (It == ValMap.end()) // No, visit itnow.
412 M = calculateGraphs(Callee, Stack,NextID, ValMap);
413 else //Yes, get it's number.
414 M = It->second;
415
416 //
417 // If we'vefound a function with a smaller ID than this funtion, record
418 // that ID asthe minimum ID.
419 //
420 if (M < Min) Min = M;
421 }
422
423 assert(ValMap[F]== MyID && "SCC construction assumption wrong!");
424
425 //
426 // If the minimumID found is not this function's ID, then this function is
427 // part of alarger SCC.
428 //
429 if (Min != MyID)
430 return Min;
在获得了可解析、可调用的函数列表后,在401行遍历这个列表,对其中的每个函数,如果尚未处理的,递归调用calculateGraphs。注意calculateGraphs开头的几行,Min与MyID都被赋予了NextID传入的值,NextID进而递增,接着MyID与参数F被添加入ValMap,因此如果411行的条件不满足,表明对该函数应该调用过calculateGraphs,在414行获取该函数的MyID,并通过420行将Min保持为该SCC中最小的MyID,因此只有SCC的头节点才满足429行条件。如下图所示,图中有2个SCC,分别是1-3-4及1-2-4。节点1的calculateGraphs首先递归进入节点2,继而进入节点4,节点4最终调用节点1,返回节点1的MyID(1)给节点2,节点2将这个值也保存为Min并返回给节点1,因此节点1进入下面的后续处理。