mirror of https://github.com/microsoft/clang.git
Implement CFG construction for __try / __except / __leave.
This makes -Wunreachable-code work for programs containing SEH (except for __finally, which is still missing for now). __try is modeled like try (but simpler since it can only have a single __except or __finally), __except is fairly similar to catch (but simpler, since it can't contain declarations). __leave is implemented similarly to break / continue. Use the existing addTryDispatchBlock infrastructure (which FindUnreachableCode() in ReachableCode.cpp uses via cfg->try_blocks_begin()) to mark things in the __except blocks as reachable. Re-use TryTerminatedBlock. This means we add EH edges from calls to the __try block, but not from all other statements. While this is incomplete, it matches LLVM's SEH codegen support. Also, in practice, BuildOpts.AddEHEdges is always false in practice from what I can tell, so we never even insert the call EH edges either. https://reviews.llvm.org/D36914 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@311561 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
d2a520ee9e
commit
7ed95d9236
|
@ -395,12 +395,16 @@ class CFGBuilder {
|
|||
ASTContext *Context;
|
||||
std::unique_ptr<CFG> cfg;
|
||||
|
||||
CFGBlock *Block;
|
||||
CFGBlock *Succ;
|
||||
CFGBlock *Block; // Current block.
|
||||
CFGBlock *Succ; // Block after the current block.
|
||||
JumpTarget ContinueJumpTarget;
|
||||
JumpTarget BreakJumpTarget;
|
||||
JumpTarget SEHLeaveJumpTarget;
|
||||
CFGBlock *SwitchTerminatedBlock;
|
||||
CFGBlock *DefaultCaseBlock;
|
||||
|
||||
// This can point either to a try or a __try block. The frontend forbids
|
||||
// mixing both kinds in one function, so having one for both is enough.
|
||||
CFGBlock *TryTerminatedBlock;
|
||||
|
||||
// Current position in local scope.
|
||||
|
@ -436,13 +440,12 @@ class CFGBuilder {
|
|||
|
||||
public:
|
||||
explicit CFGBuilder(ASTContext *astContext,
|
||||
const CFG::BuildOptions &buildOpts)
|
||||
: Context(astContext), cfg(new CFG()), // crew a new CFG
|
||||
Block(nullptr), Succ(nullptr),
|
||||
SwitchTerminatedBlock(nullptr), DefaultCaseBlock(nullptr),
|
||||
TryTerminatedBlock(nullptr), badCFG(false), BuildOpts(buildOpts),
|
||||
switchExclusivelyCovered(false), switchCond(nullptr),
|
||||
cachedEntry(nullptr), lastLookup(nullptr) {}
|
||||
const CFG::BuildOptions &buildOpts)
|
||||
: Context(astContext), cfg(new CFG()), // crew a new CFG
|
||||
Block(nullptr), Succ(nullptr), SwitchTerminatedBlock(nullptr),
|
||||
DefaultCaseBlock(nullptr), TryTerminatedBlock(nullptr), badCFG(false),
|
||||
BuildOpts(buildOpts), switchExclusivelyCovered(false),
|
||||
switchCond(nullptr), cachedEntry(nullptr), lastLookup(nullptr) {}
|
||||
|
||||
// buildCFG - Used by external clients to construct the CFG.
|
||||
std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement);
|
||||
|
@ -501,6 +504,10 @@ private:
|
|||
CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
|
||||
CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E);
|
||||
CFGBlock *VisitReturnStmt(ReturnStmt *R);
|
||||
CFGBlock *VisitSEHExceptStmt(SEHExceptStmt *S);
|
||||
CFGBlock *VisitSEHFinallyStmt(SEHFinallyStmt *S);
|
||||
CFGBlock *VisitSEHLeaveStmt(SEHLeaveStmt *S);
|
||||
CFGBlock *VisitSEHTryStmt(SEHTryStmt *S);
|
||||
CFGBlock *VisitStmtExpr(StmtExpr *S, AddStmtChoice asc);
|
||||
CFGBlock *VisitSwitchStmt(SwitchStmt *S);
|
||||
CFGBlock *VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E,
|
||||
|
@ -1731,6 +1738,18 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) {
|
|||
case Stmt::ReturnStmtClass:
|
||||
return VisitReturnStmt(cast<ReturnStmt>(S));
|
||||
|
||||
case Stmt::SEHExceptStmtClass:
|
||||
return VisitSEHExceptStmt(cast<SEHExceptStmt>(S));
|
||||
|
||||
case Stmt::SEHFinallyStmtClass:
|
||||
return VisitSEHFinallyStmt(cast<SEHFinallyStmt>(S));
|
||||
|
||||
case Stmt::SEHLeaveStmtClass:
|
||||
return VisitSEHLeaveStmt(cast<SEHLeaveStmt>(S));
|
||||
|
||||
case Stmt::SEHTryStmtClass:
|
||||
return VisitSEHTryStmt(cast<SEHTryStmt>(S));
|
||||
|
||||
case Stmt::UnaryExprOrTypeTraitExprClass:
|
||||
return VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S),
|
||||
asc);
|
||||
|
@ -2462,6 +2481,117 @@ CFGBlock *CFGBuilder::VisitReturnStmt(ReturnStmt *R) {
|
|||
return VisitStmt(R, AddStmtChoice::AlwaysAdd);
|
||||
}
|
||||
|
||||
CFGBlock *CFGBuilder::VisitSEHExceptStmt(SEHExceptStmt *ES) {
|
||||
// SEHExceptStmt are treated like labels, so they are the first statement in a
|
||||
// block.
|
||||
|
||||
// Save local scope position because in case of exception variable ScopePos
|
||||
// won't be restored when traversing AST.
|
||||
SaveAndRestore<LocalScope::const_iterator> save_scope_pos(ScopePos);
|
||||
|
||||
addStmt(ES->getBlock());
|
||||
CFGBlock *SEHExceptBlock = Block;
|
||||
if (!SEHExceptBlock)
|
||||
SEHExceptBlock = createBlock();
|
||||
|
||||
appendStmt(SEHExceptBlock, ES);
|
||||
|
||||
// Also add the SEHExceptBlock as a label, like with regular labels.
|
||||
SEHExceptBlock->setLabel(ES);
|
||||
|
||||
// Bail out if the CFG is bad.
|
||||
if (badCFG)
|
||||
return nullptr;
|
||||
|
||||
// We set Block to NULL to allow lazy creation of a new block (if necessary).
|
||||
Block = nullptr;
|
||||
|
||||
return SEHExceptBlock;
|
||||
}
|
||||
|
||||
CFGBlock *CFGBuilder::VisitSEHFinallyStmt(SEHFinallyStmt *FS) {
|
||||
return VisitCompoundStmt(FS->getBlock());
|
||||
}
|
||||
|
||||
CFGBlock *CFGBuilder::VisitSEHLeaveStmt(SEHLeaveStmt *LS) {
|
||||
// "__leave" is a control-flow statement. Thus we stop processing the current
|
||||
// block.
|
||||
if (badCFG)
|
||||
return nullptr;
|
||||
|
||||
// Now create a new block that ends with the __leave statement.
|
||||
Block = createBlock(false);
|
||||
Block->setTerminator(LS);
|
||||
|
||||
// If there is no target for the __leave, then we are looking at an incomplete
|
||||
// AST. This means that the CFG cannot be constructed.
|
||||
if (SEHLeaveJumpTarget.block) {
|
||||
addAutomaticObjHandling(ScopePos, SEHLeaveJumpTarget.scopePosition, LS);
|
||||
addSuccessor(Block, SEHLeaveJumpTarget.block);
|
||||
} else
|
||||
badCFG = true;
|
||||
|
||||
return Block;
|
||||
}
|
||||
|
||||
CFGBlock *CFGBuilder::VisitSEHTryStmt(SEHTryStmt *Terminator) {
|
||||
// "__try"/"__except"/"__finally" is a control-flow statement. Thus we stop
|
||||
// processing the current block.
|
||||
CFGBlock *SEHTrySuccessor = nullptr;
|
||||
|
||||
if (Block) {
|
||||
if (badCFG)
|
||||
return nullptr;
|
||||
SEHTrySuccessor = Block;
|
||||
} else SEHTrySuccessor = Succ;
|
||||
|
||||
// FIXME: Implement __finally support.
|
||||
if (Terminator->getFinallyHandler())
|
||||
return NYS();
|
||||
|
||||
CFGBlock *PrevSEHTryTerminatedBlock = TryTerminatedBlock;
|
||||
|
||||
// Create a new block that will contain the __try statement.
|
||||
CFGBlock *NewTryTerminatedBlock = createBlock(false);
|
||||
|
||||
// Add the terminator in the __try block.
|
||||
NewTryTerminatedBlock->setTerminator(Terminator);
|
||||
|
||||
if (SEHExceptStmt *Except = Terminator->getExceptHandler()) {
|
||||
// The code after the try is the implicit successor if there's an __except.
|
||||
Succ = SEHTrySuccessor;
|
||||
Block = nullptr;
|
||||
CFGBlock *ExceptBlock = VisitSEHExceptStmt(Except);
|
||||
if (!ExceptBlock)
|
||||
return nullptr;
|
||||
// Add this block to the list of successors for the block with the try
|
||||
// statement.
|
||||
addSuccessor(NewTryTerminatedBlock, ExceptBlock);
|
||||
}
|
||||
if (PrevSEHTryTerminatedBlock)
|
||||
addSuccessor(NewTryTerminatedBlock, PrevSEHTryTerminatedBlock);
|
||||
else
|
||||
addSuccessor(NewTryTerminatedBlock, &cfg->getExit());
|
||||
|
||||
// The code after the try is the implicit successor.
|
||||
Succ = SEHTrySuccessor;
|
||||
|
||||
// Save the current "__try" context.
|
||||
SaveAndRestore<CFGBlock *> save_try(TryTerminatedBlock,
|
||||
NewTryTerminatedBlock);
|
||||
cfg->addTryDispatchBlock(TryTerminatedBlock);
|
||||
|
||||
// Save the current value for the __leave target.
|
||||
// All __leaves should go to the code following the __try
|
||||
// (FIXME: or if the __try has a __finally, to the __finally.)
|
||||
SaveAndRestore<JumpTarget> save_break(SEHLeaveJumpTarget);
|
||||
SEHLeaveJumpTarget = JumpTarget(SEHTrySuccessor, ScopePos);
|
||||
|
||||
assert(Terminator->getTryBlock() && "__try must contain a non-NULL body");
|
||||
Block = nullptr;
|
||||
return addStmt(Terminator->getTryBlock());
|
||||
}
|
||||
|
||||
CFGBlock *CFGBuilder::VisitLabelStmt(LabelStmt *L) {
|
||||
// Get the block of the labeled statement. Add it to our map.
|
||||
addStmt(L->getSubStmt());
|
||||
|
@ -4323,6 +4453,10 @@ public:
|
|||
OS << "try ...";
|
||||
}
|
||||
|
||||
void VisitSEHTryStmt(SEHTryStmt *CS) {
|
||||
OS << "__try ...";
|
||||
}
|
||||
|
||||
void VisitAbstractConditionalOperator(AbstractConditionalOperator* C) {
|
||||
if (Stmt *Cond = C->getCond())
|
||||
Cond->printPretty(OS, Helper, Policy);
|
||||
|
@ -4555,7 +4689,11 @@ static void print_block(raw_ostream &OS, const CFG* cfg,
|
|||
else
|
||||
OS << "...";
|
||||
OS << ")";
|
||||
|
||||
} else if (SEHExceptStmt *ES = dyn_cast<SEHExceptStmt>(Label)) {
|
||||
OS << "__except (";
|
||||
ES->getFilterExpr()->printPretty(OS, &Helper,
|
||||
PrintingPolicy(Helper.getLangOpts()), 0);
|
||||
OS << ")";
|
||||
} else
|
||||
llvm_unreachable("Invalid label statement in CFGBlock.");
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
// RUN: %clang_cc1 %s -triple=i686-pc-win32 -fsyntax-only -verify -fms-extensions -Wunreachable-code
|
||||
|
||||
void f();
|
||||
|
||||
void g1() {
|
||||
__try {
|
||||
f();
|
||||
__leave;
|
||||
f(); // expected-warning{{will never be executed}}
|
||||
} __except(1) {
|
||||
f();
|
||||
}
|
||||
|
||||
// Completely empty.
|
||||
__try {
|
||||
} __except(1) {
|
||||
}
|
||||
|
||||
__try {
|
||||
f();
|
||||
return;
|
||||
} __except(1) { // Filter expression should not be marked as unreachable.
|
||||
// Empty __except body.
|
||||
}
|
||||
}
|
||||
|
||||
void g2() {
|
||||
__try {
|
||||
// Nested __try.
|
||||
__try {
|
||||
f();
|
||||
__leave;
|
||||
f(); // expected-warning{{will never be executed}}
|
||||
} __except(2) {
|
||||
}
|
||||
f();
|
||||
__leave;
|
||||
f(); // expected-warning{{will never be executed}}
|
||||
} __except(1) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
void g3() {
|
||||
__try {
|
||||
__try {
|
||||
f();
|
||||
} __except (1) {
|
||||
__leave; // should exit outer try
|
||||
}
|
||||
__leave;
|
||||
f(); // expected-warning{{never be executed}}
|
||||
} __except (1) {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue