[llvm] r228733 - Adding support for llvm.eh.begincatch and llvm.eh.endcatch intrinsics and beginning the documentation of native Windows exception handling.

Andrew Kaylor andrew.kaylor at intel.com
Tue Feb 10 11:52:43 PST 2015


Author: akaylor
Date: Tue Feb 10 13:52:43 2015
New Revision: 228733

URL: http://llvm.org/viewvc/llvm-project?rev=228733&view=rev
Log:
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


Added:
    llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll
    llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics.ll
Modified:
    llvm/trunk/docs/ExceptionHandling.rst
    llvm/trunk/include/llvm/IR/Intrinsics.td
    llvm/trunk/lib/Analysis/Lint.cpp
    llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Modified: llvm/trunk/docs/ExceptionHandling.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/ExceptionHandling.rst?rev=228733&r1=228732&r2=228733&view=diff
==============================================================================
--- llvm/trunk/docs/ExceptionHandling.rst (original)
+++ llvm/trunk/docs/ExceptionHandling.rst Tue Feb 10 13:52:43 2015
@@ -64,6 +64,21 @@ handling at the expense of slower execut
 exceptions are, by their nature, intended for uncommon code paths, DWARF
 exception handling is generally preferred to SJLJ.
 
+Windows Runtime Exception Handling
+-----------------------------------
+
+Windows runtime based exception handling uses the same basic IR structure as
+Itanium ABI based exception handling, but it relies on the personality
+functions provided by the native Windows runtime library, ``__CxxFrameHandler3``
+for C++ exceptions: ``__C_specific_handler`` for 64-bit SEH or 
+``_frame_handler3/4`` for 32-bit SEH.  This results in a very different
+execution model and requires some minor modifications to the initial IR
+representation and a significant restructuring just before code generation.
+
+General information about the Windows x64 exception handling mechanism can be
+found at `MSDN Exception Handling (x64)
+<https://msdn.microsoft.com/en-us/library/1eyas8tf(v=vs.80).aspx>_`.
+
 Overview
 --------
 
@@ -306,6 +321,97 @@ the selector results they understand and
 the `resume instruction <LangRef.html#i_resume>`_ if none of the conditions
 match.
 
