至此,我们成功创建了一个函数图。接着在LocalDataStructures::runOnModule的1485行,把函数图的FucntionCalls拷贝入AuxFucntionCalls,这样可以开始处理这些函数调用了。DataStructures的成员DSInfo用于绑定Function与相应的DSGraph,在1486行完成这个绑定。接着把未知标记传播到从具有未知标记的节点可到达的节点。
221 static void propagateUnknownFlag(DSGraph * G) {
222 std::vector<DSNode *> workList;
223 DenseSet<DSNode *> visited;
224 for(DSGraph::node_iterator I = G->node_begin(), E = G->node_end(); I != E;++I)
225 if (I->isUnknownNode())
226 workList.push_back(&*I);
227
228 while(!workList.empty()) {
229 DSNode * N = workList.back();
230 workList.pop_back();
231 if (visited.count(N) != 0) continue;
232 visited.insert(N);
233 N->setUnknownMarker();
234 for(DSNode::edge_iterator I = N->edge_begin(), E = N->edge_end(); I != E;++I)
235 if (!I->second.isNull())
236 workList.push_back(I->second.getNode());
237 }
238 }
具体来说,未知标记出现在这样的情形中:DSNode的某个偏移处同时代表整数类型及指针类型、ConstantExpr到不是PointerType的转换、既不是转换也不是GetElementPtr操作的ConstantExpr、整数到指针的转换、非x86平台上的变长参数、intrinsicllvm.stacksave、intrinsic llvm.stackstore、intrinsic llvm.returnaddress、intrinsic llvm.frameaddress、其他DSA不处理的指令(visitInstruction)。
在LocalDataStructures::runOnModule的1488行,callgraph是DataStructure的成员,其insureEntry方法确保在SimpleCallees(Function到Function集合的std::map)中已经创建了对应的Function集合(显然集合是空的)。
接着,通过下面的函数在callgraph中构建调用图。注意,这只是调用图构建的第一步,这个调用图会传递给后续的DSA遍进行后续处理。
1566 void DSGraph::buildCallGraph(DSCallGraph& DCG,std::vector<const Function*>&
GlobalFunctionList, bool filter) const {
1567 //
1568 // Get the list ofunresolved call sites.
1569 //
1570 constFunctionListTy& Calls = getFunctionCalls();
1571 for(FunctionListTy::const_iterator ii = Calls.begin(),
1572 ee = Calls.end();
1573 ii != ee; ++ii) {
1574 //
1575 // Direct callsare easy. We know to where they go.
1576 //
1577
1578 if (ii->isDirectCall()) {
1579 DCG. insert(ii->getCallSite(),ii->getCalleeFunc());
在当前函数中调用的函数指令都记录在FunctionCalls中,1571行的循环遍历这个集合。如果是直接函数调用,调用下面的函数处理。在llvm IR中instruction基类的Parent总是BasicBlock(包含它的基本块),而BasicBlock的Parent总是Function(包含它的方法),因此235行获得了包含这个函数调用的Function对象(的地址)。
230 void
231 DSCallGraph::insert(llvm::CallSiteCS, const llvm::Function* F) {
232 //
233 // Find thefunction to which the call site belongs.
234 //
235 constllvm::Function * Parent = CS.getInstruction()->getParent()->getParent();
236
237 //
238 // Determine theSCC leaders for both the calling function and the called
239 // function. If they don't belong to an SCC, add them asleaders.
240 //
241 SCCs.insert (Parent);
242 SCCs.insert (F);
243 constllvm::Function * ParentLeader = SCCs.getLeaderValue (Parent);
244 constllvm::Function * FLeader =SCCs.getLeaderValue (F);
245
246 //
247 // Create an emptyset for the callee; hence, all called functions get to be
248 // in the callgraph also. This simplifies SCCformation.
249 //
250 SimpleCallees[ParentLeader];
251 if (F) {
252 ActualCallees[CS].insert(FLeader);
253 SimpleCallees[ParentLeader].insert(FLeader);
254 }
255 }
241行的SCCs的类型是llvm::EquivalenceClasses<constllvm::Function*>,这是一个同类集的集合,借这个同类集构成一个强连同分量(stronglyconnected component),故以此命名。252行的ActualCallees是CallSite到Function集合的std::map。成员SimpleCallees记录了调用者与被调用者一对多的关系,而ActualCallees则记录了调用点与被调用者一对多的关系(比如函数指针)。
DSGraph::buildCallGraph(续)
1580 } else {
1581 CallSite CS = ii->getCallSite();
1582 std::vector<constFunction*> MaybeTargets;
1583
1584 if(ii->getCalleeNode()->isIncompleteNode())
1585 continue;
1586 //
1587 // Get the listof known targets of this function.
1588 //
1589 ii->getCalleeNode()->addFullFunctionList(MaybeTargets);
1590
1591 //
1592 // Ensure thatthe call graph at least knows about (has a record of) this
1593 // call site.
1594 //
1595 DCG.insert(CS,0);
1596
1597 //
1598 // Add to thecall graph only function targets that have well-defined
1599 // behaviorusing LLVM semantics.
1600 //
1601 for(std::vector<const Function*>::iteratorFi = MaybeTargets.begin(),
1602 Fe = MaybeTargets.end(); Fi != Fe;++Fi)
1603 if (!filter || functionIsCallable(CS, *Fi))
1604 DCG.insert(CS, *Fi);
1605 else
1606 ++NumFiltered;
1607 for(DSCallSite::MappedSites_t::iterator I = ii->ms_begin(),
1608 E = ii->ms_end(); I != E; ++I) {
1609 CallSite MCS = *I;
1610 for(std::vector<const Function*>::iteratorFi = MaybeTargets.begin(),
1611 Fe = MaybeTargets.end(); Fi != Fe;++Fi)
1612 if (!filter ||functionIsCallable(MCS, *Fi))
1613 DCG.insert(MCS, *Fi);
1614 else
1615 ++NumFiltered;
1616 }
1617 }
1618 }
1619 }
在1589行,addFullFunctionList把父图中所有作为同类集Leader的Function对象记录在MaybeTargets里,因为我们假定所有的函数都可以是间接调用的目标。1601行遍历这个集合,查找可被指定CallSite调用的函数。另外,前面看到,DSCallSite的MappedSites记录了对同一个函数调用的CallSite对象,这是由CallSite简并产生的,在1607行遍历这个集合。
接下来在LocalDataStructures::runOnModule的1490行,清除当前函数图中节点的未完成标记。接着调用markIncompleteNodes,只把参数标记为未完成(在前面,我们还把全局对象标记为未完成)。
而在对cloneIntoGlobals的调用中,第二个参数起作用只有DSGraph::StripAllocaBit。通过ReachabilityCloner,函数图中的全局对象节点合并回全局图中对应的节点里。这一步是必须的,因为当前函数可能操作了全局对象,这必须反映在全局图里。这是把全局图与函数图中全局对象DSNode统一起来的第二步。
1463 void DataStructures::cloneIntoGlobals(DSGraph*Graph, unsigned cloneFlags) {
1464 // When this graphis finalized, clone the globals in the graph into the
1465 // globals graph tomake sure it has everything, from all graphs.
1466 DSScalarMap &MainSM =Graph->getScalarMap();
1467 ReachabilityCloner RC(GlobalsGraph, Graph,cloneFlags);
1468
1469 // Clone everythingreachable from globals in the function graph into the
1470 // globals graph.
1471 for(DSScalarMap::global_iterator I = MainSM.global_begin(),
1472 E = MainSM.global_end(); I != E; ++I)
1473 RC.getClonedNH(MainSM[*I]);
1474 }
上述的操作涉及全局对象节点的简并,因此接下来要再次找出新形成的同类集,并使用同类集的Leader替换图中的同类集成员。
因为在节点简并时,NodeType的简并操作是“|”操作符,这意味着打上的标记不可能通过简并去掉。要获得正确的NodeType,必须要小心地处理。尤其是未完成标记,如前所述,这个标记对DSA算法有深刻的影响。首先,在1478行,maskIncompleteMarkers去掉全局图的节点的未完成标记。在1490行,指定的函数图也通过maskIncompleteMarkers去掉了所有节点的未完成标记。接着在1491行,将指针参数、返回值、可变参数以及调用的函数标记为未完成。随后,把其中的全局对象节点克隆、简并回全局图(在这个过程中,还去掉了AllocaNode标记)。而在1501行,把全局图中全局对象以外的节点标记为未完成。在1509行再次计算全局对象的同类集(这个调用似乎并不必要)。接着传播未知标记。
LocalDataStructures::runOnModule(续)
1500 //GlobalsGraph->removeTriviallyDeadNodes();
1501 GlobalsGraph->markIncompleteNodes(DSGraph::MarkFormalArgs
1502 |DSGraph::IgnoreGlobals);
1503 GlobalsGraph->computeExternalFlags(DSGraph::ProcessCallSites);
1504
1505 // Now that we've computed all of the graphs,and merged all of the info into
1506 // the globalsgraph, see if we have further constrained the globals in the
1507 // program if so,update GlobalECs and remove the extraneous globals from the
1508 // program.
1509 formGlobalECs();
1510
1511 propagateUnknownFlag(GlobalsGraph);
1512 for(Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
1513 if (!I->isDeclaration()) {
1514 DSGraph *Graph = getOrCreateGraph(I);
1515 Graph->maskIncompleteMarkers();
1516 cloneGlobalsInto(Graph,DSGraph::DontCloneCallNodes |
1517 DSGraph::DontCloneAuxCallNodes);
1518 Graph->markIncompleteNodes(DSGraph::MarkFormalArgs
1519 |DSGraph::IgnoreGlobals);
1520 }
1521
1522 return false;
1523 }
在1512行,又一次遍历当前模块中的函数,对于LocalDataStructures的安排,1514行的getOrCreateGraph不做任何事。同样在1515行,清除全局图中节点的未完成标记,并在1518行设置函数参数节点的未完成标记。
在1516行,通过下面的函数从全局图向函数图导入全局对象的信息。这是把全局图与函数图中全局对象DSNode统一起来的最后一步。
1448 void DataStructures::cloneGlobalsInto(DSGraph*Graph, unsigned cloneFlags) {
1449 // If this graphcontains main, copy the contents of the globals graph over.
1450 // Note that thisis *required* for correctness. If acallee contains a use
1451 // of a global, wehave to make sure to link up nodes due to global-argument
1452 // bindings.
1453 constDSGraph* GG = Graph->getGlobalsGraph();
1454 ReachabilityCloner RC(Graph, GG, cloneFlags);
1455
1456 // Clone the globalnodes into this graph.
1457 for(DSScalarMap::global_iterator I = Graph->getScalarMap().global_begin(),
1458 E =Graph->getScalarMap().global_end(); I != E; ++I)
1459 RC.getClonedNH(GG->getNodeForValue(*I));
1460 }
最后LocalDataStructures::runOnModule返回false,表示当前模块没有发生变化。