diff options
author | Nico Weber <nicolasweber@gmx.de> | 2017-08-23 15:33:16 +0000 |
---|---|---|
committer | Nico Weber <nicolasweber@gmx.de> | 2017-08-23 15:33:16 +0000 |
commit | 699670e764ed4f76fc028180baa2aca979790f94 (patch) | |
tree | 17830e4b89623795725a455505ff21af923e49a2 /clang/lib/Analysis/CFG.cpp | |
parent | 3697ebe25fac44888d5adf9d663eb45ca1171a3c (diff) | |
download | bcm5719-llvm-699670e764ed4f76fc028180baa2aca979790f94.tar.gz bcm5719-llvm-699670e764ed4f76fc028180baa2aca979790f94.zip |
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
llvm-svn: 311561
Diffstat (limited to 'clang/lib/Analysis/CFG.cpp')
-rw-r--r-- | clang/lib/Analysis/CFG.cpp | 158 |
1 files changed, 148 insertions, 10 deletions
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index c47a917bef6..164d9640af2 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -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."); |