Index: docs/ExceptionHandling.rst =================================================================== --- docs/ExceptionHandling.rst +++ docs/ExceptionHandling.rst @@ -64,6 +64,18 @@ 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. + + Overview -------- @@ -306,6 +318,63 @@ 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 unwind handler for each frame above the handler which has an +unwind 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 for these handlers to be outlined +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 frame's stack pointer 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. + +Unwind handlers perform any cleanup necessary as the frame goes out of scope, +but the runtime handles the actual unwinding of the stack. If an exception +occurs in an unwind handler the runtime manages termination of the process. +Unwind handlers are called with the same arguments as catch handlers (a +pointer to the handler and a pointer to the parent frame) and use the same +mechanism described above to access frame variables in the parent function. +Unwind 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 and the +``landingpad`` instruction is followed by a call to the ``llvm.eh.actions`` +intrinsic which describes the order in which handlers will be processed from +the logical location of the landing pad. 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. + + Exception Handling Intrinsics ============================= @@ -329,6 +398,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: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -410,6 +410,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: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ 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: lib/IR/Verifier.cpp =================================================================== --- lib/IR/Verifier.cpp +++ lib/IR/Verifier.cpp @@ -46,6 +46,7 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/Verifier.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" @@ -370,6 +371,8 @@ const Value *V); void VerifyConstantExprBitcastType(const ConstantExpr *CE); + void VerifyEHBeginCatch(CallInst &CI); + void VerifyEHEndCatch(CallInst &CI); void VerifyStatepoint(CallInst &CI); }; class DebugInfoVerifier : public VerifierSupport { @@ -1046,6 +1049,136 @@ return false; } +/// \brief Verify that the begin catch intrinsic follows a landingpad and leads +/// to an eh.endcatch. +void Verifier::VerifyEHBeginCatch(CallInst &CI) { + DenseSet VisitedBlocks; // Used in both lambdas below. + + BasicBlock *CatchBB = CI.getParent(); + + // The begin catch must occur in a landing pad block or all paths + // to it must have come from a landing pad. + std::function proceedsFromLPad = [&](BasicBlock *BB) { + 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 (pred_iterator I = pred_begin(BB), E = pred_end(BB); I != E; ++I) { + if (VisitedBlocks.find(*I) != VisitedBlocks.end()) + continue; + if (!proceedsFromLPad(*I)) + return false; + } + return true; + }; + Assert1(proceedsFromLPad(CatchBB), + "llvm.eh.begincatch is reachable without passing a landingpad", &CI); + + // Reset the visited block list. + VisitedBlocks.clear(); + + IntrinsicInst *SecondBeginCatch = nullptr; + std::function reachesEndCatch = [&](BasicBlock *BB) { + VisitedBlocks.insert(BB); + for (BasicBlock::iterator II = BB == CatchBB ? ++BasicBlock::iterator(&CI) + : BB->begin(), + IE = BB->end(); + II != IE; ++II) { + IntrinsicInst *IC = dyn_cast(II); + 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. + TerminatorInst *Terminator = BB->getTerminator(); + if (Terminator->getNumSuccessors() == 0) + return false; + // Otherwise, search all of the successors. + for (unsigned i = 0, e = Terminator->getNumSuccessors(); i != e; ++i) { + BasicBlock *BB = Terminator->getSuccessor(i); + if (VisitedBlocks.find(BB) != VisitedBlocks.end()) + continue; + if (!reachesEndCatch(BB)) + return false; + } + return true; + }; + // This has to be called before it is asserted. Otherwise, the first assert + // below can never be hit. + bool EndCatchFound = reachesEndCatch(CatchBB); + Assert2(SecondBeginCatch == nullptr, + "llvm.eh.begincatch is called a second time before llvm.eh.endcatch", + &CI, SecondBeginCatch); + Assert1(EndCatchFound, + "Not all paths from llvm.eh.begincatch reach llvm.eh.endcatch", &CI); +} + +/// \brief Verify that the end catch intrinsic follows an eh.endcatch. +void Verifier::VerifyEHEndCatch(CallInst &CI) { + BasicBlock *EndCatchBB = CI.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. + DenseSet VisitedBlocks; + IntrinsicInst *SecondEndCatch = nullptr; + std::function proceedsFromBeginCatch = + [&](BasicBlock *BB) { + VisitedBlocks.insert(BB); + // Look for a begincatch in this block. + for (BasicBlock::reverse_iterator + RI = BB == EndCatchBB ? BasicBlock::reverse_iterator(&CI) + : BB->rbegin(), + 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 (pred_iterator I = pred_begin(BB), E = pred_end(BB); I != E; ++I) { + if (VisitedBlocks.find(*I) != VisitedBlocks.end()) + continue; + if (!proceedsFromBeginCatch(*I)) + return false; + } + return true; + }; + + // This has to be called before it is asserted. Otherwise, the first assert + // below can never be hit. + bool BeginCatchFound = proceedsFromBeginCatch(EndCatchBB); + Assert2(SecondEndCatch == nullptr, + "llvm.eh.endcatch is called a second time after llvm.eh.begincatch", + &CI, SecondEndCatch); + Assert1(BeginCatchFound, + "llvm.eh.endcatch is reachable without passing llvm.eh.begincatch", + &CI); +} + /// \brief Verify that statepoint intrinsic is well formed. void Verifier::VerifyStatepoint(CallInst &CI) { assert(CI.getCalledFunction() && @@ -2725,6 +2858,12 @@ "argument must be function defined in this module", &CI); break; } + case Intrinsic::eh_begincatch: + VerifyEHBeginCatch(CI); + break; + case Intrinsic::eh_endcatch: + VerifyEHEndCatch(CI); + break; case Intrinsic::experimental_gc_statepoint: VerifyStatepoint(CI); Index: test/Verifier/cppeh-catch-intrinsics-clean.ll =================================================================== --- test/Verifier/cppeh-catch-intrinsics-clean.ll +++ test/Verifier/cppeh-catch-intrinsics-clean.ll @@ -0,0 +1,109 @@ +; RUN: llvm-as %s -o /dev/null 2>&1 + +; 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: test/Verifier/cppeh-catch-intrinsics.ll =================================================================== --- test/Verifier/cppeh-catch-intrinsics.ll +++ test/Verifier/cppeh-catch-intrinsics.ll @@ -0,0 +1,278 @@ +; RUN: not llvm-as %s -o /dev/null 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() { +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 +} +; CHECK: Not all paths from llvm.eh.begincatch reach llvm.eh.endcatch +; CHECK-NEXT: %2 = call i8* @llvm.eh.begincatch(i8* %exn) + +; Function Attrs: uwtable +define void @test_missing_begincatch() { +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 +} +; CHECK: llvm.eh.endcatch is reachable without passing llvm.eh.begincatch +; CHECK-NEXT: call void @llvm.eh.endcatch() + +; Function Attrs: uwtable +define void @test_multiple_begin() { +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 +} +; CHECK: llvm.eh.begincatch is 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) + +; Function Attrs: uwtable +define void @test_multiple_end() { +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 +} +; CHECK: llvm.eh.endcatch is called a second time after llvm.eh.begincatch +; CHECK-NEXT: call void @llvm.eh.endcatch() +; CHECK-NEXT: call void @llvm.eh.endcatch() + + +; Function Attrs: uwtable +define void @test_begincatch_without_lpad() { +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 +} +; CHECK: llvm.eh.begincatch is reachable without passing a landingpad +; CHECK-NEXT: %0 = call i8* @llvm.eh.begincatch(i8* %exn) + +; Function Attrs: uwtable +define void @test_branch_to_begincatch_with_no_lpad(i32 %fake.sel) { +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 +} +; CHECK: llvm.eh.begincatch is reachable without passing a landingpad +; CHECK-NEXT: %3 = call i8* @llvm.eh.begincatch(i8* %exn2) + +; Function Attrs: uwtable +define void @test_branch_missing_endcatch() { +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 +} +; CHECK: Not all paths from llvm.eh.begincatch reach llvm.eh.endcatch +; CHECK-NEXT: %3 = call i8* @llvm.eh.begincatch(i8* %exn2) + +declare void @_Z9may_throwv() + +declare i32 @__CxxFrameHandler3(...) + +; Function Attrs: nounwind readnone +declare i32 @llvm.eh.typeid.for(i8*) + +declare void @_Z10handle_intv() +