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中的函数,这显然就是没有被调用的函数。

0 0
原创粉丝点击