[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