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.

llvm-svn: 94074
This commit is contained in:
Mike Stump 2010-01-21 15:20:48 +00:00
parent d2eba45be3
commit 04c6851cd6
7 changed files with 121 additions and 16 deletions

View File

@ -133,7 +133,7 @@ class CFGBlock {
/// 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 @@ public:
/// 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;

View File

@ -46,14 +46,21 @@ class AnalysisContext {
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();

View File

@ -55,7 +55,7 @@ const ImplicitParamDecl *AnalysisContext::getSelfDecl() const {
CFG *AnalysisContext::getCFG() {
if (!cfg)
cfg = CFG::buildCFG(D, getBody(), &D->getASTContext());
cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), AddEHEdges);
return cfg;
}

View File

@ -94,7 +94,8 @@ public:
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 @@ private:
}
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 @@ static VariableArrayType* FindVA(Type* t) {
/// 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 @@ CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) {
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 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
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 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
// 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 @@ CFGBlock* CFG::createBlock() {
/// 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);
}
//===----------------------------------------------------------------------===//

View File

@ -2091,6 +2091,9 @@ static SourceLocation GetUnreachableLoc(CFGBlock &b) {
}
return b[1].getStmt()->getLocStart();
}
case Stmt::CXXTryStmtClass: {
return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc();
}
default: ;
}
return S->getLocStart();
@ -2167,12 +2170,20 @@ void Sema::CheckUnreachable(AnalysisContext &AC) {
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 @@ Sema::ControlFlowKind Sema::CheckFallThrough(AnalysisContext &AC) {
// 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 @@ Sema::ControlFlowKind Sema::CheckFallThrough(AnalysisContext &AC) {
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;

View File

@ -4102,7 +4102,9 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,
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)

View File

@ -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}}
}