Speed up compilation by avoiding generating exceptional edges from
CallExprs as those edges help cause a n^2 explosion in the number of
destructor calls. Other consumers, such as static analysis, that
would like to have more a more complete CFG can select the inclusion
of those edges as CFG build time.
This also fixes up the two compilation users of CFGs to be tolerant of
having or not having those edges. All catch code is assumed be to
live if we didn't generate the exceptional edges for CallExprs.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94074 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h
index fcfa6b7..e87784f 100644
--- a/include/clang/Analysis/CFG.h
+++ b/include/clang/Analysis/CFG.h
@@ -133,7 +133,7 @@
/// Label - An (optional) label that prefixes the executable
/// statements in the block. When this variable is non-NULL, it is
- /// either an instance of LabelStmt or SwitchCase.
+ /// either an instance of LabelStmt, SwitchCase or CXXCatchStmt.
Stmt *Label;
/// Terminator - The terminator for a basic block that
@@ -287,6 +287,7 @@
/// buildCFG - Builds a CFG from an AST. The responsibility to free the
/// constructed CFG belongs to the caller.
static CFG* buildCFG(const Decl *D, Stmt* AST, ASTContext *C,
+ bool AddEHEdges = false,
bool AddScopes = false);
/// createBlock - Create a new block in the CFG. The CFG owns the block;
diff --git a/include/clang/Analysis/PathSensitive/AnalysisContext.h b/include/clang/Analysis/PathSensitive/AnalysisContext.h
index 63ba558..c82bb96 100644
--- a/include/clang/Analysis/PathSensitive/AnalysisContext.h
+++ b/include/clang/Analysis/PathSensitive/AnalysisContext.h
@@ -46,14 +46,21 @@
ParentMap *PM;
llvm::DenseMap<const BlockDecl*,void*> *ReferencedBlockVars;
llvm::BumpPtrAllocator A;
+ bool AddEHEdges;
public:
- AnalysisContext(const Decl *d) : D(d), cfg(0), liveness(0), PM(0),
- ReferencedBlockVars(0) {}
+ AnalysisContext(const Decl *d, bool addehedges = false)
+ : D(d), cfg(0), liveness(0), PM(0), ReferencedBlockVars(0),
+ AddEHEdges(addehedges) {}
~AnalysisContext();
ASTContext &getASTContext() { return D->getASTContext(); }
const Decl *getDecl() { return D; }
+ /// getAddEHEdges - Return true iff we are adding exceptional edges from
+ /// callExprs. If this is false, then try/catch statements and blocks
+ /// reachable from them can appear to be dead in the CFG, analysis passes must
+ /// cope with that.
+ bool getAddEHEdges() const { return AddEHEdges; }
Stmt *getBody();
CFG *getCFG();
ParentMap &getParentMap();
diff --git a/lib/Analysis/AnalysisContext.cpp b/lib/Analysis/AnalysisContext.cpp
index 1d5b4a1..ad9f6dd1 100644
--- a/lib/Analysis/AnalysisContext.cpp
+++ b/lib/Analysis/AnalysisContext.cpp
@@ -55,7 +55,7 @@
CFG *AnalysisContext::getCFG() {
if (!cfg)
- cfg = CFG::buildCFG(D, getBody(), &D->getASTContext());
+ cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), AddEHEdges);
return cfg;
}
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 57053b1..ef3cdd8 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -94,7 +94,8 @@
TryTerminatedBlock(NULL) {}
// buildCFG - Used by external clients to construct the CFG.
- CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C, bool AddScopes);
+ CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C, bool AddEHEdges,
+ bool AddScopes);
private:
// Visitors to walk an AST and construct the CFG.
@@ -208,6 +209,11 @@
}
bool badCFG;
+
+ // True iff EH edges on CallExprs should be added to the CFG.
+ bool AddEHEdges;
+
+ // True iff scope start and scope end notes should be added to the CFG.
bool AddScopes;
};
@@ -231,7 +237,7 @@
/// transferred to the caller. If CFG construction fails, this method returns
/// NULL.
CFG* CFGBuilder::buildCFG(const Decl *D, Stmt* Statement, ASTContext* C,
- bool AddScopes) {
+ bool AddEHEdges, bool AddScopes) {
Context = C;
assert(cfg.get());
if (!Statement)
@@ -540,6 +546,22 @@
return Block;
}
+static bool CanThrow(Expr *E) {
+ QualType Ty = E->getType();
+ if (Ty->isFunctionPointerType())
+ Ty = Ty->getAs<PointerType>()->getPointeeType();
+ else if (Ty->isBlockPointerType())
+ Ty = Ty->getAs<BlockPointerType>()->getPointeeType();
+
+ const FunctionType *FT = Ty->getAs<FunctionType>();
+ if (FT) {
+ if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT))
+ if (Proto->hasEmptyExceptionSpec())
+ return false;
+ }
+ return true;
+}
+
CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
// If this is a call to a no-return function, this stops the block here.
bool NoReturn = false;
@@ -547,21 +569,25 @@
NoReturn = true;
}
- bool CanThrow = false;
+ bool AddEHEdge = false;
// Languages without exceptions are assumed to not throw.
if (Context->getLangOptions().Exceptions) {
- CanThrow = true;
+ if (AddEHEdges)
+ AddEHEdge = true;
}
if (FunctionDecl *FD = C->getDirectCallee()) {
if (FD->hasAttr<NoReturnAttr>())
NoReturn = true;
if (FD->hasAttr<NoThrowAttr>())
- CanThrow = false;
+ AddEHEdge = false;
}
- if (!NoReturn && !CanThrow)
+ if (!CanThrow(C->getCallee()))
+ AddEHEdge = false;
+
+ if (!NoReturn && !AddEHEdge)
return VisitStmt(C, asc);
if (Block) {
@@ -577,7 +603,7 @@
// Wire this to the exit block directly.
AddSuccessor(Block, &cfg->getExit());
}
- if (CanThrow) {
+ if (AddEHEdge) {
// Add exceptional edges.
if (TryTerminatedBlock)
AddSuccessor(Block, TryTerminatedBlock);
@@ -1714,9 +1740,9 @@
/// buildCFG - Constructs a CFG from an AST. Ownership of the returned
/// CFG is returned to the caller.
CFG* CFG::buildCFG(const Decl *D, Stmt* Statement, ASTContext *C,
- bool AddScopes) {
+ bool AddEHEdges, bool AddScopes) {
CFGBuilder Builder;
- return Builder.buildCFG(D, Statement, C, AddScopes);
+ return Builder.buildCFG(D, Statement, C, AddEHEdges, AddScopes);
}
//===----------------------------------------------------------------------===//
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index e61110f..9a6f950 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -2091,6 +2091,9 @@
}
return b[1].getStmt()->getLocStart();
}
+ case Stmt::CXXTryStmtClass: {
+ return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc();
+ }
default: ;
}
return S->getLocStart();
@@ -2167,12 +2170,20 @@
return;
llvm::SmallVector<SourceLocation, 24> lines;
+ bool AddEHEdges = AC.getAddEHEdges();
// First, give warnings for blocks with no predecessors, as they
// can't be part of a loop.
for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) {
CFGBlock &b = **I;
if (!live[b.getBlockID()]) {
if (b.pred_begin() == b.pred_end()) {
+ if (!AddEHEdges && b.getTerminator()
+ && isa<CXXTryStmt>(b.getTerminator())) {
+ // When not adding EH edges from calls, catch clauses
+ // can otherwise seem dead. Avoid noting them as dead.
+ count += MarkLive(&b, live);
+ continue;
+ }
SourceLocation c = GetUnreachableLoc(b);
if (!c.isValid()) {
// Blocks without a location can't produce a warning, so don't mark
@@ -2222,11 +2233,29 @@
// FIXME: This should be NeverFallThrough
return NeverFallThroughOrReturn;
- // The CFG leaves in dead things, and we don't want to dead code paths to
+ // The CFG leaves in dead things, and we don't want the dead code paths to
// confuse us, so we mark all live things first.
std::queue<CFGBlock*> workq;
llvm::BitVector live(cfg->getNumBlockIDs());
- MarkLive(&cfg->getEntry(), live);
+ unsigned count = MarkLive(&cfg->getEntry(), live);
+
+ bool AddEHEdges = AC.getAddEHEdges();
+ if (!AddEHEdges && count != cfg->getNumBlockIDs())
+ // When there are things remaining dead, and we didn't add EH edges
+ // from CallExprs to the catch clauses, we have to go back and
+ // mark them as live.
+ for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) {
+ CFGBlock &b = **I;
+ if (!live[b.getBlockID()]) {
+ if (b.pred_begin() == b.pred_end()) {
+ if (b.getTerminator() && isa<CXXTryStmt>(b.getTerminator()))
+ // When not adding EH edges from calls, catch clauses
+ // can otherwise seem dead. Avoid noting them as dead.
+ count += MarkLive(&b, live);
+ continue;
+ }
+ }
+ }
// Now we know what is live, we check the live precessors of the exit block
// and look for fall through paths, being careful to ignore normal returns,
@@ -2243,6 +2272,11 @@
if (!live[B.getBlockID()])
continue;
if (B.size() == 0) {
+ if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) {
+ HasAbnormalEdge = true;
+ continue;
+ }
+
// A labeled empty statement, or the entry block...
HasPlainEdge = true;
continue;
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 1e1df06..b6b3b5b 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -4102,7 +4102,9 @@
Decl *dcl = D.getAs<Decl>();
Stmt *Body = BodyArg.takeAs<Stmt>();
- AnalysisContext AC(dcl);
+ // Don't generate EH edges for CallExprs as we'd like to avoid the n^2
+ // explosion for destrutors that can result and the compile time hit.
+ AnalysisContext AC(dcl, false);
FunctionDecl *FD = 0;
FunctionTemplateDecl *FunTmpl = dyn_cast_or_null<FunctionTemplateDecl>(dcl);
if (FunTmpl)
diff --git a/test/SemaCXX/warn-unreachable.cpp b/test/SemaCXX/warn-unreachable.cpp
new file mode 100644
index 0000000..13a82f4
--- /dev/null
+++ b/test/SemaCXX/warn-unreachable.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang %s -fsyntax-only -Xclang -verify -fblocks -Wunreachable-code -Wno-unused-value
+
+int live();
+int dead();
+int liveti() throw(int);
+int (*livetip)() throw(int);
+
+int test1() {
+ try {
+ live();
+ } catch (int i) {
+ live();
+ }
+ return 1;
+}
+
+void test2() {
+ try {
+ live();
+ } catch (int i) {
+ live();
+ }
+ try {
+ liveti();
+ } catch (int i) {
+ live();
+ }
+ try {
+ livetip();
+ } catch (int i) {
+ live();
+ }
+ throw 1;
+ dead(); // expected-warning {{will never be executed}}
+}