diff options
author | Andrew Kaylor <andrew.kaylor@intel.com> | 2015-02-10 19:52:43 +0000 |
---|---|---|
committer | Andrew Kaylor <andrew.kaylor@intel.com> | 2015-02-10 19:52:43 +0000 |
commit | 77418518199b0834afae681f53a95c5119c215db (patch) | |
tree | ebb9fa65d01d921cfed0e476fc78be82ce3f74e8 /lib/Analysis/Lint.cpp | |
parent | b613f8842e77483311483b628c68e3c4e857cc77 (diff) |
Adding support for llvm.eh.begincatch and llvm.eh.endcatch intrinsics and beginning the documentation of native Windows exception handling.
Differential Revision: http://reviews.llvm.org/D7398
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@228733 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis/Lint.cpp')
-rw-r--r-- | lib/Analysis/Lint.cpp | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/lib/Analysis/Lint.cpp b/lib/Analysis/Lint.cpp index 30a26665347..beebb3b0f29 100644 --- a/lib/Analysis/Lint.cpp +++ b/lib/Analysis/Lint.cpp @@ -35,6 +35,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/Lint.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AssumptionCache.h" @@ -73,6 +74,8 @@ namespace { void visitMemoryReference(Instruction &I, Value *Ptr, uint64_t Size, unsigned Align, Type *Ty, unsigned Flags); + void visitEHBeginCatch(IntrinsicInst *II); + void visitEHEndCatch(IntrinsicInst *II); void visitCallInst(CallInst &I); void visitInvokeInst(InvokeInst &I); @@ -346,6 +349,13 @@ void Lint::visitCallSite(CallSite CS) { visitMemoryReference(I, CS.getArgument(0), AliasAnalysis::UnknownSize, 0, nullptr, MemRef::Read | MemRef::Write); break; + + case Intrinsic::eh_begincatch: + visitEHBeginCatch(II); + break; + case Intrinsic::eh_endcatch: + visitEHEndCatch(II); + break; } } @@ -509,6 +519,188 @@ void Lint::visitShl(BinaryOperator &I) { "Undefined result: Shift count out of range", &I); } +static bool +allPredsCameFromLandingPad(BasicBlock *BB, + SmallSet<BasicBlock *, 4> &VisitedBlocks) { + VisitedBlocks.insert(BB); + if (BB->isLandingPad()) + return true; + // If we find a block with no predecessors, the search failed. + if (pred_empty(BB)) + return false; + for (BasicBlock *Pred : predecessors(BB)) { + if (VisitedBlocks.count(Pred)) + continue; + if (!allPredsCameFromLandingPad(Pred, VisitedBlocks)) + return false; + } + return true; +} + +static bool +allSuccessorsReachEndCatch(BasicBlock *BB, BasicBlock::iterator InstBegin, + IntrinsicInst **SecondBeginCatch, + SmallSet<BasicBlock *, 4> &VisitedBlocks) { + VisitedBlocks.insert(BB); + for (BasicBlock::iterator I = InstBegin, E = BB->end(); I != E; ++I) { + IntrinsicInst *IC = dyn_cast<IntrinsicInst>(I); + if (IC && IC->getIntrinsicID() == Intrinsic::eh_endcatch) + return true; + // If we find another begincatch while looking for an endcatch, + // that's also an error. + if (IC && IC->getIntrinsicID() == Intrinsic::eh_begincatch) { + *SecondBeginCatch = IC; + return false; + } + } + + // If we reach a block with no successors while searching, the + // search has failed. + if (succ_empty(BB)) + return false; + // Otherwise, search all of the successors. + for (BasicBlock *Succ : successors(BB)) { + if (VisitedBlocks.count(Succ)) + continue; + if (!allSuccessorsReachEndCatch(Succ, Succ->begin(), SecondBeginCatch, + VisitedBlocks)) + return false; + } + return true; +} + +void Lint::visitEHBeginCatch(IntrinsicInst *II) { + // The checks in this function make a potentially dubious assumption about + // the CFG, namely that any block involved in a catch is only used for the + // catch. This will very likely be true of IR generated by a front end, + // but it may cease to be true, for example, if the IR is run through a + // pass which combines similar blocks. + // + // In general, if we encounter a block the isn't dominated by the catch + // block while we are searching the catch block's successors for a call + // to end catch intrinsic, then it is possible that it will be legal for + // a path through this block to never reach a call to llvm.eh.endcatch. + // An analogous statement could be made about our search for a landing + // pad among the catch block's predecessors. + // + // What is actually required is that no path is possible at runtime that + // reaches a call to llvm.eh.begincatch without having previously visited + // a landingpad instruction and that no path is possible at runtime that + // calls llvm.eh.begincatch and does not subsequently call llvm.eh.endcatch + // (mentally adjusting for the fact that in reality these calls will be + // removed before code generation). + // + // Because this is a lint check, we take a pessimistic approach and warn if + // the control flow is potentially incorrect. + + SmallSet<BasicBlock *, 4> VisitedBlocks; + BasicBlock *CatchBB = II->getParent(); + + // The begin catch must occur in a landing pad block or all paths + // to it must have come from a landing pad. + Assert1(allPredsCameFromLandingPad(CatchBB, VisitedBlocks), + "llvm.eh.begincatch may be reachable without passing a landingpad", + II); + + // Reset the visited block list. + VisitedBlocks.clear(); + + IntrinsicInst *SecondBeginCatch = nullptr; + + // This has to be called before it is asserted. Otherwise, the first assert + // below can never be hit. + bool EndCatchFound = allSuccessorsReachEndCatch( + CatchBB, std::next(static_cast<BasicBlock::iterator>(II)), + &SecondBeginCatch, VisitedBlocks); + Assert2( + SecondBeginCatch == nullptr, + "llvm.eh.begincatch may be called a second time before llvm.eh.endcatch", + II, SecondBeginCatch); + Assert1(EndCatchFound, + "Some paths from llvm.eh.begincatch may not reach llvm.eh.endcatch", + II); +} + +static bool allPredCameFromBeginCatch( + BasicBlock *BB, BasicBlock::reverse_iterator InstRbegin, + IntrinsicInst **SecondEndCatch, SmallSet<BasicBlock *, 4> &VisitedBlocks) { + VisitedBlocks.insert(BB); + // Look for a begincatch in this block. + for (BasicBlock::reverse_iterator RI = InstRbegin, RE = BB->rend(); RI != RE; + ++RI) { + IntrinsicInst *IC = dyn_cast<IntrinsicInst>(&*RI); + if (IC && IC->getIntrinsicID() == Intrinsic::eh_begincatch) + return true; + // If we find another end catch before we find a begin catch, that's + // an error. + if (IC && IC->getIntrinsicID() == Intrinsic::eh_endcatch) { + *SecondEndCatch = IC; + return false; + } + // If we encounter a landingpad instruction, the search failed. + if (isa<LandingPadInst>(*RI)) + return false; + } + // If while searching we find a block with no predeccesors, + // the search failed. + if (pred_empty(BB)) + return false; + // Search any predecessors we haven't seen before. + for (BasicBlock *Pred : predecessors(BB)) { + if (VisitedBlocks.count(Pred)) + continue; + if (!allPredCameFromBeginCatch(Pred, Pred->rbegin(), SecondEndCatch, + VisitedBlocks)) + return false; + } + return true; +} + +void Lint::visitEHEndCatch(IntrinsicInst *II) { + // The check in this function makes a potentially dubious assumption about + // the CFG, namely that any block involved in a catch is only used for the + // catch. This will very likely be true of IR generated by a front end, + // but it may cease to be true, for example, if the IR is run through a + // pass which combines similar blocks. + // + // In general, if we encounter a block the isn't post-dominated by the + // end catch block while we are searching the end catch block's predecessors + // for a call to the begin catch intrinsic, then it is possible that it will + // be legal for a path to reach the end catch block without ever having + // called llvm.eh.begincatch. + // + // What is actually required is that no path is possible at runtime that + // reaches a call to llvm.eh.endcatch without having previously visited + // a call to llvm.eh.begincatch (mentally adjusting for the fact that in + // reality these calls will be removed before code generation). + // + // Because this is a lint check, we take a pessimistic approach and warn if + // the control flow is potentially incorrect. + + BasicBlock *EndCatchBB = II->getParent(); + + // Alls paths to the end catch call must pass through a begin catch call. + + // If llvm.eh.begincatch wasn't called in the current block, we'll use this + // lambda to recursively look for it in predecessors. + SmallSet<BasicBlock *, 4> VisitedBlocks; + IntrinsicInst *SecondEndCatch = nullptr; + + // This has to be called before it is asserted. Otherwise, the first assert + // below can never be hit. + bool BeginCatchFound = + allPredCameFromBeginCatch(EndCatchBB, BasicBlock::reverse_iterator(II), + &SecondEndCatch, VisitedBlocks); + Assert2( + SecondEndCatch == nullptr, + "llvm.eh.endcatch may be called a second time after llvm.eh.begincatch", + II, SecondEndCatch); + Assert1( + BeginCatchFound, + "llvm.eh.endcatch may be reachable without passing llvm.eh.begincatch", + II); +} + static bool isZero(Value *V, const DataLayout *DL, DominatorTree *DT, AssumptionCache *AC) { // Assume undef could be zero. |