| //= ReachabilityEngine.cpp - Path-Sens. Dataflow Engine ------------*- C++ -*-// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines a generic engine for intraprocedural, path-sensitive, |
| // dataflow analysis via graph reachability engine. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Analysis/PathSensitive/ReachabilityEngine.h" |
| #include "clang/AST/Stmt.h" |
| #include "llvm/Support/Casting.h" |
| |
| using namespace clang; |
| using clang::reng::WorkList; |
| using llvm::isa; |
| using llvm::cast; |
| |
| // Place dstor here so that all of the virtual functions in DFS have their |
| // code placed in the object file of this translation unit. |
| clang::reng::DFS::~DFS() {} |
| |
| ReachabilityEngineImpl::ReachabilityEngineImpl(CFG& c, |
| clang::reng::WorkList* wlist) |
| : cfg(c), WList(wlist) {} |
| |
| ExplodedNodeImpl* ReachabilityEngineImpl::getNode(const ProgramEdge& Loc, |
| void* State, |
| ExplodedNodeImpl* Pred) { |
| |
| bool IsNew; |
| ExplodedNodeImpl* V = G->getNodeImpl(Loc,State,&IsNew); |
| |
| // Link the node with its predecessor. |
| V->addUntypedPredecessor(Pred); |
| |
| if (IsNew) { |
| // Only add the node to the worklist if it was freshly generated. |
| WList->Enqueue(V); |
| |
| // Check if the node's edge is a StmtStmtEdge where the destination |
| // statement is not a BlockLevelExpr. In this case, we must lazily |
| // populate ParentMap. |
| if (isa<StmtStmtEdge>(Loc)) { |
| Stmt* S = cast<StmtStmtEdge>(Loc).Dst(); |
| assert (CurrentBlkExpr != NULL); |
| |
| if (S != CurrentBlkExpr && ParentMap.find(S) == ParentMap.end()) { |
| // Populate ParentMap starting from CurrentBlkExpr. |
| PopulateParentMap(CurrentBlkExpr); |
| assert (ParentMap.find(S) != ParentMap.end()); |
| } |
| } |
| } |
| |
| return V; |
| } |
| |
| void ReachabilityEngineImpl::PopulateParentMap(Stmt* Parent) { |
| for (Stmt::child_iterator I=Parent->child_begin(), |
| E=Parent->child_end(); I!=E; ++I) { |
| |
| assert (ParentMap.find(*I) == ParentMap.end()); |
| ParentMap[*I] = Parent; |
| PopulateParentMap(*I); |
| } |
| } |
| |
| bool ReachabilityEngineImpl::ExecuteWorkList(unsigned Steps) { |
| |
| // Initialize the analysis by constructing the root if none exists. |
| if (G->num_roots() == 0) { |
| // Get the entry block. Make sure that it has 1 (and only 1) successor. |
| CFGBlock* Entry = &cfg.getEntry(); |
| assert (Entry->empty() && "Entry block must be empty."); |
| assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); |
| |
| // Get the first (and only) successor of Entry. |
| CFGBlock* Succ = *(Entry->succ_begin()); |
| |
| // Construct an edge representing the starting location in the function. |
| BlkBlkEdge StartLoc(Entry,Succ); |
| |
| // Create the root node. |
| WList->Enqueue(G->addRoot(G->getNodeImpl(StartLoc,getInitialState(),NULL))); |
| } |
| |
| while (Steps && WList->hasWork()) { |
| --Steps; |
| ExplodedNodeImpl* V = WList->Dequeue(); |
| |
| // Dispatch on the location type. |
| switch (V->getLocation().getKind()) { |
| case ProgramEdge::BlkBlk: |
| ProcessBlkBlk(cast<BlkBlkEdge>(V->getLocation()),V); |
| break; |
| |
| case ProgramEdge::BlkStmt: |
| ProcessBlkStmt(cast<BlkStmtEdge>(V->getLocation()),V); |
| break; |
| |
| case ProgramEdge::StmtBlk: |
| ProcessStmtBlk(cast<StmtBlkEdge>(V->getLocation()),V); |
| break; |
| |
| case ProgramEdge::StmtStmt: |
| ProcessStmt(cast<StmtStmtEdge>(V->getLocation()).Dst(),V); |
| break; |
| |
| default: |
| assert (false && "Unsupported edge type."); |
| } |
| } |
| |
| return WList->hasWork(); |
| } |
| |
| |
| void ReachabilityEngineImpl::ProcessBlkBlk(const BlkBlkEdge& E, |
| ExplodedNodeImpl* Pred) { |
| |
| CFGBlock* Blk = E.Dst(); |
| |
| // Check if we are entering the EXIT block. |
| if (Blk == &cfg.getExit()) { |
| assert (cfg.getExit().size() == 0 && "EXIT block cannot contain Stmts."); |
| // Process the End-Of-Path. |
| void* State = ProcessEOP(Blk, Pred->State); |
| bool IsNew; |
| ExplodedNodeImpl* V = G->getNodeImpl(BlkStmtEdge(Blk,NULL),State,&IsNew); |
| V->addUntypedPredecessor(Pred); |
| if (IsNew) G->addEndOfPath(V); |
| return; |
| } |
| |
| // FIXME: we will dispatch to a function that manipulates the state |
| // at the entrance to a block. |
| |
| if (!Blk->empty()) { |
| // If 'Blk' has at least one statement, create a BlkStmtEdge and create |
| // the appropriate node. This is the common case. |
| getNode(BlkStmtEdge(Blk,Blk->front()), Pred->State, Pred); |
| } |
| else { |
| // Otherwise, create a node at the BlkStmtEdge right before the terminator |
| // (if any) is evaluated. |
| getNode(StmtBlkEdge(NULL,Blk),Pred->State, Pred); |
| } |
| } |
| |
| void ReachabilityEngineImpl::ProcessBlkStmt(const BlkStmtEdge& E, |
| ExplodedNodeImpl* Pred) { |
| if (Stmt* S = E.Dst()) |
| ProcessStmt(S,Pred); |
| else { |
| // No statement. Create an edge right before the terminator is evaluated. |
| getNode(StmtBlkEdge(NULL,E.Src()), Pred->State, Pred); |
| } |
| } |
| |
| void ReachabilityEngineImpl::ProcessStmtBlk(const StmtBlkEdge& E, |
| ExplodedNodeImpl* Pred) { |
| CFGBlock* Blk = E.Dst(); |
| |
| if (Stmt* Terminator = Blk->getTerminator()) |
| ProcessTerminator(Terminator,Pred); |
| else { |
| // No terminator. We should have only 1 successor. |
| assert (Blk->succ_size() == 1); |
| getNode(BlkBlkEdge(Blk,*(Blk->succ_begin())), Pred); |
| } |
| } |