Analyzer Core: In checkDeadSymbols checker callback, provide the state in which the symbols are not yet deleted so that checkers could inspect them. Since we are now always creating a transition in ProcessStmt(), remove the logic for adding a transition when none was generated. TODO: the extra transitions will have to be removed; more cleanups; a checker that tests teh new fucntionality.

llvm-svn: 137273
This commit is contained in:
Anna Zaks 2011-08-10 23:14:54 +00:00
parent a6ab52bf9f
commit 5a56a6653f
3 changed files with 77 additions and 33 deletions

View File

@ -540,7 +540,17 @@ public:
}
const GRState* getPersistentState(GRState& Impl);
const GRState* getPersistentStateWithGDM(const GRState *FromState,
const GRState *GDMState);
bool haveEqualEnvironments(const GRState * S1, const GRState * S2) {
return S1->Env == S2->Env;
}
bool haveEqualStores(const GRState * S1, const GRState * S2) {
return S1->store == S2->store;
}
/// Periodically called by ExprEngine to recycle GRStates that were
/// created but never used for creating an ExplodedNode.
void recycleUnusedStates();
@ -690,7 +700,7 @@ inline const llvm::APSInt *GRState::getSymVal(SymbolRef sym) const {
inline SVal GRState::getSVal(const Stmt* Ex, bool useOnlyDirectBindings) const{
return Env.getSVal(Ex, *getStateManager().svalBuilder,
useOnlyDirectBindings);
useOnlyDirectBindings);
}
inline SVal GRState::getSValAsScalarOrLoc(const Stmt *S) const {

View File

@ -229,6 +229,9 @@ void ExprEngine::processCFGElement(const CFGElement E,
}
void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
// TODO: Use RAII to remove the unnecessary, tagged nodes.
//RegisterCreatedNodes registerCreatedNodes(getGraph());
// Reclaim any unnecessary nodes in the ExplodedGraph.
G.reclaimRecentlyAllocatedNodes();
// Recycle any unused states in the GRStateManager.
@ -239,74 +242,98 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
currentStmt->getLocStart(),
"Error evaluating statement");
// A tag to track convenience transitions, which can be removed at cleanup.
static unsigned tag;
Builder = &builder;
EntryNode = builder.getPredecessor();
const GRState *EntryState = EntryNode->getState();
CleanedState = EntryState;
ExplodedNode *CleanedNode = 0;
// Create the cleaned state.
const LocationContext *LC = EntryNode->getLocationContext();
SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
if (AMgr.shouldPurgeDead()) {
const GRState *St = EntryNode->getState();
getCheckerManager().runCheckersForLiveSymbols(St, SymReaper);
getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
const StackFrameContext *SFC = LC->getCurrentStackFrame();
CleanedState = StateMgr.removeDeadBindings(St, SFC, SymReaper);
} else {
CleanedState = EntryNode->getState();
// Create a state in which dead bindings are removed from the environment
// and the store. TODO: The function should just return new env and store,
// not a new state.
CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
}
// Process any special transfer function for dead symbols.
ExplodedNodeSet Tmp;
if (!SymReaper.hasDeadSymbols()) {
// Generate a CleanedNode that has the environment and store cleaned
// up. Since no symbols are dead, we can optimize and not clean out
// the constraint manager.
CleanedNode =
Builder->generateNode(currentStmt, CleanedState, EntryNode, &tag);
Tmp.Add(CleanedNode);
if (!SymReaper.hasDeadSymbols())
Tmp.Add(EntryNode);
else {
} else {
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
SaveOr OldHasGen(Builder->hasGeneratedNode);
SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols);
Builder->PurgingDeadSymbols = true;
// Call checkers with the non-cleaned state so that they could query the
// values of the soon to be dead symbols.
// FIXME: This should soon be removed.
ExplodedNodeSet Tmp2;
getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode,
CleanedState, SymReaper);
EntryState, SymReaper);
getCheckerManager().runCheckersForDeadSymbols(Tmp, Tmp2,
ExplodedNodeSet Tmp3;
getCheckerManager().runCheckersForDeadSymbols(Tmp3, Tmp2,
SymReaper, currentStmt, *this);
if (!Builder->BuildSinks && !Builder->hasGeneratedNode)
Tmp.Add(EntryNode);
// For each node in Tmp3, generate CleanedNodes that have the environment,
// the store, and the constraints cleaned up but have the user supplied
// states as the predecessors.
for (ExplodedNodeSet::const_iterator I = Tmp3.begin(), E = Tmp3.end();
I != E; ++I) {
const GRState *CheckerState = (*I)->getState();
// The constraint manager has not been cleaned up yet, so clean up now.
CheckerState = getConstraintManager().removeDeadBindings(CheckerState,
SymReaper);
assert(StateMgr.haveEqualEnvironments(CheckerState, EntryState) &&
"Checkers are not allowed to modify the Environment as a part of "
"checkDeadSymbols processing.");
assert(StateMgr.haveEqualStores(CheckerState, EntryState) &&
"Checkers are not allowed to modify the Store as a part of "
"checkDeadSymbols processing.");
// Create a state based on CleanedState with CheckerState GDM and
// generate a transition to that state.
const GRState *CleanedCheckerSt =
StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
ExplodedNode *CleanedNode = Builder->generateNode(currentStmt,
CleanedCheckerSt, *I,
&tag);
Tmp.Add(CleanedNode);
}
}
bool HasAutoGenerated = false;
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
// TODO: Remove Dest set, it's no longer needed.
ExplodedNodeSet Dst;
// Set the cleaned state.
Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I));
// Visit the statement.
Visit(currentStmt, *I, Dst);
// Do we need to auto-generate a node? We only need to do this to generate
// a node with a "cleaned" state; CoreEngine will actually handle
// auto-transitions for other cases.
if (Dst.size() == 1 && *Dst.begin() == EntryNode
&& !Builder->hasGeneratedNode && !HasAutoGenerated) {
HasAutoGenerated = true;
builder.generateNode(currentStmt, GetState(EntryNode), *I);
}
}
// NULL out these variables to cleanup.
CleanedState = NULL;
EntryNode = NULL;
currentStmt = 0;
Builder = NULL;
}

View File

@ -81,8 +81,7 @@ GRStateManager::removeDeadBindings(const GRState* state,
NewState.setStore(newStore);
SymReaper.setReapedStore(newStore);
state = getPersistentState(NewState);
return ConstraintMgr->removeDeadBindings(state, SymReaper);
return getPersistentState(NewState);
}
const GRState *GRStateManager::MarshalState(const GRState *state,
@ -338,6 +337,14 @@ void GRStateManager::recycleUnusedStates() {
recentlyAllocatedStates.clear();
}
const GRState* GRStateManager::getPersistentStateWithGDM(
const GRState *FromState,
const GRState *GDMState) {
GRState NewState = *FromState;
NewState.GDM = GDMState->GDM;
return getPersistentState(NewState);
}
const GRState* GRStateManager::getPersistentState(GRState& State) {
llvm::FoldingSetNodeID ID;