+C++ Exception Handling using the Windows Runtime
+=================================================
+
+(Note: Windows C++ exception handling support is a work in progress and is
+ not yet fully implemented.  The text below describes how it will work
+ when completed.)
+
+The Windows runtime function for C++ exception handling uses a mutli-phase
+approach.  When an exception occurs it searches the current callstack for a
+frame that has a handler for the exception.  If a handler is found, it then
+calls the cleanup handler for each frame above the handler which has a
+cleanup handler before calling the catch handler.  These calls are all made
+from a stack context different from the original frame in which the handler
+is defined.  Therefore, it is necessary to outline these handlers from their
+original context before code generation.
+
+Catch handlers are called with a pointer to the handler itself as the first
+argument and a pointer to the parent function's stack frame as the second
+argument.  The catch handler uses the `llvm.recoverframe
+<LangRef.html#llvm-frameallocate-and-llvm-framerecover-intrinsics>`_ to get a
+pointer to a frame allocation block that is created in the parent frame using
+the `llvm.allocateframe 
+<LangRef.html#llvm-frameallocate-and-llvm-framerecover-intrinsics>`_ intrinsic.
+The ``WinEHPrepare`` pass will have created a structure definition for the
+contents of this block.  The first two members of the structure will always be
+(1) a 32-bit integer that the runtime uses to track the exception state of the
+parent frame for the purposes of handling chained exceptions and (2) a pointer
+to the object associated with the exception (roughly, the parameter of the
+catch clause). These two members will be followed by any frame variables from
+the parent function which must be accessed in any of the functions unwind or
+catch handlers.  The catch handler returns the address at which execution
+should continue.
+
+Cleanup handlers perform any cleanup necessary as the frame goes out of scope,
+such as calling object destructors.  The runtime handles the actual unwinding
+of the stack.  If an exception occurs in a cleanup handler the runtime manages
+termination of the process. Cleanup handlers are called with the same arguments
+as catch handlers (a pointer to the handler and a pointer to the parent stack
+frame) and use the same mechanism described above to access frame variables
+in the parent function.  Cleanup handlers do not return a value.
+
+The IR generated for Windows runtime based C++ exception handling is initially
+very similar to the ``landingpad`` mechanism described above.  Calls to
+libc++abi functions (such as ``__cxa_begin_catch``/``__cxa_end_catch`` and
+``__cxa_throw_exception`` are replaced with calls to intrinsics or Windows
+runtime functions (such as ``llvm.eh.begincatch``/``llvm.eh.endcatch`` and
+``__CxxThrowException``).
+
+During the WinEHPrepare pass, the handler functions are outlined into handler
+functions and the original landing pad code is replaced with a call to the
+``llvm.eh.actions`` intrinsic that describes the order in which handlers will
+be processed from the logical location of the landing pad and an indirect
+branch to the return value of the ``llvm.eh.actions`` intrinsic. The
+``llvm.eh.actions`` intrinsic is defined as returning the address at which
+execution will continue.  This is a temporary construct which will be removed
+before code generation, but it allows for the accurate tracking of control
+flow until then.
+
+A typical landing pad will look like this after outlining:
+
+.. code-block:: llvm
+
+    lpad:
+      %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+	      cleanup
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+          catch i8* bitcast (i8** @_ZTIf to i8*)
+      %recover = call i8* (...)* @llvm.eh.actions(
+          i32 3, i8* bitcast (i8** @_ZTIi to i8*), i8* (i8*, i8*)* @_Z4testb.catch.1)
+          i32 2, i8* null, void (i8*, i8*)* @_Z4testb.cleanup.1)
+          i32 1, i8* bitcast (i8** @_ZTIf to i8*), i8* (i8*, i8*)* @_Z4testb.catch.0)
+          i32 0, i8* null, void (i8*, i8*)* @_Z4testb.cleanup.0)
+      indirectbr i8* %recover, [label %try.cont1, label %try.cont2]
+
+In this example, the landing pad represents an exception handling context with
+two catch handlers and a cleanup handler that have been outlined.  If an
+exception is thrown with a type that matches ``_ZTIi``, the ``_Z4testb.catch.1``
+handler will be called an no clean-up is needed.  If an exception is thrown
+with a type that matches ``_ZTIf``, first the ``_Z4testb.cleanup.1`` handler
+will be called to perform unwind-related cleanup, then the ``_Z4testb.catch.1``
+handler will be called.  If an exception is throw which does not match either
+of these types and the exception is handled by another frame further up the
+call stack, first the ``_Z4testb.cleanup.1`` handler will be called, then the
+``_Z4testb.cleanup.0`` handler (which corresponds to a different scope) will be
+called, and exception handling will continue at the next frame in the call
+stack will be called.  One of the catch handlers will return the address of
+``%try.cont1`` in the parent function and the other will return the address of
+``%try.cont2``, meaning that execution continues at one of those blocks after
+an exception is caught.
+
+
 Exception Handling Intrinsics
 =============================
 
@@ -329,6 +435,70 @@ function.  This value can be used to com
 
 Uses of this intrinsic are generated by the C++ front-end.
 
+.. _llvm.eh.begincatch:
+
+``llvm.eh.begincatch``
+----------------------
+
+.. code-block:: llvm
+
+  i8* @llvm.eh.begincatch(i8* %exn)
+
+
+This intrinsic marks the beginning of catch handling code within the blocks
+following a ``landingpad`` instruction.  The exact behavior of this function
+depends on the compilation target and the personality function associated
+with the ``landingpad`` instruction.
+
+The argument to this intrinsic is a pointer that was previously extracted from
+the aggregate return value of the ``landingpad`` instruction.  The return
+value of the intrinsic is a pointer to the exception object to be used by the
+catch code.  This pointer is returned as an ``i8*`` value, but the actual type
+of the object will depend on the exception that was thrown.
+
+Uses of this intrinsic are generated by the C++ front-end.  Many targets will
+use implementation-specific functions (such as ``__cxa_begin_catch``) instead
+of this intrinsic.  The intrinsic is provided for targets that require a more
+abstract interface.
+
+When used in the native Windows C++ exception handling implementation, this
+intrinsic serves as a placeholder to delimit code before a catch handler is
+outlined.  When the handler is is outlined, this intrinsic will be replaced
+by instructions that retrieve the exception object pointer from the frame
+allocation block.
+
+
+.. _llvm.eh.endcatch:
+
+``llvm.eh.endcatch``
+----------------------
+
+.. code-block:: llvm
+
+  void @llvm.eh.endcatch()
+
+
+This intrinsic marks the end of catch handling code within the current block,
+which will be a successor of a block which called ``llvm.eh.begincatch''.
+The exact behavior of this function depends on the compilation target and the
+personality function associated with the corresponding ``landingpad``
+instruction.
+
+There may be more than one call to ``llvm.eh.endcatch`` for any given call to
+``llvm.eh.begincatch`` with each ``llvm.eh.endcatch`` call corresponding to the
+end of a different control path.  All control paths following a call to
+``llvm.eh.begincatch`` must reach a call to ``llvm.eh.endcatch``.
+
+Uses of this intrinsic are generated by the C++ front-end.  Many targets will
+use implementation-specific functions (such as ``__cxa_begin_catch``) instead
+of this intrinsic.  The intrinsic is provided for targets that require a more
+abstract interface.
+
+When used in the native Windows C++ exception handling implementation, this
+intrinsic serves as a placeholder to delimit code before a catch handler is
+outlined.  After the handler is outlined, this intrinsic is simply removed.
+
+
 SJLJ Intrinsics
 ---------------
 

