dsa算法(22)

来源:互联网 发布:软件生存周期是什么 编辑:程序博客网 时间:2024/05/22 09:43

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进入下面的后续处理。


0 0