blob: 679be51351e87527803eaf8016a3bed9f487875e [file] [log] [blame]
//= 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);
}
}