Add CFG support for the start and end of scopes and infrastructure for
implicit destructor calls. WIP.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@93922 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Analysis/CFG.h b/include/clang/Analysis/CFG.h
index 38d4bdf..d85330b 100644
--- a/include/clang/Analysis/CFG.h
+++ b/include/clang/Analysis/CFG.h
@@ -20,6 +20,7 @@
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Basic/SourceLocation.h"
#include <cassert>
namespace llvm {
@@ -33,15 +34,39 @@
class LangOptions;
class ASTContext;
+namespace {
+// An element of the CFG for implicit descructor calls implied by the language
+// rules.
+class Dtor {
+ // Statement that introduces the variable.
+ Stmt *S;
+ // A token which ends the scope, return, goto, throw, }.
+ SourceLocation Loc;
+public:
+ Dtor(Stmt *s, SourceLocation l) : S(s), Loc(l) {
+ }
+ SourceLocation getLoc() { return Loc; }
+ Stmt *getStmt() { return S; }
+};
+}
+
/// CFGElement - Represents a top-level expression in a basic block.
class CFGElement {
- llvm::PointerIntPair<Stmt *, 1> Data;
+ llvm::PointerIntPair<Stmt *, 2> Data;
public:
+ enum Type { StartScope, EndScope };
explicit CFGElement() {}
CFGElement(Stmt *S, bool lvalue) : Data(S, lvalue ? 1 : 0) {}
+ CFGElement(Stmt *S, Type t) : Data(S, t == StartScope ? 2 : 3) {}
+ // CFGElement(Dtor *S, Type t) : Data(reinterpret_cast<Stmt*>(S), 4) {}
Stmt *getStmt() const { return Data.getPointer(); }
bool asLValue() const { return Data.getInt() == 1; }
+ bool asStartScope() const { return Data.getInt() == 2; }
+ bool asEndScope() const { return Data.getInt() == 3; }
+ bool asDtor() const { return Data.getInt() == 4; }
operator Stmt*() const { return getStmt(); }
+ operator bool() const { return getStmt() != 0; }
+ operator Dtor*() const { return reinterpret_cast<Dtor*>(getStmt()); }
};
/// CFGBlock - Represents a single basic block in a source-level CFG.
@@ -236,6 +261,12 @@
void appendStmt(Stmt* Statement, BumpVectorContext &C, bool asLValue) {
Stmts.push_back(CFGElement(Statement, asLValue), C);
}
+ void StartScope(Stmt* S, BumpVectorContext &C) {
+ Stmts.push_back(CFGElement(S, CFGElement::StartScope), C);
+ }
+ void EndScope(Stmt* S, BumpVectorContext &C) {
+ Stmts.push_back(CFGElement(S, CFGElement::EndScope), C);
+ }
};
@@ -254,7 +285,7 @@
/// buildCFG - Builds a CFG from an AST. The responsibility to free the
/// constructed CFG belongs to the caller.
- static CFG* buildCFG(Stmt* AST, ASTContext *C);
+ static CFG* buildCFG(Stmt* AST, ASTContext *C, bool AddScopes = false);
/// createBlock - Create a new block in the CFG. The CFG owns the block;
/// the caller should not directly free it.
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp
index 21c2418..6508c35 100644
--- a/lib/Analysis/CFG.cpp
+++ b/lib/Analysis/CFG.cpp
@@ -93,7 +93,7 @@
TryTerminatedBlock(NULL) {}
// buildCFG - Used by external clients to construct the CFG.
- CFG* buildCFG(Stmt *Statement, ASTContext *C);
+ CFG* buildCFG(Stmt *Statement, ASTContext *C, bool AddScopes);
private:
// Visitors to walk an AST and construct the CFG.
@@ -141,6 +141,25 @@
return Block;
}
+ CFGBlock *StartScope(Stmt *S, CFGBlock *B) {
+ if (!AddScopes)
+ return B;
+
+ if (B == 0)
+ B = createBlock();
+ B->StartScope(S, cfg->getBumpVectorContext());
+ return B;
+ }
+
+ void EndScope(Stmt *S) {
+ if (!AddScopes)
+ return;
+
+ if (Block == 0)
+ Block = createBlock();
+ Block->EndScope(S, cfg->getBumpVectorContext());
+ }
+
void autoCreateBlock() { if (!Block) Block = createBlock(); }
CFGBlock *createBlock(bool add_successor = true);
bool FinishBlock(CFGBlock* B);
@@ -188,6 +207,7 @@
}
bool badCFG;
+ bool AddScopes;
};
// FIXME: Add support for dependent-sized array types in C++?
@@ -209,12 +229,13 @@
/// body (compound statement). The ownership of the returned CFG is
/// transferred to the caller. If CFG construction fails, this method returns
/// NULL.
-CFG* CFGBuilder::buildCFG(Stmt* Statement, ASTContext* C) {
+CFG* CFGBuilder::buildCFG(Stmt* Statement, ASTContext* C, bool AddScopes) {
Context = C;
assert(cfg.get());
if (!Statement)
return NULL;
+ this->AddScopes = AddScopes;
badCFG = false;
// Create an empty block that will serve as the exit block for the CFG. Since
@@ -519,22 +540,43 @@
NoReturn = true;
}
- if (FunctionDecl *FD = C->getDirectCallee())
+ bool CanThrow = false;
+
+ // Languages without exceptions are assumed to not throw.
+ if (Context->getLangOptions().Exceptions) {
+ CanThrow = true;
+ }
+
+ if (FunctionDecl *FD = C->getDirectCallee()) {
if (FD->hasAttr<NoReturnAttr>())
NoReturn = true;
+ if (FD->hasAttr<NoThrowAttr>())
+ CanThrow = false;
+ }
- if (!NoReturn)
+ if (!NoReturn && !CanThrow)
return VisitStmt(C, asc);
- if (Block && !FinishBlock(Block))
- return 0;
+ if (Block) {
+ Succ = Block;
+ if (!FinishBlock(Block))
+ return 0;
+ }
- // Create new block with no successor for the remaining pieces.
- Block = createBlock(false);
+ Block = createBlock(!NoReturn);
AppendStmt(Block, C, asc);
- // Wire this to the exit block directly.
- AddSuccessor(Block, &cfg->getExit());
+ if (NoReturn) {
+ // Wire this to the exit block directly.
+ AddSuccessor(Block, &cfg->getExit());
+ }
+ if (CanThrow) {
+ // Add exceptional edges.
+ if (TryTerminatedBlock)
+ AddSuccessor(Block, TryTerminatedBlock);
+ else
+ AddSuccessor(Block, &cfg->getExit());
+ }
return VisitChildren(C);
}
@@ -569,6 +611,8 @@
CFGBlock* CFGBuilder::VisitCompoundStmt(CompoundStmt* C) {
+ EndScope(C);
+
CFGBlock* LastBlock = Block;
for (CompoundStmt::reverse_body_iterator I=C->body_rbegin(), E=C->body_rend();
@@ -578,6 +622,9 @@
if (badCFG)
return NULL;
}
+
+ LastBlock = StartScope(C, LastBlock);
+
return LastBlock;
}
@@ -1648,9 +1695,9 @@
/// buildCFG - Constructs a CFG from an AST. Ownership of the returned
/// CFG is returned to the caller.
-CFG* CFG::buildCFG(Stmt* Statement, ASTContext *C) {
+CFG* CFG::buildCFG(Stmt* Statement, ASTContext *C, bool AddScopes) {
CFGBuilder Builder;
- return Builder.buildCFG(Statement, C);
+ return Builder.buildCFG(Statement, C, AddScopes);
}
//===----------------------------------------------------------------------===//
@@ -1909,7 +1956,18 @@
static void print_stmt(llvm::raw_ostream &OS, StmtPrinterHelper* Helper,
- Stmt* Terminator) {
+ const CFGElement &E) {
+ Stmt *Terminator = E;
+
+ if (E.asStartScope()) {
+ OS << "start scope\n";
+ return;
+ }
+ if (E.asEndScope()) {
+ OS << "end scope\n";
+ return;
+ }
+
if (Helper) {
// special printing for statement-expressions.
if (StmtExpr* SE = dyn_cast<StmtExpr>(Terminator)) {
@@ -1959,14 +2017,14 @@
OS << " ]\n";
// Print the label of this block.
- if (Stmt* Terminator = const_cast<Stmt*>(B.getLabel())) {
+ if (Stmt* Label = const_cast<Stmt*>(B.getLabel())) {
if (print_edges)
OS << " ";
- if (LabelStmt* L = dyn_cast<LabelStmt>(Terminator))
+ if (LabelStmt* L = dyn_cast<LabelStmt>(Label))
OS << L->getName();
- else if (CaseStmt* C = dyn_cast<CaseStmt>(Terminator)) {
+ else if (CaseStmt* C = dyn_cast<CaseStmt>(Label)) {
OS << "case ";
C->getLHS()->printPretty(OS, Helper,
PrintingPolicy(Helper->getLangOpts()));
@@ -1975,9 +2033,9 @@
C->getRHS()->printPretty(OS, Helper,
PrintingPolicy(Helper->getLangOpts()));
}
- } else if (isa<DefaultStmt>(Terminator))
+ } else if (isa<DefaultStmt>(Label))
OS << "default";
- else if (CXXCatchStmt *CS = dyn_cast<CXXCatchStmt>(Terminator)) {
+ else if (CXXCatchStmt *CS = dyn_cast<CXXCatchStmt>(Label)) {
OS << "catch (";
CS->getExceptionDecl()->print(OS, PrintingPolicy(Helper->getLangOpts()),
0);