dsa算法(24)
来源:互联网 发布:star走心机编程 编辑:程序博客网 时间:2024/05/16 05:53
1.3.8.2.3.计算图节点标记
BUDataStructures::runOnModuleInternal接下来重新计算全局图中各节点的标记、同类集,然后把全局图的相关信息整合入函数图,并计算函数图的方方面面。首先,在85行将全局图节点的未完成标记去掉,在88行将全局对象以外的节点标记为未完成。而对于函数图,在105将全局图有关的全局对象节点的信息克隆、简并进指定图,然后在108行将指定图节点的未完成标记去掉,在109行将函数指针参数、返回值、可变参数及未解析的函数调用标记为未完成(但全局对象节点保留了完成标记)。
BUDataStructures::runOnModuleInternal(续)
77 // At the end ofthe bottom-up pass, the globals graph becomes complete.
78 // FIXME: This isnot the right way to do this, but it is sorta better than
79 // nothing! In particular, externally visible globals andunresolvable call
80 // nodes at theend of the BU phase should make things that they point to
81 // incomplete inthe globals graph.
82 //
83
84 GlobalsGraph->removeTriviallyDeadNodes();
85 GlobalsGraph->maskIncompleteMarkers();
86
87 // Mark externalglobals incomplete.
88 GlobalsGraph->markIncompleteNodes(DSGraph::IgnoreGlobals);
89 GlobalsGraph->computeExternalFlags(DSGraph::DontMarkFormalsExternal);
90 GlobalsGraph->computeIntPtrFlags();
91
92 //
93 // Createequivalence classes for aliasing globals so that we only need to
94 // record oneglobal per DSNode.
95 //
96 formGlobalECs();
97
98 // Merge theglobals variables (not the calls) from the globals graph back
99 // into theindividual function's graph so that changes made to globals during
100 // BU can bereflected. This is specifically needed for correct call graph
101 //
102 for(Module::iterator F = M.begin(); F != M.end(); ++F) {
103 if (!(F->isDeclaration())){
104 DSGraph *Graph = getOrCreateGraph(F);
105 cloneGlobalsInto(Graph,DSGraph::DontCloneCallNodes |
106 DSGraph::DontCloneAuxCallNodes);
107 Graph->buildCallGraph(callgraph,GlobalFunctionList, filterCallees);
108 Graph->maskIncompleteMarkers();
109 Graph->markIncompleteNodes(DSGraph::MarkFormalArgs|
110 DSGraph::IgnoreGlobals);
111 Graph->computeExternalFlags(DSGraph::DontMarkFormalsExternal);
112 Graph->computeIntPtrFlags();
113 }
114 }
1.3.8.2.3.构建完整调用图
在内联了被调用函数的DSGraph图后,我们基本上掌握了完整的信息,因此可以开始构建完整的调用图。实际上,在前面的处理中,我们一直在收集调用信息,这些都通过buildCallGraph保存在BUDataStructures的callgraph成员里。
BUDataStructures::runOnModuleInternal(续)
116 // Once thecorrect flags have been calculated. Update the callgraph.
117 for(Module::iterator F = M.begin(); F != M.end(); ++F) {
118 if (!(F->isDeclaration())){
119 DSGraph *Graph = getOrCreateGraph(F);
120 Graph->buildCompleteCallGraph(callgraph,
121 GlobalFunctionList, filterCallees);
122 }
123 }
124
125 NumCallEdges += callgraph.size();
126
127 // Put the callgraph in canonical form
128 callgraph.buildSCCs();
129 callgraph.buildRoots();
130
131 return false;
132 }
buildCompleteCallGraph的参数DCG,在上面调用处的实参是callgraph,它记录所有普通函数(不包括main)里的调用点。前面看到AuxFunctionCalls保留的是当前函数所调用的未解析的函数(比如通过函数指针的函数调用),对这些调用我们还是可以找出可能的被调用函数。这些可能的被调用函数来自GlobalFunctionList,这个集合是地址可被援引的函数(地址不可援引函数的例子有C静态函数)。
1621 void DSGraph::buildCompleteCallGraph(DSCallGraph&DCG,
1622 std::vector<const Function*>& GlobalFunctionList, boolfilter)const {
1623 //
1624 // Get the listof unresolved call sites.
1625 //
1626 constFunctionListTy& Calls = getAuxFunctionCalls();
1627 for(FunctionListTy::const_iterator ii = Calls.begin(),
1628 ee= Calls.end();
1629 ii != ee; ++ii) {
1630
1631 if (ii->isDirectCall()) continue;
1632 CallSite CS = ii->getCallSite();
1633 if (DCG.callee_size(CS) != 0)continue;
1634 std::vector<constFunction*> MaybeTargets;
1635 MaybeTargets.assign(GlobalFunctionList.begin(),GlobalFunctionList.end());
1636
1637 DCG.insert(CS, 0);
1638 //
1639 // Add to thecall graph only function targets that have well-defined
1640 // behaviorusing LLVM semantics.
1641 //
1642 for(std::vector<const Function*>::iteratorFi = MaybeTargets.begin(),
1643 Fe = MaybeTargets.end(); Fi != Fe;++Fi)
1644 if (!filter || functionIsCallable(CS,*Fi))
1645 DCG.insert(CS, *Fi);
1646 else
1647 ++NumFiltered;
1648
1649 for(DSCallSite::MappedSites_t::iterator I = ii->ms_begin(),
1650 E = ii->ms_end(); I != E; ++I) {
1651 CallSite MCS = *I;
1652 for(std::vector<const Function*>::iteratorFi = MaybeTargets.begin(),
1653 Fe = MaybeTargets.end(); Fi != Fe;++Fi) {
1654 if (!filter || functionIsCallable(MCS,*Fi))
1655 DCG.insert(MCS, *Fi);
1656 else
1657 ++NumFiltered;
1658 }
1659 }
1660 }
1661 svset<constllvm::Function*> callees;
1662 callees.insert(GlobalFunctionList.begin(),GlobalFunctionList.end());
1663 DCG.buildIncompleteCalleeSet(callees);
1664 }
在这里,只要函数是与调用语句匹配的,都把它记录为可能的目标函数(因为filter是true)。因为GlobalFunctionList中的函数能被尚未完全了解、解析的调用点(说明白点就是间接调用,通过函数指针)调用,DSCallGraph通过成员IncompleteCalleeSet来记录这个列表(1663行),这个信息是PoolAllocate遍要使用的。
1.3.8.2.3.1. 构建SCC
通过调用图给出的信息,我们可以构建调用关系中的SCC。
112 void DSCallGraph::buildSCCs() {
113 TFStack Stack;
114 TFMap ValMap;
115 unsigned NextID = 1;
116
117 for(flat_key_iterator ii = flat_key_begin(),ee = flat_key_end();
118 ii != ee; ++ii)
119 if (!ValMap.count(*ii))
120 tarjan_rec(*ii,Stack, NextID, ValMap);
121
122 removeECFunctions();
123 }
117行的flat_key_begin与flat_key_end返回SimpleCallees的迭代器。
123 flat_key_iterator flat_key_begin()const {
124 returnflat_key_iterator(SimpleCallees.begin());
125 }
127 flat_key_iterator flat_key_end()const {
128 returnflat_key_iterator(SimpleCallees.end());
129 }
flat_key_iterator的定义是:KeyIterator<SimpleCalleesTy::const_iterator>,并且其中的SimpleCalleesTy是std::map<constllvm::Function*, FuncSet>。KeyIterator是一个专门访问像std::map这种映射容器指定位置上键值的迭代器。
149 template <classIter>
150 class KeyIterator
151 : publicref_mapped_iterator<Iter, select1st<typenameIter::value_type> > {
152 typedefselect1st<typename Iter::value_type>FunTy;
153 public:
154
155 KeyIterator(constIter I)
156 : ref_mapped_iterator<Iter,select1st<typename Iter::value_type> >(I, FunTy()) { }
157 };
ref_mapped_iterator作为基类提供了遍历,提领等基本操作,下面是其定义的片段。
53 template <classRootIt,class UnaryFunc>
54 class ref_mapped_iterator {
55 RootIt current;
56 UnaryFunc Fn;
…
84 inlinereference operator*() const { // All this work to do this
85 returnFn(*current); // little change
86 }
87
88 _Self & operator++() {
89 ++current;
90 return*this;
91 }
93 _Self & operator--() {
94 --current;
95 return*this;
96 }
ref_mapped_iterator接受两个参数,第一个是一个迭代器,第二个是提领函数。对于KeyIterator,这个提领函数是select1st:
41 template <class_Pair>class select1st:public _Select1st<_Pair> {
42 };
select1st实际是一个functor。显然对于SimpleCalleesTy::const_iterator,其()操作符返回键值Function*。
23 template <class _Pair>
24 class _Select1st :public std::unary_function<_Pair, typename _Pair::first_type> {
25 public:
26
27 const typename _Pair::first_type & operator()(const_Pair& __x) const {
28 return__x.first;
29 }
30 };
对于SimpleCallees的一个指定元素,其第一项Function是调用者函数,第二项FuncSet则是第一项所调用的函数的集合。因此在对下面tarjan_rec的调用里,参数F接受调用者函数的Function*。59行的flat_callee_begin类似于flat_key_begin,但返回的是SimpleCallees指定元素中的第二项FuncSet——被调用的函数集合。
51 unsigned DSCallGraph::tarjan_rec(const llvm::Function* F, TFStack& Stack,
52 unsigned&NextID, TFMap& ValMap) {
53 assert(!ValMap.count(F)&& "Shouldn't revisit functions!");
54 unsigned Min = NextID++, MyID = Min;
55 ValMap[F] = Min;
56 Stack.push_back(F);
57
58 // The edges outof the current node are the call site targets...
59 for(flat_iterator ii = flat_callee_begin(F),
60 ee = flat_callee_end(F); ii != ee; ++ii){
61 unsigned M = Min;
62 // Have wevisited the destination function yet?
63 TFMap::iterator It = ValMap.find(*ii);
64 if (It == ValMap.end())// No, visit it now.
65 M = tarjan_rec(*ii, Stack, NextID,ValMap);
66 else if (std::find(Stack.begin(),Stack.end(), *ii) != Stack.end())
67 M = It->second;
68 if (M < Min) Min = M;
69 }
70
71 assert(ValMap[F]== MyID && "SCC construction assumption wrong!");
72 if (Min != MyID)
73 return Min;// This is part of a larger SCC!
74
75 // If this is anew SCC, process it now.
76 if (F == Stack.back()) {
77 // single nodecase
78 Stack.pop_back();
79 SCCs.insert(F);
80 } else {
81 // Take carethat the leader is not an external function
82 std::vector<constllvm::Function*> microSCC;
83 constllvm::Function* NF = 0;
84 constllvm::Function* Leader = 0;
85 do {
86 NF = Stack.back();
87 Stack.pop_back();
88 microSCC.push_back(NF);
89 if (!Leader &&!NF->isDeclaration()) Leader = NF;
90 } while (NF!= F);
91 //Leader is notan extern function
92 //Nomulti-function SCC can not have a defined function, as all externs
93 //are treatedas having no callees
94 assert(Leader&& "No Leader?");
95 SCCs.insert(Leader);
96 Leader = SCCs.getLeaderValue(Leader);
97 assert(!Leader->isDeclaration()&& "extern leader");
98 for(std::vector<constllvm::Function*>::iterator ii = microSCC.begin(),
99 ee = microSCC.end(); ii != ee; ++ii) {
100 SCCs.insert(*ii);
101 constllvm::Function* Temp = SCCs.getLeaderValue(*ii);
102 //OrderMatters
103 SCCs.unionSets(Leader, Temp);
104 assert(SCCs.getLeaderValue(Leader) == Leader && "SCC constructionwrong");
105 assert(SCCs.getLeaderValue(Temp) == Leader && "SCC constructionwrong");
106 }
107 }
108
109 return MyID;
110 }
tarjan_rec仍然使用Tarjan的SCC查找算法。Stack包含了SCC的组成成员(最顶层的函数在最前),microSCC将其中组成成员提取出来。Leader则是调用链最底层在当前程序中有定义的函数(否则就是0),而调用Leader函数必定也是在当前程序中有定义的。对于调用图而言,调用链上的SCC被处理为处于同一个同类集。
135 void DSCallGraph::removeECFunctions() {
136 //First thecallers
137 for(SimpleCalleesTy::iterator ii = SimpleCallees.begin(),
138 ee = SimpleCallees.end(); ii != ee;) {
139 constllvm::Function* Leader = SCCs.getLeaderValue(ii->first);
140 if (Leader == ii->first) {
141 // This isthe leader, leave it alone
142 ++ii;
143 } else {
144 //This is notthe leader, merge into the leader
145 SimpleCallees[Leader].insert(ii->second.begin(),ii->second.end());
146 SimpleCalleesTy::iterator tmpii = ii;
147 ++ii;
148 SimpleCallees.erase(tmpii);
149 }
150 }
151 // then thecallees
152 for(SimpleCalleesTy::iterator ii = SimpleCallees.begin(),
153 ee = SimpleCallees.end(); ii != ee;++ii) {
154 removeECs(ii->second, SCCs);
155 //and apparentself loops inside an SCC
156 ii->second.erase(ii->first);
157 }
158 for (ActualCalleesTy::iteratorii = ActualCallees.begin(),
159 ee = ActualCallees.end(); ii != ee;++ii)
160 removeECs(ii->second, SCCs);
161 }
既然处于同一个同类集的成员都由Leader来表示,那么反映调用关系的SimpleCallees也要相应更新。因为前面的tarjan_rec进行同类集合并的对象是SimpleCallees元素的第一项,直到152行,我们把同类的Function的映射值合并给Leader,并删除非Leader。那么在152行,SimpleCallees中的键值都是Leader,而映射值则是同类集成员映射值的总合(即这个同类集Function所调用的函数的总集合)。
125 static voidremoveECs(DSCallGraph::FuncSet&F,
126 llvm::EquivalenceClasses<constllvm::Function*>& ECs) {
127 DSCallGraph::FuncSet result;
128 for(DSCallGraph::FuncSet::const_iterator ii = F.begin(), ee = F.end();
129 ii != ee; ++ii)
130 result.insert(ECs.getLeaderValue(*ii));
131
132 F.swap(result);
133 }
通过removeECs,我们把这些被调用函数也通过它们自己的Leader来表示(因为所有程序中出现的函数都会出现在SCCs中)。这样的好处是大大减少了节点的数目,防止BU过程出现节点指数级增长(这是本节开头提到的第一个控制因素)。
1.3.8.2.3.2. 确定没有被调用的函数
最后确定当前程序中没有被调用的函数。前面对SCC的处理,大大简化了这里的处理——现在每个SCC只由一个节点(Leader)来代表。
163 void DSCallGraph::buildRoots() {
164 FuncSet knownCallees;
165 FuncSet knownCallers;
166 for(SimpleCalleesTy::iterator ii = SimpleCallees.begin(),
167 ee = SimpleCallees.end(); ii != ee;++ii) {
168 knownCallees.insert(ii->second.begin(),ii->second.end());
169 knownCallers.insert(ii->first);
170 }
171 knownRoots.clear();
172 std::set_difference(knownCallers.begin(),knownCallers.end(),
173 knownCallees.begin(),knownCallees.end(),
174 std::inserter(knownRoots,knownRoots.begin()));
175 }
171行的knownRoots就是DSCallGraph用来记录这些函数的成员。std::set_difference给出出现在knownCallers,而没有在knownCallees中的函数,这显然就是没有被调用的函数。
- dsa算法(24)
- dsa算法(1)
- dsa算法(2)
- dsa算法(3)
- dsa算法(4)
- dsa算法(5)
- dsa算法(6)
- dsa算法(7)
- dsa算法(8)
- dsa算法(9)
- dsa算法(10)
- dsa算法(11)
- dsa算法(12)
- dsa算法(13)
- dsa算法(14)
- dsa算法(15)
- dsa算法(16)
- dsa算法(17)
- 一个好日子
- UVa 1623 Enter The Dragon
- 编程题:计算2个日期之间包括所有月份,输出格式类似如’2015-05’
- intellij下编译so库
- 局部static变量 和 成员变量 的使用技巧
- dsa算法(24)
- VMware NAT模式 在保持原来ip前提下 使用自动获取主机网络的方法
- EmEditor添加一个快捷方式到资源管理器的上下文菜单
- Myeclipse 10.1下载与破解
- 微信公众号开发_如何接入微信支付?[网页发起支付]
- AppStore 下载失败 使用已购页面再试一次
- 启动tomcat 停止在信息: Initializing Spring root WebApplicationContext
- Tracking Movement
- 冒泡排序