Modified: llvm/trunk/include/llvm/IR/Intrinsics.td
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/Intrinsics.td?rev=228733&r1=228732&r2=228733&view=diff
==============================================================================
--- llvm/trunk/include/llvm/IR/Intrinsics.td (original)
+++ llvm/trunk/include/llvm/IR/Intrinsics.td Tue Feb 10 13:52:43 2015
@@ -411,6 +411,11 @@ def int_eh_typeid_for : Intrinsic<[llvm_
 def int_eh_return_i32 : Intrinsic<[], [llvm_i32_ty, llvm_ptr_ty]>;
 def int_eh_return_i64 : Intrinsic<[], [llvm_i64_ty, llvm_ptr_ty]>;
 
+// eh.begincatch takes a pointer returned by a landingpad instruction and
+// returns the exception object pointer for the exception to be handled.
+def int_eh_begincatch : Intrinsic<[llvm_ptr_ty], [llvm_ptr_ty]>;
+def int_eh_endcatch : Intrinsic<[], []>;
+
 // __builtin_unwind_init is an undocumented GCC intrinsic that causes all
 // callee-saved registers to be saved and restored (regardless of whether they
 // are used) in the calling function. It is used by libgcc_eh.

Modified: llvm/trunk/lib/Analysis/Lint.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/Lint.cpp?rev=228733&r1=228732&r2=228733&view=diff
==============================================================================
--- llvm/trunk/lib/Analysis/Lint.cpp (original)
+++ llvm/trunk/lib/Analysis/Lint.cpp Tue Feb 10 13:52:43 2015
@@ -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.

Modified: llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp?rev=228733&r1=228732&r2=228733&view=diff
==============================================================================
--- llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (original)
+++ llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp Tue Feb 10 13:52:43 2015
@@ -5670,6 +5670,9 @@ SelectionDAGBuilder::visitIntrinsicCall(
 
     return nullptr;
   }
+  case Intrinsic::eh_begincatch:
+  case Intrinsic::eh_endcatch:
+    llvm_unreachable("begin/end catch intrinsics not lowered in codegen");
   }
 }
 

