1.3.3.3. 添加函数节点
接下来处理模块内的函数。1461行的addrAnalysis就是AddressTakenAnalysis的实例,方法hasAddressTaken检查FI是否在addressTakenFunctions里。
LocalDataStructures::runOnModule(续)
1459 // Add Functionsto the globals graph.
1460 for(Module::iterator FI = M.begin(), FE = M.end(); FI != FE; ++FI){
1461 if(addrAnalysis->hasAddressTaken(FI)){
1462 GGB.mergeFunction(FI);
1463 }
1464 }
1465 }
216 void mergeFunction(Function*F) { getValueDest(F); }
如果是,mergeFunction调用getValueDest为Function创建DSNode,而getValueDest调用addFunction,这个方法立即调用addGlobal把这个Function加入DSNode的Globals。
1.3.3.4. 对函数定义的处理
在创建了所有必需的DSNode节点后,在1472行,formGlobalECs只执行了其中的buildGlobalECs函数,而eliminateUsesOfECGlobals没有执行,因为这时DSInfo是空的。其效果就是全局图中同一个DSNode对应的多个全局对象被合并为同类集,在GlobalsGraph的ScalarMap及对应的DSNode的Globals中只保留同类集的Leader。
LocalDataStructures::runOnModule(续)
1467 if (hasMagicSections.size())
1468 handleMagicSections(GlobalsGraph, M);
1469
1470 // Next step,iterate through the nodes in the globals graph, unioning
1471 // together theglobals into equivalence classes.
1472 fomGlobalIECs();
1473
1474 // Iterate throughthe address taken functions in the globals graph,
1475 // collecting themin a list, to be used as target for call sites that
1476 // cant beresolved.
1477 formGlobalFunctionList();
1478 GlobalsGraph->maskIncompleteMarkers();
在DataStructures结构中GlobalFunctionList用于保存所有全局函数。这个数据将由后续的自底向上遍(BUDataStructures)使用。这些函数来自AddressTakenAnalysis的实例。
1322 void DataStructures::formGlobalFunctionList(){
1323 std::vector<constFunction*> List;
1324 DSScalarMap &SN =GlobalsGraph->getScalarMap();
1325 EquivalenceClasses<const GlobalValue*> &EC = GlobalsGraph->getGlobalECs();
1326 for(DSScalarMap::global_iterator I = SN.global_begin(), E = SN.global_end(); I !=E; ++I) {
1327 EquivalenceClasses<const GlobalValue*>::iterator ECI = EC.findValue(*I);
1328 if (ECI == EC.end()) {
1329 if (constFunction *F = dyn_cast<Function>(*I))
1330 List.push_back(F);
1331 } else {
1332 for(EquivalenceClasses<constGlobalValue*>::member_iterator MI =
1333 EC.member_begin(ECI), ME =EC.member_end(); MI != ME; ++MI){
1334 if (constFunction *F = dyn_cast<Function>(*MI))
1335 List.push_back(F);
1336 }
1337 }
1338 }
1339 GlobalFunctionList.swap(List);
1340 }
1326行的迭代器访问所有全局对象所引用的全局对象,而1325行返回全局图所包含的同类集。因为,前面执行buildGlobalECs时DSScalarMap中只包含同类集中的Leader成员,为了确保不遗漏其他同类集成员,在1332行的循环遍历指定的同类集中的Function对象。
1.3.3.4.1. 构建函数的DSGraph
除了全局对象,每个函数定义也要伴随一个DSGraph。1481行开始的循环,为每个函数定义创建一个DSGraph。
LocalDataStructures::runOnModule(续)
1480 // Calculate all ofthe graphs...
1481 for(Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
1482 if (!I->isDeclaration()) {
1483 DSGraph* G = new DSGraph(GlobalECs, getDataLayout(), *TypeSS,GlobalsGraph);
1484 GraphBuilder GGB(*I, *G, *this);
1485 G->getAuxFunctionCalls() =G->getFunctionCalls();
1486 setDSGraph(*I, G);
1487 propagateUnknownFlag(G);
1488 callgraph.insureEntry(I);
1489 G->buildCallGraph(callgraph,GlobalFunctionList, true);
1490 G->maskIncompleteMarkers();
1491 G->markIncompleteNodes(DSGraph::MarkFormalArgs
1492 |DSGraph::IgnoreGlobals);
1493 cloneIntoGlobals(G,DSGraph::DontCloneCallNodes |
1494 DSGraph::DontCloneAuxCallNodes|
1495 DSGraph::StripAllocaBit);
1496 formGlobalECs();
1497 DEBUG(G->AssertGraphOK());
1498 }
首先通过下面的函数构造每个函数定义专用的GraphBuilder。
148 GraphBuilder(Function&f, DSGraph &g, LocalDataStructures& DSi)
149 : G(g), FB(&f), DS(&DSi),TD(g.getDataLayout()), VAArray(0) {
150 // Createscalar nodes for all pointer arguments...
151 for(Function::arg_iterator I = f.arg_begin(), E = f.arg_end();
152 I != E; ++I) {
153 if(isa<PointerType>(I->getType())) {
154 // WD: Whydo we set the external marker so early in the analysis?
155 //Functions we have definitions for, but are externally reachable have no external
156 // contextsthat we'd want to BU external information into (since those contexts are
157 // bydefinition ones we don't have code for). Shouldn't this just be set in TD?
158 #if 0
159 DSNode * Node = getValueDest(I).getNode();
160
161 if (!f.hasInternalLinkage() ||!f.hasPrivateLinkage())
162 Node->setExternalMarker();
163 #else
164 getValueDest(I).getNode();
165 #endif
166
167 }
168 }
169
170 // Create anentry for the return, which tracks which functions are in
171 // the graph
172 g.getOrCreateReturnNodeFor(f);
173
174 // Create anode to handle information about variable arguments
175 g.getOrCreateVANodeFor(f);
176
177 visit(f); // Single pass overthe function
DSGraph的成员ReturnNodes的类型是std::map<constFunction*, DSNodeHandle>,它用于保存当前函数的返回值对象,使用std::map是因为一个图可能代表多个函数(在后续的BU及TDD处理中可以看到有调用及被调用关系的函数图会被合并起来);成员VANodes的类型是std::map<const Function*,DSNodeHandle> ,它用于保存当前函数的可变长实参对象,使用std::map也是因为一个图可能代表多个函数。在第一次访问它们时,会创建缺省的DSNodeHandle。
118 RetTy visit(Instruction&I) {
119 switch(I.getOpcode()) {
120 default:llvm_unreachable("Unknown instruction type encountered!");
121 // Build theswitch statement using the Instruction.def file...
122 #define HANDLE_INST(NUM, OPCODE,CLASS) \
123 caseInstruction::OPCODE: return \
124 static_cast<SubClass*>(this)->\
125 visit##OPCODE(static_cast<CLASS&>(I));
126 #include"llvm/IR/Instruction.def"
127 }
128 }
GraphBuilder的基类是InstVisitor<GraphBuilder>,InstVisitor是一个模板类,它用作指令访问者(instruction visitor)的基类。在上面124行,SubClass就是传给InstVisitor的模板实参GraphBuilder,而文件llvm/IR/Instruction.def则是根据特定的宏是否有定义,对指定类别的指令(比如内存指令,转换指令等,注意这些指令与目标机器无关,是llvm IR指令),在122行进行展开。InstVisitor已对所有的指令类别定义了visit*方法,不过这些方法都不做任何事。InstVisitor在这些visit*方法中有特别的处理,使派生类只要重载特定的方法即可,比如:
166 RetTyvisitReturnInst(ReturnInst &I) { DELEGATE(TerminatorInst);}
30 #define DELEGATE(CLASS_TO_VISIT) \
31 return static_cast<SubClass*>(this)-> \
32 visit##CLASS_TO_VISIT(static_cast<CLASS_TO_VISIT&>(I))
InstVisitor把ReturnInst,BranchInst,SwitchInst,IndirectBrInst,ResumeInst及UnreachableInst归类为TerminatorInst,派生类可只重载visitTerminatorInst来涵盖对这些指令的处理,也可以重载部分visit*方法,把余下的给visitTerminatorInst来涵盖。这极大地增加看派生类的灵活性。其他类型的指令也有类似的关系,此处省略。
GraphBuilder重载了如下的方法:
1.3.3.4.1.1. 对指令节点的处理
1.3.3.4.1.1.1. 对Φ节点的处理
Φ节点是Chris在论文中未考虑到的。Φ节点的出现在于llvmIR是SSA形式的,Φ节点是同一个变量不同版本的值的汇聚点。对于与Φ节点关联的非指针对象,Φ节点只是给出了取值的几个可能性,对象的地址不会变。不过对于指针对象,就不一样了,指针将指向几个可能的地址,这是visitPHINode要考虑的。
365 void GraphBuilder::visitPHINode(PHINode&PN) {
366 if (!isa<PointerType>(PN.getType())) return; // Only pointerPHIs
367
368 DSNodeHandle &PNDest = G.getNodeForValue(&PN);
369 for(unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i)
370 PNDest.mergeWith(getValueDest(PN.getIncomingValue(i)));
371 }
这个处理也很简单,对于与Φ节点关联的指针对象,把进入Φ节点的对象的DSNode(通过DSNodeHandle)简并,来表示指针可能引用这些对象。
1.3.3.4.1.1.2. 对select指令的处理
LlvmIR的select指令有点像表达式res = cond? val1: val2,只有在val1和val2的类型是指针时,才需要下面的处理,因为此时这两个指针及接受结果的指针(res)可能互为别名(may aliase)。
373 void GraphBuilder::visitSelectInst(SelectInst&SI) {
374 if (!isa<PointerType>(SI.getType()))
375 return; // Only pointer Selects
376
377 DSNodeHandle &Dest = G. getNodeForValue(&SI);
378 DSNodeHandle S1 = getValueDest(SI.getOperand(1));
379 DSNodeHandle S2 =getValueDest(SI.getOperand(2));
380 Dest.mergeWith(S1);
381 Dest.mergeWith(S2);
382 }
显然,处理的方法是简并这3个指针的DSNode节点,用一个DSNode节点来代替它们。
1.3.3.4.1.1.3. 对load指令的处理
在llvm中任何内存访问都必须通过与该访问的地址段关联的指针来完成,因此,load指令的源(即第一个操作数)必须是一个指针。390行的getPointerOperand实际上就是返回第一个操作数。
384 void GraphBuilder::visitLoadInst(LoadInst&LI) {
385 //
386 // Create a DSNodefor the pointer dereferenced by the load. If the DSNode
387 // is NULL, donothing more (this can occur if the load is loading from a
388 // NULL pointerconstant (bugpoint can generate such code).
389 //
390 DSNodeHandle Ptr = getValueDest(LI.getPointerOperand());
391 if (Ptr.isNull()) return;// Load from null
392
393 // Make that thenode is read from...
394 Ptr.getNode()->setReadMarker();
395
396 // Ensure atyperecord exists...
397 Ptr.getNode()->growSizeForType(LI.getType(),Ptr.getOffset());
398
399 if (isa<PointerType>(LI.getType()))
400 setDestTo(LI,getLink(Ptr));
401
402 // check that it isthe inserted value
403 if(TypeInferenceOptimize)
404 if(LI.hasOneUse())
405 if(StoreInst *SI =dyn_cast<StoreInst>(*(LI.use_begin())))
406 if(SI->getOperand(0) == &LI) {
407 ++NumIgnoredInst;
408 return;
409 }
410 Ptr.getNode()->mergeTypeInfo(LI.getType(), Ptr.getOffset());
411 }
如果load指令的目标也是指针类型,这意味着目标与源指向同一个对象,通过400行完成DSNodeHandle形式的表示(setDestTo会进行节点简并)。在410行对目标与源的类型信息进行整合,注意整合的信息保存在源节点中。
1.3.3.4.1.1.4. 对store指令的处理
类似的,llvm中store指令的目标必须是指针类型。因为操作数是从0开始计数的,所以415行获取的是第二个操作数,它是store指令的目标。
413 void GraphBuilder::visitStoreInst(StoreInst&SI) {
414 Type *StoredTy =SI.getOperand(0)->getType();
415 DSNodeHandle Dest = getValueDest(SI.getOperand(1));
416 if (Dest.isNull()) return;
417
418 // Mark that thenode is written to...
419 Dest.getNode()->setModifiedMarker();
420
421 // Ensure atype-record exists...
422 Dest.getNode()->growSizeForType(StoredTy,Dest.getOffset());
423
424 // Avoid adding edgesfrom null, or processing non-"pointer" stores
425 if (isa<PointerType>(StoredTy))
426 Dest.addEdgeTo(getValueDest(SI.getOperand(0)));
427
428 if(TypeInferenceOptimize)
429 if(SI.getOperand(0)->hasOneUse())
430 if(isa<LoadInst>(SI.getOperand(0))){
431 ++NumIgnoredInst;
432 return;
433 }
434 Dest.getNode()->mergeTypeInfo(StoredTy, Dest.getOffset());
435 }
426行的addEdgeTo把指向Dest偏移0处的DSNode节点与源操作数的DSNode节点简并。