forked from OSchip/llvm-project
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:
parent
a6ab52bf9f
commit
5a56a6653f
|
|
@ -540,6 +540,16 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
const GRState* getPersistentState(GRState& Impl);
|
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
|
/// Periodically called by ExprEngine to recycle GRStates that were
|
||||||
/// created but never used for creating an ExplodedNode.
|
/// created but never used for creating an ExplodedNode.
|
||||||
|
|
@ -690,7 +700,7 @@ inline const llvm::APSInt *GRState::getSymVal(SymbolRef sym) const {
|
||||||
|
|
||||||
inline SVal GRState::getSVal(const Stmt* Ex, bool useOnlyDirectBindings) const{
|
inline SVal GRState::getSVal(const Stmt* Ex, bool useOnlyDirectBindings) const{
|
||||||
return Env.getSVal(Ex, *getStateManager().svalBuilder,
|
return Env.getSVal(Ex, *getStateManager().svalBuilder,
|
||||||
useOnlyDirectBindings);
|
useOnlyDirectBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline SVal GRState::getSValAsScalarOrLoc(const Stmt *S) const {
|
inline SVal GRState::getSValAsScalarOrLoc(const Stmt *S) const {
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,9 @@ void ExprEngine::processCFGElement(const CFGElement E,
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
|
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.
|
// Reclaim any unnecessary nodes in the ExplodedGraph.
|
||||||
G.reclaimRecentlyAllocatedNodes();
|
G.reclaimRecentlyAllocatedNodes();
|
||||||
// Recycle any unused states in the GRStateManager.
|
// Recycle any unused states in the GRStateManager.
|
||||||
|
|
@ -239,74 +242,98 @@ void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) {
|
||||||
currentStmt->getLocStart(),
|
currentStmt->getLocStart(),
|
||||||
"Error evaluating statement");
|
"Error evaluating statement");
|
||||||
|
|
||||||
|
// A tag to track convenience transitions, which can be removed at cleanup.
|
||||||
|
static unsigned tag;
|
||||||
Builder = &builder;
|
Builder = &builder;
|
||||||
EntryNode = builder.getPredecessor();
|
EntryNode = builder.getPredecessor();
|
||||||
|
|
||||||
|
const GRState *EntryState = EntryNode->getState();
|
||||||
|
CleanedState = EntryState;
|
||||||
|
ExplodedNode *CleanedNode = 0;
|
||||||
|
|
||||||
// Create the cleaned state.
|
// Create the cleaned state.
|
||||||
const LocationContext *LC = EntryNode->getLocationContext();
|
const LocationContext *LC = EntryNode->getLocationContext();
|
||||||
SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
|
SymbolReaper SymReaper(LC, currentStmt, SymMgr, getStoreManager());
|
||||||
|
|
||||||
if (AMgr.shouldPurgeDead()) {
|
if (AMgr.shouldPurgeDead()) {
|
||||||
const GRState *St = EntryNode->getState();
|
getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
|
||||||
getCheckerManager().runCheckersForLiveSymbols(St, SymReaper);
|
|
||||||
|
|
||||||
const StackFrameContext *SFC = LC->getCurrentStackFrame();
|
const StackFrameContext *SFC = LC->getCurrentStackFrame();
|
||||||
CleanedState = StateMgr.removeDeadBindings(St, SFC, SymReaper);
|
|
||||||
} else {
|
// Create a state in which dead bindings are removed from the environment
|
||||||
CleanedState = EntryNode->getState();
|
// 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.
|
// Process any special transfer function for dead symbols.
|
||||||
ExplodedNodeSet Tmp;
|
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())
|
} else {
|
||||||
Tmp.Add(EntryNode);
|
|
||||||
else {
|
|
||||||
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
|
SaveAndRestore<bool> OldSink(Builder->BuildSinks);
|
||||||
SaveOr OldHasGen(Builder->hasGeneratedNode);
|
SaveOr OldHasGen(Builder->hasGeneratedNode);
|
||||||
|
|
||||||
SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols);
|
SaveAndRestore<bool> OldPurgeDeadSymbols(Builder->PurgingDeadSymbols);
|
||||||
Builder->PurgingDeadSymbols = true;
|
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.
|
// FIXME: This should soon be removed.
|
||||||
ExplodedNodeSet Tmp2;
|
ExplodedNodeSet Tmp2;
|
||||||
getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode,
|
getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode,
|
||||||
CleanedState, SymReaper);
|
EntryState, SymReaper);
|
||||||
|
|
||||||
getCheckerManager().runCheckersForDeadSymbols(Tmp, Tmp2,
|
ExplodedNodeSet Tmp3;
|
||||||
|
getCheckerManager().runCheckersForDeadSymbols(Tmp3, Tmp2,
|
||||||
SymReaper, currentStmt, *this);
|
SymReaper, currentStmt, *this);
|
||||||
|
|
||||||
if (!Builder->BuildSinks && !Builder->hasGeneratedNode)
|
// For each node in Tmp3, generate CleanedNodes that have the environment,
|
||||||
Tmp.Add(EntryNode);
|
// 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) {
|
for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) {
|
||||||
|
// TODO: Remove Dest set, it's no longer needed.
|
||||||
ExplodedNodeSet Dst;
|
ExplodedNodeSet Dst;
|
||||||
|
|
||||||
// Set the cleaned state.
|
|
||||||
Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I));
|
|
||||||
|
|
||||||
// Visit the statement.
|
// Visit the statement.
|
||||||
Visit(currentStmt, *I, Dst);
|
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.
|
// NULL out these variables to cleanup.
|
||||||
CleanedState = NULL;
|
CleanedState = NULL;
|
||||||
EntryNode = NULL;
|
EntryNode = NULL;
|
||||||
|
|
||||||
currentStmt = 0;
|
currentStmt = 0;
|
||||||
|
|
||||||
Builder = NULL;
|
Builder = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,7 @@ GRStateManager::removeDeadBindings(const GRState* state,
|
||||||
NewState.setStore(newStore);
|
NewState.setStore(newStore);
|
||||||
SymReaper.setReapedStore(newStore);
|
SymReaper.setReapedStore(newStore);
|
||||||
|
|
||||||
state = getPersistentState(NewState);
|
return getPersistentState(NewState);
|
||||||
return ConstraintMgr->removeDeadBindings(state, SymReaper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const GRState *GRStateManager::MarshalState(const GRState *state,
|
const GRState *GRStateManager::MarshalState(const GRState *state,
|
||||||
|
|
@ -338,6 +337,14 @@ void GRStateManager::recycleUnusedStates() {
|
||||||
recentlyAllocatedStates.clear();
|
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) {
|
const GRState* GRStateManager::getPersistentState(GRState& State) {
|
||||||
|
|
||||||
llvm::FoldingSetNodeID ID;
|
llvm::FoldingSetNodeID ID;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue