From 94cc33f33e8381712a6e8042c7eb0ff1a961aa48 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 17 Dec 2009 20:06:29 +0000 Subject: [PATCH] Convert GRExprEngine::VisitCallExpr() to use a worklist instead of recursion to evaluate the arguments of a CallExpr. This simplifies the logic and makes it easier to read. (it also avoids any issues with blowing out the stack if the CallExpr had a ridiculous number of arguments) llvm-svn: 91613 --- .../Analysis/PathSensitive/GRExprEngine.h | 4 - clang/lib/Analysis/GRExprEngine.cpp | 109 ++++++++++-------- 2 files changed, 64 insertions(+), 49 deletions(-) diff --git a/clang/include/clang/Analysis/PathSensitive/GRExprEngine.h b/clang/include/clang/Analysis/PathSensitive/GRExprEngine.h index da95a1cf4c1d..6c422edff95b 100644 --- a/clang/include/clang/Analysis/PathSensitive/GRExprEngine.h +++ b/clang/include/clang/Analysis/PathSensitive/GRExprEngine.h @@ -266,10 +266,6 @@ protected: void VisitCall(CallExpr* CE, ExplodedNode* Pred, CallExpr::arg_iterator AI, CallExpr::arg_iterator AE, ExplodedNodeSet& Dst); - void VisitCallRec(CallExpr* CE, ExplodedNode* Pred, - CallExpr::arg_iterator AI, CallExpr::arg_iterator AE, - ExplodedNodeSet& Dst, const FunctionProtoType *, - unsigned ParamIdx = 0); /// VisitCast - Transfer function logic for all casts (implicit and explicit). void VisitCast(Expr* CastE, Expr* Ex, ExplodedNode* Pred, diff --git a/clang/lib/Analysis/GRExprEngine.cpp b/clang/lib/Analysis/GRExprEngine.cpp index 017f0682efcd..9405db7ca9b1 100644 --- a/clang/lib/Analysis/GRExprEngine.cpp +++ b/clang/lib/Analysis/GRExprEngine.cpp @@ -1549,94 +1549,112 @@ void GRExprEngine::EvalLocation(ExplodedNodeSet &Dst, Stmt *S, // Transfer function: Function calls. //===----------------------------------------------------------------------===// +namespace { +class CallExprWLItem { +public: + CallExpr::arg_iterator I; + ExplodedNode *N; + + CallExprWLItem(const CallExpr::arg_iterator &i, ExplodedNode *n) + : I(i), N(n) {} +}; +} // end anonymous namespace + void GRExprEngine::VisitCall(CallExpr* CE, ExplodedNode* Pred, CallExpr::arg_iterator AI, CallExpr::arg_iterator AE, ExplodedNodeSet& Dst) { + // Determine the type of function we're calling (if available). const FunctionProtoType *Proto = NULL; QualType FnType = CE->getCallee()->IgnoreParens()->getType(); if (const PointerType *FnTypePtr = FnType->getAs()) Proto = FnTypePtr->getPointeeType()->getAs(); - VisitCallRec(CE, Pred, AI, AE, Dst, Proto, /*ParamIdx=*/0); -} - -void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred, - CallExpr::arg_iterator AI, - CallExpr::arg_iterator AE, - ExplodedNodeSet& Dst, - const FunctionProtoType *Proto, - unsigned ParamIdx) { - - // Process the arguments. - if (AI != AE) { - // If the call argument is being bound to a reference parameter, - // visit it as an lvalue, not an rvalue. + // Create a worklist to process the arguments. + llvm::SmallVector WorkList; + WorkList.reserve(AE - AI); + WorkList.push_back(CallExprWLItem(AI, Pred)); + + ExplodedNodeSet ArgsEvaluated; + + while (!WorkList.empty()) { + CallExprWLItem Item = WorkList.back(); + WorkList.pop_back(); + + if (Item.I == AE) { + ArgsEvaluated.insert(Item.N); + continue; + } + + // Evaluate the argument. + ExplodedNodeSet Tmp; + const unsigned ParamIdx = Item.I - AI; + bool VisitAsLvalue = false; if (Proto && ParamIdx < Proto->getNumArgs()) VisitAsLvalue = Proto->getArgType(ParamIdx)->isReferenceType(); - - ExplodedNodeSet DstTmp; + if (VisitAsLvalue) - VisitLValue(*AI, Pred, DstTmp); + VisitLValue(*Item.I, Item.N, Tmp); else - Visit(*AI, Pred, DstTmp); - ++AI; + Visit(*Item.I, Item.N, Tmp); + + // Enqueue evaluating the next argument on the worklist. + ++(Item.I); - for (ExplodedNodeSet::iterator DI=DstTmp.begin(), DE=DstTmp.end(); DI != DE; - ++DI) - VisitCallRec(CE, *DI, AI, AE, Dst, Proto, ParamIdx + 1); - - return; + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + WorkList.push_back(CallExprWLItem(Item.I, *NI)); } - // If we reach here we have processed all of the arguments. Evaluate - // the callee expression. + // Now process the call itself. First evaluate the callee. ExplodedNodeSet DstTmp; Expr* Callee = CE->getCallee()->IgnoreParens(); + + for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(), + NE=ArgsEvaluated.end(); + NI != NE; ++NI) { - { // Enter new scope to make the lifetime of 'DstTmp2' bounded. ExplodedNodeSet DstTmp2; - Visit(Callee, Pred, DstTmp2); - + Visit(Callee, *NI, DstTmp2); + // Perform the previsit of the CallExpr, storing the results in DstTmp. CheckerVisit(CE, DstTmp, DstTmp2, true); } - - // Finally, evaluate the function call. + + // Finally, evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call. ExplodedNodeSet DstTmp3; for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); DI != DE; ++DI) { - + const GRState* state = GetState(*DI); SVal L = state->getSVal(Callee); - + // FIXME: Add support for symbolic function calls (calls involving // function pointer values that are symbolic). SaveAndRestore OldSink(Builder->BuildSinks); ExplodedNodeSet DstChecker; - + // If the callee is processed by a checker, skip the rest logic. if (CheckerEvalCall(CE, DstChecker, *DI)) DstTmp3.insert(DstChecker); else { for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(), - DE_Checker = DstChecker.end(); - DI_Checker != DE_Checker; ++DI_Checker) { - - // Dispatch to the plug-in transfer function. + DE_Checker = DstChecker.end(); + DI_Checker != DE_Checker; ++DI_Checker) { + + // Dispatch to the plug-in transfer function. unsigned OldSize = DstTmp3.size(); SaveOr OldHasGen(Builder->HasGeneratedNode); Pred = *DI_Checker; - + // Dispatch to transfer function logic to handle the call itself. // FIXME: Allow us to chain together transfer functions. - assert(Builder && "GRStmtNodeBuilder must be defined."); - + assert(Builder && "GRStmtNodeBuilder must be defined."); getTF().EvalCall(DstTmp3, *this, *Builder, CE, L, Pred); - + // Handle the case where no nodes where generated. Auto-generate that // contains the updated state if we aren't generating sinks. if (!Builder->BuildSinks && DstTmp3.size() == OldSize && @@ -1645,9 +1663,10 @@ void GRExprEngine::VisitCallRec(CallExpr* CE, ExplodedNode* Pred, } } } - - // Perform the post-condition check of the CallExpr. - CheckerVisit(CE, Dst, DstTmp3, false); + + // Finally, perform the post-condition check of the CallExpr and store + // the created nodes in 'Dst'. + CheckerVisit(CE, Dst, DstTmp3, false); } //===----------------------------------------------------------------------===//