Added: llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll?rev=228733&view=auto
==============================================================================
--- llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll (added)
+++ llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll Tue Feb 10 13:52:43 2015
@@ -0,0 +1,109 @@
+; RUN: opt -lint -disable-output < %s
+
+; This test is meant to prove that the verifier does not report errors for correct
+; use of the llvm.eh.begincatch and llvm.eh.endcatch intrinsics.
+
+target triple = "x86_64-pc-windows-msvc"
+
+declare i8* @llvm.eh.begincatch(i8*)
+
+declare void @llvm.eh.endcatch()
+
+ at _ZTIi = external constant i8*
+
+; Function Attrs: uwtable
+define void @test_ref_clean() {
+entry:
+  invoke void @_Z9may_throwv()
+          to label %try.cont unwind label %lpad
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn = extractvalue { i8*, i32 } %0, 0
+  %sel = extractvalue { i8*, i32 } %0, 1
+  %1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matches = icmp eq i32 %sel, %1
+  br i1 %matches, label %catch, label %eh.resume
+
+catch:                                            ; preds = %lpad
+  %2 = call i8* @llvm.eh.begincatch(i8* %exn)
+  call void @_Z10handle_intv()
+  br label %invoke.cont2
+
+invoke.cont2:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %try.cont
+
+try.cont:                                         ; preds = %invoke.cont2, %entry
+  ret void
+
+eh.resume:                                        ; preds = %catch.dispatch
+  resume { i8*, i32 } %0
+}
+
+; Function Attrs: uwtable
+define void @test_ref_clean_multibranch() {
+entry:
+  invoke void @_Z9may_throwv()
+          to label %invoke.cont unwind label %lpad
+
+invoke.cont:
+  invoke void @_Z9may_throwv()
+          to label %invoke.cont unwind label %lpad1
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn = extractvalue { i8*, i32 } %0, 0
+  %sel = extractvalue { i8*, i32 } %0, 1
+  %1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matches = icmp eq i32 %sel, %1
+  br i1 %matches, label %catch, label %eh.resume
+
+  invoke void @_Z9may_throwv()
+          to label %try.cont unwind label %lpad
+
+lpad1:                                            ; preds = %entry
+  %l1.0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+		  cleanup
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn1 = extractvalue { i8*, i32 } %l1.0, 0
+  %sel1 = extractvalue { i8*, i32 } %l1.0, 1
+  %l1.1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matchesl1 = icmp eq i32 %sel1, %l1.1
+  br i1 %matchesl1, label %catch, label %eh.resume
+
+catch:                                            ; preds = %lpad, %lpad1
+  %exn2 = phi i8* [%exn, %lpad], [%exn1, %lpad1]
+  %sel2 = phi i32 [%sel, %lpad], [%sel1, %lpad1]
+  %3 = call i8* @llvm.eh.begincatch(i8* %exn2)
+  call void @_Z10handle_intv()
+  %matches1 = icmp eq i32 %sel2, 0
+  br i1 %matches1, label %invoke.cont2, label %invoke.cont3
+
+invoke.cont2:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %try.cont
+
+invoke.cont3:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %eh.resume
+
+try.cont:                                         ; preds = %invoke.cont2, %entry
+  ret void
+
+eh.resume:                                        ; preds = %catch.dispatch
+  %lpad.val = insertvalue { i8*, i32 } undef, i32 0, 1
+  resume { i8*, i32 } %lpad.val
+}
+
+declare void @_Z9may_throwv()
+
+declare i32 @__CxxFrameHandler3(...)
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.eh.typeid.for(i8*)
+
+declare void @_Z10handle_intv()
+

