From: Ted Kremenek Date: Wed, 13 Feb 2008 23:08:21 +0000 (+0000) Subject: Added support to GRCoreEngine/GRExprEngine for processing control-flow X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=daeb9a7376830d637e02b5bc51faf4750a7bce70;p=clang Added support to GRCoreEngine/GRExprEngine for processing control-flow from switch...case...default statements. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@47100 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Analysis/GRCoreEngine.cpp b/Analysis/GRCoreEngine.cpp index 5a4ae81ecd..0e8be29186 100644 --- a/Analysis/GRCoreEngine.cpp +++ b/Analysis/GRCoreEngine.cpp @@ -213,6 +213,15 @@ void GRCoreEngineImpl::HandleBlockExit(CFGBlock * B, ExplodedNodeImpl* Pred) { return; } + case Stmt::SwitchStmtClass: { + GRSwitchNodeBuilderImpl builder(Pred, B, + cast(Term)->getCond(), + this); + + ProcessSwitch(builder); + return; + } + case Stmt::WhileStmtClass: HandleBranch(cast(Term)->getCond(), Term, B, Pred); return; @@ -386,3 +395,49 @@ GRIndirectGotoNodeBuilderImpl::generateNodeImpl(const Iterator& I, return NULL; } + + +ExplodedNodeImpl* +GRSwitchNodeBuilderImpl::generateCaseStmtNodeImpl(const Iterator& I, void* St) { + + bool IsNew; + + ExplodedNodeImpl* Succ = Eng.G->getNodeImpl(BlockEdge(Eng.getCFG(), Src, + I.getBlock()), + St, &IsNew); + Succ->addPredecessor(Pred); + + if (IsNew) { + Eng.WList->Enqueue(Succ); + return Succ; + } + + return NULL; +} + + +ExplodedNodeImpl* +GRSwitchNodeBuilderImpl::generateDefaultCaseNodeImpl(void* St, bool isSink) { + + // Get the block for the default case. + assert (Src->succ_rbegin() != Src->succ_rend()); + CFGBlock* DefaultBlock = *Src->succ_rbegin(); + + bool IsNew; + + ExplodedNodeImpl* Succ = Eng.G->getNodeImpl(BlockEdge(Eng.getCFG(), Src, + DefaultBlock), + St, &IsNew); + Succ->addPredecessor(Pred); + + if (IsNew) { + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} diff --git a/Analysis/GRExprEngine.cpp b/Analysis/GRExprEngine.cpp index ed4e17c1b2..7addb5c053 100644 --- a/Analysis/GRExprEngine.cpp +++ b/Analysis/GRExprEngine.cpp @@ -65,11 +65,15 @@ class VISIBILITY_HIDDEN GRExprEngine { public: typedef ValueStateManager::StateTy StateTy; + typedef ExplodedGraph GraphTy; + typedef GraphTy::NodeTy NodeTy; + + // Builders. typedef GRStmtNodeBuilder StmtNodeBuilder; typedef GRBranchNodeBuilder BranchNodeBuilder; typedef GRIndirectGotoNodeBuilder IndirectGotoNodeBuilder; - typedef ExplodedGraph GraphTy; - typedef GraphTy::NodeTy NodeTy; + typedef GRSwitchNodeBuilder SwitchNodeBuilder; + class NodeSet { typedef llvm::SmallVector ImplTy; @@ -197,6 +201,10 @@ public: /// nodes by processing the 'effects' of a computed goto jump. void ProcessIndirectGoto(IndirectGotoNodeBuilder& builder); + /// ProcessSwitch - Called by GRCoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a switch statement. + void ProcessSwitch(SwitchNodeBuilder& builder); + /// RemoveDeadBindings - Return a new state that is the same as 'St' except /// that all subexpression mappings are removed and that any /// block-level expressions that are not live at 'S' also have their @@ -474,6 +482,97 @@ void GRExprEngine::ProcessIndirectGoto(IndirectGotoNodeBuilder& builder) { builder.generateNode(I, St); } +/// ProcessSwitch - Called by GRCoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a switch statement. +void GRExprEngine::ProcessSwitch(SwitchNodeBuilder& builder) { + + typedef SwitchNodeBuilder::iterator iterator; + + StateTy St = builder.getState(); + NonLValue CondV = cast(GetValue(St, builder.getCondition())); + + if (isa(CondV)) { + NodeTy* N = builder.generateDefaultCaseNode(St, true); + UninitBranches.insert(N); + return; + } + + StateTy DefaultSt = St; + + // While most of this can be assumed (such as the signedness), having it + // just computed makes sure everything makes the same assumptions end-to-end. + unsigned bits = getContext().getTypeSize(getContext().IntTy,SourceLocation()); + APSInt V1(bits, false); + APSInt V2 = V1; + + for (iterator I=builder.begin(), E=builder.end(); I!=E; ++I) { + + CaseStmt* Case = cast(I.getCase()); + + // Evaluate the case. + if (!Case->getLHS()->isIntegerConstantExpr(V1, getContext(), 0, true)) { + assert (false && "Case condition must evaluate to an integer constant."); + return; + } + + // Get the RHS of the case, if it exists. + + if (Expr* E = Case->getRHS()) { + if (!E->isIntegerConstantExpr(V2, getContext(), 0, true)) { + assert (false && + "Case condition (RHS) must evaluate to an integer constant."); + return ; + } + + assert (V1 <= V2); + } + else V2 = V1; + + // FIXME: Eventually we should replace the logic below with a range + // comparison, rather than concretize the values within the range. + // This should be easy once we have "ranges" for NonLValues. + + do { + nonlval::ConcreteInt CaseVal(ValMgr.getValue(V1)); + + NonLValue Result = + CondV.EvalBinaryOp(ValMgr, BinaryOperator::EQ, CaseVal); + + // Now "assume" that the case matches. + bool isFeasible; + + StateTy StNew = Assume(St, Result, true, isFeasible); + + if (isFeasible) { + builder.generateCaseStmtNode(I, StNew); + + // If CondV evaluates to a constant, then we know that this + // is the *only* case that we can take, so stop evaluating the + // others. + if (isa(CondV)) + return; + } + + // Now "assume" that the case doesn't match. Add this state + // to the default state (if it is feasible). + + StNew = Assume(DefaultSt, Result, false, isFeasible); + + if (isFeasible) + DefaultSt = StNew; + + // Concretize the next value in the range. + ++V1; + + } while (V1 < V2); + } + + // If we reach here, than we know that the default branch is + // possible. + builder.generateDefaultCaseNode(DefaultSt); +} + + void GRExprEngine::VisitLogicalExpr(BinaryOperator* B, NodeTy* Pred, NodeSet& Dst) { @@ -861,8 +960,8 @@ void GRExprEngine::VisitAssignmentLHS(Expr* E, GRExprEngine::NodeTy* Pred, } void GRExprEngine::VisitBinaryOperator(BinaryOperator* B, - GRExprEngine::NodeTy* Pred, - GRExprEngine::NodeSet& Dst) { + GRExprEngine::NodeTy* Pred, + GRExprEngine::NodeSet& Dst) { NodeSet S1; if (B->isAssignmentOp()) @@ -1354,7 +1453,30 @@ struct VISIBILITY_HIDDEN DOTGraphTraits : Out << "\\|Terminator: "; E.getSrc()->printTerminator(Out); - if (isa(T) || isa(T)) { + if (isa(T)) { + Stmt* Label = E.getDst()->getLabel(); + + if (Label) { + if (CaseStmt* C = dyn_cast(Label)) { + Out << "\\lcase "; + C->getLHS()->printPretty(Out); + + if (Stmt* RHS = C->getRHS()) { + Out << " .. "; + RHS->printPretty(Out); + } + + Out << ":"; + } + else { + assert (isa(Label)); + Out << "\\ldefault:"; + } + } + else + Out << "\\l(implicit) default:"; + } + else if (isa(T)) { // FIXME } else { diff --git a/include/clang/Analysis/PathSensitive/ExplodedGraph.h b/include/clang/Analysis/PathSensitive/ExplodedGraph.h index acc97084ec..871d765bd3 100644 --- a/include/clang/Analysis/PathSensitive/ExplodedGraph.h +++ b/include/clang/Analysis/PathSensitive/ExplodedGraph.h @@ -31,6 +31,7 @@ class ExplodedNodeImpl; class GRStmtNodeBuilderImpl; class GRBranchNodeBuilderImpl; class GRIndirectGotoNodeBuilderImpl; +class GRSwitchNodeBuilderImpl; class CFG; class ASTContext; class FunctionDecl; @@ -43,6 +44,7 @@ protected: friend class GRStmtNodeBuilderImpl; friend class GRBranchNodeBuilderImpl; friend class GRIndirectGotoNodeBuilderImpl; + friend class GRSwitchNodeBuilderImpl; class NodeGroup { enum { Size1 = 0x0, SizeOther = 0x1, AuxFlag = 0x2, Mask = 0x3 }; @@ -198,6 +200,7 @@ protected: friend class GRStmtNodeBuilderImpl; friend class GRBranchNodeBuilderImpl; friend class GRIndirectGotoNodeBuilderImpl; + friend class GRSwitchNodeBuilderImpl; // Type definitions. typedef llvm::DenseMap EdgeNodeSetMap; diff --git a/include/clang/Analysis/PathSensitive/GRCoreEngine.h b/include/clang/Analysis/PathSensitive/GRCoreEngine.h index 4dd2b6464a..f5ad8700ff 100644 --- a/include/clang/Analysis/PathSensitive/GRCoreEngine.h +++ b/include/clang/Analysis/PathSensitive/GRCoreEngine.h @@ -27,8 +27,8 @@ namespace clang { class GRStmtNodeBuilderImpl; class GRBranchNodeBuilderImpl; class GRIndirectGotoNodeBuilderImpl; +class GRSwitchNodeBuilderImpl; class GRWorkList; -class LabelStmt; //===----------------------------------------------------------------------===// /// GRCoreEngineImpl - Implements the core logic of the graph-reachability analysis. @@ -44,6 +44,7 @@ protected: friend class GRStmtNodeBuilderImpl; friend class GRBranchNodeBuilderImpl; friend class GRIndirectGotoNodeBuilderImpl; + friend class GRSwitchNodeBuilderImpl; typedef llvm::DenseMap ParentMapTy; @@ -93,6 +94,8 @@ protected: GRBranchNodeBuilderImpl& Builder) = 0; virtual void ProcessIndirectGoto(GRIndirectGotoNodeBuilderImpl& Builder) = 0; + + virtual void ProcessSwitch(GRSwitchNodeBuilderImpl& Builder) = 0; private: GRCoreEngineImpl(const GRCoreEngineImpl&); // Do not implement. @@ -331,6 +334,79 @@ public: return GRTrait::toState(NB.getState()); } }; + +class GRSwitchNodeBuilderImpl { + GRCoreEngineImpl& Eng; + CFGBlock* Src; + Expr* Condition; + ExplodedNodeImpl* Pred; +public: + GRSwitchNodeBuilderImpl(ExplodedNodeImpl* pred, CFGBlock* src, + Expr* condition, GRCoreEngineImpl* eng) + : Eng(*eng), Src(src), Condition(condition), Pred(pred) {} + + class Iterator { + CFGBlock::succ_reverse_iterator I; + + friend class GRSwitchNodeBuilderImpl; + Iterator(CFGBlock::succ_reverse_iterator i) : I(i) {} + public: + + Iterator& operator++() { ++I; return *this; } + bool operator!=(const Iterator& X) const { return I != X.I; } + + CaseStmt* getCase() const { + return llvm::cast((*I)->getLabel()); + } + + CFGBlock* getBlock() const { + return *I; + } + }; + + Iterator begin() { return Iterator(Src->succ_rbegin()+1); } + Iterator end() { return Iterator(Src->succ_rend()); } + + ExplodedNodeImpl* generateCaseStmtNodeImpl(const Iterator& I, void* State); + ExplodedNodeImpl* generateDefaultCaseNodeImpl(void* State, bool isSink); + + inline Expr* getCondition() const { return Condition; } + inline void* getState() const { return Pred->State; } +}; + +template +class GRSwitchNodeBuilder { + typedef CHECKER CheckerTy; + typedef typename CheckerTy::StateTy StateTy; + typedef ExplodedGraph GraphTy; + typedef typename GraphTy::NodeTy NodeTy; + + GRSwitchNodeBuilderImpl& NB; + +public: + GRSwitchNodeBuilder(GRSwitchNodeBuilderImpl& nb) : NB(nb) {} + + typedef GRSwitchNodeBuilderImpl::Iterator iterator; + + inline iterator begin() { return NB.begin(); } + inline iterator end() { return NB.end(); } + + inline Expr* getCondition() const { return NB.getCondition(); } + + inline NodeTy* generateCaseStmtNode(const iterator& I, StateTy St) { + void *state = GRTrait::toPtr(St); + return static_cast(NB.generateCaseStmtNodeImpl(I, state)); + } + + inline NodeTy* generateDefaultCaseNode(StateTy St, bool isSink = false) { + void *state = GRTrait::toPtr(St); + return static_cast(NB.generateDefaultCaseNodeImpl(state, isSink)); + } + + inline StateTy getState() const { + return GRTrait::toState(NB.getState()); + } +}; template @@ -371,6 +447,11 @@ protected: Checker->ProcessIndirectGoto(Builder); } + virtual void ProcessSwitch(GRSwitchNodeBuilderImpl& BuilderImpl) { + GRSwitchNodeBuilder Builder(BuilderImpl); + Checker->ProcessSwitch(Builder); + } + public: /// Construct a GRCoreEngine object to analyze the provided CFG using /// a DFS exploration of the exploded graph.