Index: llvm/trunk/docs/ExceptionHandling.rst =================================================================== --- llvm/trunk/docs/ExceptionHandling.rst +++ llvm/trunk/docs/ExceptionHandling.rst @@ -64,6 +64,21 @@ 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) +_`. + Overview -------- @@ -306,6 +321,97 @@ the `resume instruction `_ 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 +`_ to get a +pointer to a frame allocation block that is created in the parent frame using +the `llvm.allocateframe +`_ 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 @@ 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 --------------- Index: llvm/trunk/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/trunk/include/llvm/IR/Intrinsics.td +++ llvm/trunk/include/llvm/IR/Intrinsics.td @@ -411,6 +411,11 @@ 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. Index: llvm/trunk/lib/Analysis/Lint.cpp =================================================================== --- llvm/trunk/lib/Analysis/Lint.cpp +++ llvm/trunk/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 @@ 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 @@ 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 @@ "Undefined result: Shift count out of range", &I); } +static bool +allPredsCameFromLandingPad(BasicBlock *BB, + SmallSet &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 &VisitedBlocks) { + VisitedBlocks.insert(BB); + for (BasicBlock::iterator I = InstBegin, E = BB->end(); I != E; ++I) { + IntrinsicInst *IC = dyn_cast(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 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(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 &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(&*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(*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 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. Index: llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5670,6 +5670,9 @@ return nullptr; } + case Intrinsic::eh_begincatch: + case Intrinsic::eh_endcatch: + llvm_unreachable("begin/end catch intrinsics not lowered in codegen"); } } Index: llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll =================================================================== --- llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll +++ llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics-clean.ll @@ -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() + +@_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() + Index: llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics.ll =================================================================== --- llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics.ll +++ llvm/trunk/test/Analysis/Lint/cppeh-catch-intrinsics.ll @@ -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() + +@_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() +