Add static analyzer support for conditionally executing static initializers.
llvm-svn: 178318
This commit is contained in:
parent
233c1b0c77
commit
338c3aa8d1
|
@ -96,6 +96,10 @@ private:
|
||||||
void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B,
|
void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B,
|
||||||
ExplodedNode *Pred);
|
ExplodedNode *Pred);
|
||||||
|
|
||||||
|
/// Handle conditional logic for running static initializers.
|
||||||
|
void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
|
||||||
|
ExplodedNode *Pred);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CoreEngine(const CoreEngine &) LLVM_DELETED_FUNCTION;
|
CoreEngine(const CoreEngine &) LLVM_DELETED_FUNCTION;
|
||||||
void operator=(const CoreEngine &) LLVM_DELETED_FUNCTION;
|
void operator=(const CoreEngine &) LLVM_DELETED_FUNCTION;
|
||||||
|
|
|
@ -224,6 +224,15 @@ public:
|
||||||
const CFGBlock *DstT,
|
const CFGBlock *DstT,
|
||||||
const CFGBlock *DstF);
|
const CFGBlock *DstF);
|
||||||
|
|
||||||
|
/// Called by CoreEngine. Used to processing branching behavior
|
||||||
|
/// at static initalizers.
|
||||||
|
void processStaticInitializer(const DeclStmt *DS,
|
||||||
|
NodeBuilderContext& BuilderCtx,
|
||||||
|
ExplodedNode *Pred,
|
||||||
|
ExplodedNodeSet &Dst,
|
||||||
|
const CFGBlock *DstT,
|
||||||
|
const CFGBlock *DstF);
|
||||||
|
|
||||||
/// processIndirectGoto - Called by CoreEngine. Used to generate successor
|
/// processIndirectGoto - Called by CoreEngine. Used to generate successor
|
||||||
/// nodes by processing the 'effects' of a computed goto jump.
|
/// nodes by processing the 'effects' of a computed goto jump.
|
||||||
void processIndirectGoto(IndirectGotoNodeBuilder& builder);
|
void processIndirectGoto(IndirectGotoNodeBuilder& builder);
|
||||||
|
|
|
@ -72,6 +72,15 @@ public:
|
||||||
const CFGBlock *DstT,
|
const CFGBlock *DstT,
|
||||||
const CFGBlock *DstF) = 0;
|
const CFGBlock *DstF) = 0;
|
||||||
|
|
||||||
|
/// Called by CoreEngine. Used to processing branching behavior
|
||||||
|
/// at static initalizers.
|
||||||
|
virtual void processStaticInitializer(const DeclStmt *DS,
|
||||||
|
NodeBuilderContext& BuilderCtx,
|
||||||
|
ExplodedNode *Pred,
|
||||||
|
ExplodedNodeSet &Dst,
|
||||||
|
const CFGBlock *DstT,
|
||||||
|
const CFGBlock *DstF) = 0;
|
||||||
|
|
||||||
/// Called by CoreEngine. Used to generate successor
|
/// Called by CoreEngine. Used to generate successor
|
||||||
/// nodes by processing the 'effects' of a computed goto jump.
|
/// nodes by processing the 'effects' of a computed goto jump.
|
||||||
virtual void processIndirectGoto(IndirectGotoNodeBuilder& builder) = 0;
|
virtual void processIndirectGoto(IndirectGotoNodeBuilder& builder) = 0;
|
||||||
|
|
|
@ -1665,6 +1665,7 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
|
||||||
// whether or not they are initialized.
|
// whether or not they are initialized.
|
||||||
if (Block) {
|
if (Block) {
|
||||||
Succ = Block;
|
Succ = Block;
|
||||||
|
Block = 0;
|
||||||
if (badCFG)
|
if (badCFG)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1714,15 +1715,14 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
|
||||||
if (ScopePos && VD == *ScopePos)
|
if (ScopePos && VD == *ScopePos)
|
||||||
++ScopePos;
|
++ScopePos;
|
||||||
|
|
||||||
CFGBlock *B = Block ? Block : LastBlock;
|
CFGBlock *B = LastBlock;
|
||||||
if (blockBeforeInit) {
|
if (blockBeforeInit) {
|
||||||
Succ = B;
|
Succ = B;
|
||||||
Block = 0;
|
Block = createBlock(false);
|
||||||
CFGBlock *branchBlock = createBlock(false);
|
Block->setTerminator(DS);
|
||||||
branchBlock->setTerminator(DS);
|
addSuccessor(Block, blockBeforeInit);
|
||||||
addSuccessor(branchBlock, blockBeforeInit);
|
addSuccessor(Block, B);
|
||||||
addSuccessor(branchBlock, B);
|
B = Block;
|
||||||
B = branchBlock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return B;
|
return B;
|
||||||
|
|
|
@ -238,6 +238,6 @@ bool AnalyzerOptions::shouldPrunePaths() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
|
bool AnalyzerOptions::shouldConditionalizeStaticInitializers() {
|
||||||
return getBooleanOption("conditional-static-initializers", false);
|
return getBooleanOption("cfg-conditional-static-initializers", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -346,6 +346,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
|
||||||
default:
|
default:
|
||||||
llvm_unreachable("Analysis for this terminator not implemented.");
|
llvm_unreachable("Analysis for this terminator not implemented.");
|
||||||
|
|
||||||
|
// Model static initializers.
|
||||||
|
case Stmt::DeclStmtClass:
|
||||||
|
HandleStaticInit(cast<DeclStmt>(Term), B, Pred);
|
||||||
|
return;
|
||||||
|
|
||||||
case Stmt::BinaryOperatorClass: // '&&' and '||'
|
case Stmt::BinaryOperatorClass: // '&&' and '||'
|
||||||
HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred);
|
HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred);
|
||||||
return;
|
return;
|
||||||
|
@ -456,6 +461,19 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
|
||||||
enqueue(Dst);
|
enqueue(Dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
|
||||||
|
ExplodedNode *Pred) {
|
||||||
|
assert(B->succ_size() == 2);
|
||||||
|
NodeBuilderContext Ctx(*this, B, Pred);
|
||||||
|
ExplodedNodeSet Dst;
|
||||||
|
SubEng.processStaticInitializer(DS, Ctx, Pred, Dst,
|
||||||
|
*(B->succ_begin()), *(B->succ_begin()+1));
|
||||||
|
// Enqueue the new frontier onto the worklist.
|
||||||
|
enqueue(Dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
|
void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
|
||||||
ExplodedNode *Pred) {
|
ExplodedNode *Pred) {
|
||||||
assert(B);
|
assert(B);
|
||||||
|
|
|
@ -1344,6 +1344,34 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
|
||||||
currBldrCtx = 0;
|
currBldrCtx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The GDM component containing the set of global variables which have been
|
||||||
|
/// previously initialized with explicit initializers.
|
||||||
|
REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet,
|
||||||
|
llvm::ImmutableSet<const VarDecl *>)
|
||||||
|
|
||||||
|
void ExprEngine::processStaticInitializer(const DeclStmt *DS,
|
||||||
|
NodeBuilderContext &BuilderCtx,
|
||||||
|
ExplodedNode *Pred,
|
||||||
|
clang::ento::ExplodedNodeSet &Dst,
|
||||||
|
const CFGBlock *DstT,
|
||||||
|
const CFGBlock *DstF) {
|
||||||
|
currBldrCtx = &BuilderCtx;
|
||||||
|
|
||||||
|
const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
|
||||||
|
ProgramStateRef state = Pred->getState();
|
||||||
|
bool initHasRun = state->contains<InitializedGlobalsSet>(VD);
|
||||||
|
BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF);
|
||||||
|
|
||||||
|
if (!initHasRun) {
|
||||||
|
state = state->add<InitializedGlobalsSet>(VD);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.generateNode(state, initHasRun, Pred);
|
||||||
|
builder.markInfeasible(!initHasRun);
|
||||||
|
|
||||||
|
currBldrCtx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// processIndirectGoto - Called by CoreEngine. Used to generate successor
|
/// processIndirectGoto - Called by CoreEngine. Used to generate successor
|
||||||
/// nodes by processing the 'effects' of a computed goto jump.
|
/// nodes by processing the 'effects' of a computed goto jump.
|
||||||
void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
|
void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
|
||||||
|
|
|
@ -423,11 +423,6 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
|
||||||
B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV));
|
B.generateNode(CL, Pred, state->BindExpr(CL, LC, ILV));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The GDM component containing the set of global variables which have been
|
|
||||||
/// previously initialized with explicit initializers.
|
|
||||||
REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet,
|
|
||||||
llvm::ImmutableSet<const VarDecl *> )
|
|
||||||
|
|
||||||
void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
|
void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
|
||||||
ExplodedNodeSet &Dst) {
|
ExplodedNodeSet &Dst) {
|
||||||
// Assumption: The CFG has one DeclStmt per Decl.
|
// Assumption: The CFG has one DeclStmt per Decl.
|
||||||
|
@ -439,15 +434,6 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a value has been previously initialized. There will be an entry in
|
|
||||||
// the set for variables with global storage which have been previously
|
|
||||||
// initialized.
|
|
||||||
if (VD->hasGlobalStorage())
|
|
||||||
if (Pred->getState()->contains<InitializedGlobalsSet>(VD)) {
|
|
||||||
Dst.insert(Pred);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: all pre/post visits should eventually be handled by ::Visit().
|
// FIXME: all pre/post visits should eventually be handled by ::Visit().
|
||||||
ExplodedNodeSet dstPreVisit;
|
ExplodedNodeSet dstPreVisit;
|
||||||
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
|
getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
|
||||||
|
@ -464,11 +450,6 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
|
||||||
|
|
||||||
// Note in the state that the initialization has occurred.
|
// Note in the state that the initialization has occurred.
|
||||||
ExplodedNode *UpdatedN = N;
|
ExplodedNode *UpdatedN = N;
|
||||||
if (VD->hasGlobalStorage()) {
|
|
||||||
state = state->add<InitializedGlobalsSet>(VD);
|
|
||||||
UpdatedN = B.generateNode(DS, N, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
SVal InitVal = state->getSVal(InitEx, LC);
|
SVal InitVal = state->getSVal(InitEx, LC);
|
||||||
|
|
||||||
if (isa<CXXConstructExpr>(InitEx->IgnoreImplicit())) {
|
if (isa<CXXConstructExpr>(InitEx->IgnoreImplicit())) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ void bar() {}
|
||||||
void foo() { bar(); }
|
void foo() { bar(); }
|
||||||
|
|
||||||
// CHECK: [config]
|
// CHECK: [config]
|
||||||
|
// CHECK-NEXT: cfg-conditional-static-initializers = true
|
||||||
// CHECK-NEXT: cfg-temporary-dtors = false
|
// CHECK-NEXT: cfg-temporary-dtors = false
|
||||||
// CHECK-NEXT: faux-bodies = true
|
// CHECK-NEXT: faux-bodies = true
|
||||||
// CHECK-NEXT: graph-trim-interval = 1000
|
// CHECK-NEXT: graph-trim-interval = 1000
|
||||||
|
@ -15,4 +16,5 @@ void foo() { bar(); }
|
||||||
// CHECK-NEXT: max-times-inline-large = 32
|
// CHECK-NEXT: max-times-inline-large = 32
|
||||||
// CHECK-NEXT: mode = deep
|
// CHECK-NEXT: mode = deep
|
||||||
// CHECK-NEXT: [stats]
|
// CHECK-NEXT: [stats]
|
||||||
// CHECK-NEXT: num-entries = 9
|
// CHECK-NEXT: num-entries = 10
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ public:
|
||||||
// CHECK-NEXT: c++-inlining = constructors
|
// CHECK-NEXT: c++-inlining = constructors
|
||||||
// CHECK-NEXT: c++-stdlib-inlining = true
|
// CHECK-NEXT: c++-stdlib-inlining = true
|
||||||
// CHECK-NEXT: c++-template-inlining = true
|
// CHECK-NEXT: c++-template-inlining = true
|
||||||
|
// CHECK-NEXT: cfg-conditional-static-initializers = true
|
||||||
// CHECK-NEXT: cfg-temporary-dtors = false
|
// CHECK-NEXT: cfg-temporary-dtors = false
|
||||||
// CHECK-NEXT: faux-bodies = true
|
// CHECK-NEXT: faux-bodies = true
|
||||||
// CHECK-NEXT: graph-trim-interval = 1000
|
// CHECK-NEXT: graph-trim-interval = 1000
|
||||||
|
@ -24,4 +25,4 @@ public:
|
||||||
// CHECK-NEXT: max-times-inline-large = 32
|
// CHECK-NEXT: max-times-inline-large = 32
|
||||||
// CHECK-NEXT: mode = deep
|
// CHECK-NEXT: mode = deep
|
||||||
// CHECK-NEXT: [stats]
|
// CHECK-NEXT: [stats]
|
||||||
// CHECK-NEXT: num-entries = 12
|
// CHECK-NEXT: num-entries = 13
|
||||||
|
|
Loading…
Reference in New Issue