Rework ExprEngine::processCFGBlockEntrance()

to use a node builder.  This paves the way
for Checkers to interpose (via a "visit" method)
at the entrance to blocks.

llvm-svn: 123217
This commit is contained in:
Ted Kremenek 2011-01-11 06:37:47 +00:00
parent f82068a994
commit a00bccc0c5
6 changed files with 117 additions and 24 deletions

View File

@ -37,6 +37,7 @@ namespace ento {
/// any transfer function logic and the sub-expression level (if any).
class CoreEngine {
friend class StmtNodeBuilder;
friend class GenericNodeBuilderImpl;
friend class BranchNodeBuilder;
friend class IndirectGotoNodeBuilder;
friend class SwitchNodeBuilder;
@ -397,6 +398,50 @@ public:
const GRState* getState() const { return Pred->State; }
};
class GenericNodeBuilderImpl {
protected:
CoreEngine &engine;
ExplodedNode *pred;
bool HasGeneratedNode;
ProgramPoint pp;
llvm::SmallVector<ExplodedNode*, 2> sinksGenerated;
ExplodedNode *generateNodeImpl(const GRState *state, ExplodedNode *pred,
ProgramPoint programPoint, bool asSink);
GenericNodeBuilderImpl(CoreEngine &eng, ExplodedNode *pr, ProgramPoint p)
: engine(eng), pred(pr), HasGeneratedNode(false), pp(p) {}
public:
bool hasGeneratedNode() const { return HasGeneratedNode; }
WorkList &getWorkList() { return *engine.WList; }
ExplodedNode* getPredecessor() const { return pred; }
BlockCounter getBlockCounter() const {
return engine.WList->getBlockCounter();
}
const llvm::SmallVectorImpl<ExplodedNode*> &sinks() const {
return sinksGenerated;
}
};
template <typename PP>
class GenericNodeBuilder : public GenericNodeBuilderImpl {
public:
GenericNodeBuilder(CoreEngine &eng, ExplodedNode *pr, const PP &p)
: GenericNodeBuilderImpl(eng, pr, p) {}
ExplodedNode *generateNode(const GRState *state, ExplodedNode *pred,
PP programPoint, bool asSink) {
return generateNodeImpl(state, pred, programPoint, asSink);
}
const PP &getProgramPoint() const { return cast<PP>(pp); }
};
class EndOfFunctionNodeBuilder {
CoreEngine &Eng;
const CFGBlock& B;

View File

@ -193,12 +193,10 @@ public:
void ProcessTemporaryDtor(const CFGTemporaryDtor D,
StmtNodeBuilder &builder);
/// processCFGBlockEntrance - Called by CoreEngine when start processing
/// a CFGBlock. This method returns true if the analysis should continue
/// exploring the given path, and false otherwise.
bool processCFGBlockEntrance(const CFGBlock* B, const ExplodedNode *Pred,
BlockCounter BC);
/// Called by CoreEngine when processing the entrance of a CFGBlock.
virtual void processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
GenericNodeBuilder<BlockEntrance> &nodeBuilder);
/// ProcessBranch - Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a branch condition.
void processBranch(const Stmt* Condition, const Stmt* Term,

View File

@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_GR_SUBENGINE_H
#define LLVM_CLANG_GR_SUBENGINE_H
#include "clang/Analysis/ProgramPoint.h"
#include "clang/StaticAnalyzer/PathSensitive/SVals.h"
namespace clang {
@ -23,8 +24,10 @@ class LocationContext;
class Stmt;
namespace ento {
template <typename PP> class GenericNodeBuilder;
class AnalysisManager;
class ExplodedNodeSet;
class ExplodedNode;
class GRState;
class GRStateManager;
@ -52,12 +55,11 @@ public:
/// nodes by processing the 'effects' of a block-level statement.
virtual void processCFGElement(const CFGElement E, StmtNodeBuilder& builder)=0;
/// Called by CoreEngine when start processing
/// a CFGBlock. This method returns true if the analysis should continue
/// exploring the given path, and false otherwise.
virtual bool processCFGBlockEntrance(const CFGBlock* B,
const ExplodedNode *Pred,
BlockCounter BC) = 0;
/// Called by CoreEngine when it starts processing a CFGBlock. The
/// SubEngine is expected to populate dstNodes with new nodes representing
/// updated analysis state, or generate no nodes at all if it doesn't.
virtual void processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
GenericNodeBuilder<BlockEntrance> &nodeBuilder) = 0;
/// Called by CoreEngine. Used to generate successor
/// nodes by processing the 'effects' of a branch condition.

View File

@ -1077,11 +1077,22 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred,
// Block entrance. (Update counters).
//===----------------------------------------------------------------------===//
bool ExprEngine::processCFGBlockEntrance(const CFGBlock* B,
const ExplodedNode *Pred,
BlockCounter BC) {
return BC.getNumVisited(Pred->getLocationContext()->getCurrentStackFrame(),
B->getBlockID()) < AMgr.getMaxVisit();
void ExprEngine::processCFGBlockEntrance(ExplodedNodeSet &dstNodes,
GenericNodeBuilder<BlockEntrance> &nodeBuilder){
// FIXME: Refactor this into a checker.
const CFGBlock *block = nodeBuilder.getProgramPoint().getBlock();
ExplodedNode *pred = nodeBuilder.getPredecessor();
if (nodeBuilder.getBlockCounter().getNumVisited(
pred->getLocationContext()->getCurrentStackFrame(),
block->getBlockID()) >= AMgr.getMaxVisit()) {
static int tag = 0;
const BlockEntrance &BE = nodeBuilder.getProgramPoint();
BlockEntrance BE_tagged(BE.getBlock(), BE.getLocationContext(), &tag);
nodeBuilder.generateNode(pred->getState(), pred, BE_tagged, true);
}
}
//===----------------------------------------------------------------------===//

View File

@ -288,13 +288,29 @@ void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) {
return;
}
// FIXME: Should we allow processCFGBlockEntrance to also manipulate state?
// Call into the subengine to process entering the CFGBlock.
ExplodedNodeSet dstNodes;
BlockEntrance BE(Blk, Pred->getLocationContext());
GenericNodeBuilder<BlockEntrance> nodeBuilder(*this, Pred, BE);
SubEng.processCFGBlockEntrance(dstNodes, nodeBuilder);
if (SubEng.processCFGBlockEntrance(Blk, Pred, WList->getBlockCounter()))
generateNode(BlockEntrance(Blk, Pred->getLocationContext()),
Pred->State, Pred);
if (dstNodes.empty()) {
if (!nodeBuilder.hasGeneratedNode()) {
// Auto-generate a node and enqueue it to the worklist.
generateNode(BE, Pred->State, Pred);
}
}
else {
blocksAborted.push_back(std::make_pair(L, Pred));
for (ExplodedNodeSet::iterator I = dstNodes.begin(), E = dstNodes.end();
I != E; ++I) {
WList->enqueue(*I);
}
}
for (llvm::SmallVectorImpl<ExplodedNode*>::const_iterator
I = nodeBuilder.sinks().begin(), E = nodeBuilder.sinks().end();
I != E; ++I) {
blocksAborted.push_back(std::make_pair(L, *I));
}
}
@ -446,6 +462,27 @@ void CoreEngine::generateNode(const ProgramPoint& Loc,
if (IsNew) WList->enqueue(Node);
}
ExplodedNode *
GenericNodeBuilderImpl::generateNodeImpl(const GRState *state,
ExplodedNode *pred,
ProgramPoint programPoint,
bool asSink) {
HasGeneratedNode = true;
bool isNew;
ExplodedNode *node = engine.getGraph().getNode(programPoint, state, &isNew);
if (pred)
node->addPredecessor(pred, engine.getGraph());
if (isNew) {
if (asSink) {
node->markAsSink();
sinksGenerated.push_back(node);
}
return node;
}
return 0;
}
StmtNodeBuilder::StmtNodeBuilder(const CFGBlock* b, unsigned idx,
ExplodedNode* N, CoreEngine* e,
GRStateManager &mgr)

View File

@ -1,4 +1,4 @@
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -verify %s
// RUN: %clang_cc1 -analyze -analyzer-check-objc-mem -analyzer-store=region -analyzer-max-loop 6 -verify %s
//===----------------------------------------------------------------------===//
// The following code is reduced using delta-debugging from