Added: llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics.ll?rev=228733&view=auto
==============================================================================
--- llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics.ll (added)
+++ llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics.ll Tue Feb 10 13:52:43 2015
@@ -0,0 +1,278 @@
+; RUN: opt -lint -disable-output < %s 2>&1 | FileCheck %s
+
+; This test is meant to prove that the Verifier is able to identify a variety
+; of errors with the llvm.eh.begincatch and llvm.eh.endcatch intrinsics.
+; See cppeh-catch-intrinsics-clean for correct uses.
+
+target triple = "x86_64-pc-windows-msvc"
+
+declare i8* @llvm.eh.begincatch(i8*)
+
+declare void @llvm.eh.endcatch()
+
+ at _ZTIi = external constant i8*
+
+; Function Attrs: uwtable
+define void @test_missing_endcatch() {
+; CHECK: Some paths from llvm.eh.begincatch may not reach llvm.eh.endcatch
+; CHECK-NEXT: %2 = call i8* @llvm.eh.begincatch(i8* %exn)
+entry:
+  invoke void @_Z9may_throwv()
+          to label %try.cont unwind label %lpad
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn = extractvalue { i8*, i32 } %0, 0
+  %sel = extractvalue { i8*, i32 } %0, 1
+  %1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matches = icmp eq i32 %sel, %1
+  br i1 %matches, label %catch, label %eh.resume
+
+catch:                                            ; preds = %lpad
+  %2 = call i8* @llvm.eh.begincatch(i8* %exn)
+  call void @_Z10handle_intv()
+  br label %invoke.cont2
+
+invoke.cont2:                                     ; preds = %catch
+  br label %try.cont
+
+try.cont:                                         ; preds = %invoke.cont2, %entry
+  ret void
+
+eh.resume:                                        ; preds = %catch.dispatch
+  resume { i8*, i32 } %0
+}
+
+; Function Attrs: uwtable
+define void @test_missing_begincatch() {
+; CHECK: llvm.eh.endcatch may be reachable without passing llvm.eh.begincatch
+; CHECK-NEXT:  call void @llvm.eh.endcatch()
+entry:
+  invoke void @_Z9may_throwv()
+          to label %try.cont unwind label %lpad
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn = extractvalue { i8*, i32 } %0, 0
+  %sel = extractvalue { i8*, i32 } %0, 1
+  %1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matches = icmp eq i32 %sel, %1
+  br i1 %matches, label %catch, label %eh.resume
+
+catch:                                            ; preds = %lpad
+  call void @_Z10handle_intv()
+  br label %invoke.cont2
+
+invoke.cont2:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %try.cont
+
+try.cont:                                         ; preds = %invoke.cont2, %entry
+  ret void
+
+eh.resume:                                        ; preds = %catch.dispatch
+  resume { i8*, i32 } %0
+}
+
+; Function Attrs: uwtable
+define void @test_multiple_begin() {
+; CHECK: llvm.eh.begincatch may be called a second time before llvm.eh.endcatch
+; CHECK-NEXT:  %2 = call i8* @llvm.eh.begincatch(i8* %exn)
+; CHECK-NEXT:  %3 = call i8* @llvm.eh.begincatch(i8* %exn)
+entry:
+  invoke void @_Z9may_throwv()
+          to label %try.cont unwind label %lpad
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn = extractvalue { i8*, i32 } %0, 0
+  %sel = extractvalue { i8*, i32 } %0, 1
+  %1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matches = icmp eq i32 %sel, %1
+  br i1 %matches, label %catch, label %eh.resume
+
+catch:                                            ; preds = %lpad
+  %2 = call i8* @llvm.eh.begincatch(i8* %exn)
+  call void @_Z10handle_intv()
+  br label %invoke.cont2
+
+invoke.cont2:                                     ; preds = %catch
+  %3 = call i8* @llvm.eh.begincatch(i8* %exn)
+  call void @llvm.eh.endcatch()
+  br label %try.cont
+
+try.cont:                                         ; preds = %invoke.cont2, %entry
+  ret void
+
+eh.resume:                                        ; preds = %catch.dispatch
+  resume { i8*, i32 } %0
+}
+
+; Function Attrs: uwtable
+define void @test_multiple_end() {
+; CHECK: llvm.eh.endcatch may be called a second time after llvm.eh.begincatch
+; CHECK-NEXT:  call void @llvm.eh.endcatch()
+; CHECK-NEXT:  call void @llvm.eh.endcatch()
+entry:
+  invoke void @_Z9may_throwv()
+          to label %try.cont unwind label %lpad
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn = extractvalue { i8*, i32 } %0, 0
+  %sel = extractvalue { i8*, i32 } %0, 1
+  %1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matches = icmp eq i32 %sel, %1
+  br i1 %matches, label %catch, label %eh.resume
+
+catch:                                            ; preds = %lpad
+  %2 = call i8* @llvm.eh.begincatch(i8* %exn)
+  call void @_Z10handle_intv()
+  call void @llvm.eh.endcatch()
+  br label %invoke.cont2
+
+invoke.cont2:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %try.cont
+
+try.cont:                                         ; preds = %invoke.cont2, %entry
+  ret void
+
+eh.resume:                                        ; preds = %catch.dispatch
+  resume { i8*, i32 } %0
+}
+
+
+; Function Attrs: uwtable
+define void @test_begincatch_without_lpad() {
+; CHECK: llvm.eh.begincatch may be reachable without passing a landingpad
+; CHECK-NEXT:  %0 = call i8* @llvm.eh.begincatch(i8* %exn)
+entry:
+  %exn = alloca i8
+  %0 = call i8* @llvm.eh.begincatch(i8* %exn)
+  call void @_Z10handle_intv()
+  br label %invoke.cont2
+
+invoke.cont2:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %try.cont
+
+try.cont:                                         ; preds = %invoke.cont2, %entry
+  ret void
+}
+
+; Function Attrs: uwtable
+define void @test_branch_to_begincatch_with_no_lpad(i32 %fake.sel) {
+; CHECK: llvm.eh.begincatch may be reachable without passing a landingpad
+; CHECK-NEXT:  %3 = call i8* @llvm.eh.begincatch(i8* %exn2)
+entry:
+  %fake.exn = alloca i8
+  invoke void @_Z9may_throwv()
+          to label %catch unwind label %lpad
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn = extractvalue { i8*, i32 } %0, 0
+  %sel = extractvalue { i8*, i32 } %0, 1
+  %1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matches = icmp eq i32 %sel, %1
+  br i1 %matches, label %catch, label %eh.resume
+
+  invoke void @_Z9may_throwv()
+          to label %try.cont unwind label %lpad
+
+catch:                                            ; preds = %lpad, %entry
+  %exn2 = phi i8* [%exn, %lpad], [%fake.exn, %entry]
+  %sel2 = phi i32 [%sel, %lpad], [%fake.sel, %entry]
+  %3 = call i8* @llvm.eh.begincatch(i8* %exn2)
+  call void @_Z10handle_intv()
+  %matches1 = icmp eq i32 %sel2, 0
+  br i1 %matches1, label %invoke.cont2, label %invoke.cont3
+
+invoke.cont2:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %try.cont
+
+invoke.cont3:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %eh.resume
+
+try.cont:                                         ; preds = %invoke.cont2
+  ret void
+
+eh.resume:                                        ; preds = %catch.dispatch
+  %lpad.val = insertvalue { i8*, i32 } undef, i32 0, 1
+  resume { i8*, i32 } %lpad.val
+}
+
+; Function Attrs: uwtable
+define void @test_branch_missing_endcatch() {
+; CHECK: Some paths from llvm.eh.begincatch may not reach llvm.eh.endcatch
+; CHECK-NEXT:  %3 = call i8* @llvm.eh.begincatch(i8* %exn2)
+entry:
+  invoke void @_Z9may_throwv()
+          to label %invoke.cont unwind label %lpad
+
+invoke.cont:
+  invoke void @_Z9may_throwv()
+          to label %invoke.cont unwind label %lpad1
+
+lpad:                                             ; preds = %entry
+  %0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn = extractvalue { i8*, i32 } %0, 0
+  %sel = extractvalue { i8*, i32 } %0, 1
+  %1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matches = icmp eq i32 %sel, %1
+  br i1 %matches, label %catch, label %eh.resume
+
+  invoke void @_Z9may_throwv()
+          to label %try.cont unwind label %lpad
+
+lpad1:                                            ; preds = %entry
+  %l1.0 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
+		  cleanup
+          catch i8* bitcast (i8** @_ZTIi to i8*)
+  %exn1 = extractvalue { i8*, i32 } %l1.0, 0
+  %sel1 = extractvalue { i8*, i32 } %l1.0, 1
+  %l1.1 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*))
+  %matchesl1 = icmp eq i32 %sel1, %l1.1
+  br i1 %matchesl1, label %catch, label %eh.resume
+
+catch:                                            ; preds = %lpad, %lpad1
+  %exn2 = phi i8* [%exn, %lpad], [%exn1, %lpad1]
+  %sel2 = phi i32 [%sel, %lpad], [%sel1, %lpad1]
+  %3 = call i8* @llvm.eh.begincatch(i8* %exn2)
+  call void @_Z10handle_intv()
+  %matches1 = icmp eq i32 %sel2, 0
+  br i1 %matches1, label %invoke.cont2, label %invoke.cont3
+
+invoke.cont2:                                     ; preds = %catch
+  call void @llvm.eh.endcatch()
+  br label %try.cont
+
+invoke.cont3:                                     ; preds = %catch
+  br label %eh.resume
+
+try.cont:                                         ; preds = %invoke.cont2, %entry
+  ret void
+
+eh.resume:                                        ; preds = %catch.dispatch
+  %lpad.val = insertvalue { i8*, i32 } undef, i32 0, 1
+  resume { i8*, i32 } %lpad.val
+}
+
+declare void @_Z9may_throwv()
+
+declare i32 @__CxxFrameHandler3(...)
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.eh.typeid.for(i8*)
+
+declare void @_Z10handle_intv()
+





More information about the llvm-commits mailing list