Index: lib/CodeGen/CGCXXABI.h =================================================================== --- lib/CodeGen/CGCXXABI.h +++ lib/CodeGen/CGCXXABI.h @@ -243,6 +243,9 @@ virtual bool canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const = 0; virtual void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) = 0; + virtual void emitBeginCatch(CodeGenFunction &CGF, const VarDecl *CatchParam) { + llvm_unreachable("Only needed for the Microsoft ABI"); + } virtual llvm::CallInst * emitTerminateForUnexpectedException(CodeGenFunction &CGF, Index: lib/CodeGen/CGException.cpp =================================================================== --- lib/CodeGen/CGException.cpp +++ lib/CodeGen/CGException.cpp @@ -1289,14 +1289,22 @@ llvm::Value *EndCatchFn; llvm::Value *RethrowFn; llvm::Value *SavedExnVar; + bool IsOutlined; PerformFinally(const Stmt *Body, llvm::Value *ForEHVar, - llvm::Value *EndCatchFn, - llvm::Value *RethrowFn, llvm::Value *SavedExnVar) - : Body(Body), ForEHVar(ForEHVar), EndCatchFn(EndCatchFn), - RethrowFn(RethrowFn), SavedExnVar(SavedExnVar) {} + llvm::Value *EndCatchFn, llvm::Value *RethrowFn, + llvm::Value *SavedExnVar, bool IsOutlined) + : Body(Body), ForEHVar(ForEHVar), EndCatchFn(EndCatchFn), + RethrowFn(RethrowFn), SavedExnVar(SavedExnVar), + IsOutlined(IsOutlined) {} void Emit(CodeGenFunction &CGF, Flags flags) override { + if (IsOutlined) { + // Just emit the call to the outlined finally. + CGF.EmitStmt(Body); + return; + } + // Enter a cleanup to call the end-catch function if one was provided. if (EndCatchFn) CGF.EHStack.pushCleanup(NormalAndEHCleanup, @@ -1345,7 +1353,7 @@ CGF.PopCleanupBlock(); CGF.Builder.restoreIP(SavedIP); } - + // Now make sure we actually have an insertion point or the // cleanup gods will hate us. CGF.EnsureInsertPoint(); @@ -1365,21 +1373,6 @@ "begin/end catch functions not paired"); assert(rethrowFn && "rethrow function is required"); - BeginCatchFn = beginCatchFn; - - // The rethrow function has one of the following two types: - // void (*)() - // void (*)(void*) - // In the latter case we need to pass it the exception object. - // But we can't use the exception slot because the @finally might - // have a landing pad (which would overwrite the exception slot). - llvm::FunctionType *rethrowFnTy = - cast( - cast(rethrowFn->getType())->getElementType()); - SavedExnVar = nullptr; - if (rethrowFnTy->getNumParams()) - SavedExnVar = CGF.CreateTempAlloca(CGF.Int8PtrTy, "finally.exn"); - // A finally block is a statement which must be executed on any edge // out of a given scope. Unlike a cleanup, the finally block may // contain arbitrary control flow leading out of itself. In @@ -1392,24 +1385,56 @@ // The finally block itself is generated in the context of a cleanup // which conditionally leaves the catch-all. - // Jump destination for performing the finally block on an exception - // edge. We'll never actually reach this block, so unreachable is - // fine. - RethrowDest = CGF.getJumpDestInCurrentScope(CGF.getUnreachableBlock()); - - // Whether the finally block is being executed for EH purposes. - ForEHVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.for-eh"); - CGF.Builder.CreateFlagStore(false, ForEHVar); + // For funclet-based EH (such as Windows MSVC), we cannot branch out of the + // catch-all to the cleanup, since the only valid way to leave the catch-all + // scope would be via a catchret, which would destroy the exception. Instead, + // we expect the frontend to outline the body of the finally, so that it can + // be called separately from both the catch-all and the cleanup. This also + // means we do not currently support control flow leaving the finally (e.g. + // returns and gotos); the frontend will emit errors for that. + IsOutlined = CGF.CGM.getTriple().isWindowsMSVCEnvironment(); + if (IsOutlined) { + assert(!beginCatchFn && + "Should not have begin-catch function when @finally is outlined"); + assert(!endCatchFn && + "Should not have end-catch function when @finally is outlined"); + FinallyBody = dyn_cast(body); + assert(FinallyBody && "Outlined @finally should be a CapturedStmt"); + } else { + BeginCatchFn = beginCatchFn; + + // The rethrow function has one of the following two types: + // void (*)() + // void (*)(void*) + // In the latter case we need to pass it the exception object. + // But we can't use the exception slot because the @finally might + // have a landing pad (which would overwrite the exception slot). + llvm::FunctionType *rethrowFnTy = + cast( + cast(rethrowFn->getType())->getElementType()); + SavedExnVar = nullptr; + if (rethrowFnTy->getNumParams()) + SavedExnVar = CGF.CreateTempAlloca(CGF.Int8PtrTy, "finally.exn"); + + // Jump destination for performing the finally block on an exception + // edge. We'll never actually reach this block, so unreachable is + // fine. + RethrowDest = CGF.getJumpDestInCurrentScope(CGF.getUnreachableBlock()); + + // Whether the finally block is being executed for EH purposes. + ForEHVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.for-eh"); + CGF.Builder.CreateFlagStore(false, ForEHVar); + } // Enter a normal cleanup which will perform the @finally block. - CGF.EHStack.pushCleanup(NormalCleanup, body, - ForEHVar, endCatchFn, - rethrowFn, SavedExnVar); + CGF.EHStack.pushCleanup(NormalCleanup, body, ForEHVar, + endCatchFn, rethrowFn, SavedExnVar, + IsOutlined); // Enter a catch-all scope. llvm::BasicBlock *catchBB = CGF.createBasicBlock("finally.catchall"); EHCatchScope *catchScope = CGF.EHStack.pushCatch(1); - catchScope->setCatchAllHandler(0, catchBB); + catchScope->setHandler(0, CGF.CGM.getCXXABI().getCatchAllTypeInfo(), catchBB); } void CodeGenFunction::FinallyInfo::exit(CodeGenFunction &CGF) { @@ -1426,25 +1451,33 @@ CGBuilderTy::InsertPoint savedIP = CGF.Builder.saveAndClearIP(); CGF.EmitBlock(catchBB); - llvm::Value *exn = nullptr; + if (IsOutlined) { + SaveAndRestore RestoreCurrentFuncletPad( + CGF.CurrentFuncletPad); + CGF.CurrentFuncletPad = catchBB->getFirstNonPHI(); + CGF.EmitStmt(FinallyBody); + CGF.CGM.getCXXABI().emitRethrow(CGF, /*IsNoReturn=*/true); + } else { + llvm::Value *exn = nullptr; - // If there's a begin-catch function, call it. - if (BeginCatchFn) { - exn = CGF.getExceptionFromSlot(); - CGF.EmitNounwindRuntimeCall(BeginCatchFn, exn); - } + // If there's a begin-catch function, call it. + if (BeginCatchFn) { + exn = CGF.getExceptionFromSlot(); + CGF.EmitNounwindRuntimeCall(BeginCatchFn, exn); + } - // If we need to remember the exception pointer to rethrow later, do so. - if (SavedExnVar) { - if (!exn) exn = CGF.getExceptionFromSlot(); - CGF.Builder.CreateAlignedStore(exn, SavedExnVar, CGF.getPointerAlign()); - } + // If we need to remember the exception pointer to rethrow later, do so. + if (SavedExnVar) { + if (!exn) exn = CGF.getExceptionFromSlot(); + CGF.Builder.CreateAlignedStore(exn, SavedExnVar, CGF.getPointerAlign()); + } - // Tell the cleanups in the finally block that we're do this for EH. - CGF.Builder.CreateFlagStore(true, ForEHVar); + // Tell the cleanups in the finally block that we're do this for EH. + CGF.Builder.CreateFlagStore(true, ForEHVar); - // Thread a jump through the finally cleanup. - CGF.EmitBranchThroughCleanup(RethrowDest); + // Thread a jump through the finally cleanup. + CGF.EmitBranchThroughCleanup(RethrowDest); + } CGF.Builder.restoreIP(savedIP); } Index: lib/CodeGen/CGObjCGNU.cpp =================================================================== --- lib/CodeGen/CGObjCGNU.cpp +++ lib/CodeGen/CGObjCGNU.cpp @@ -813,19 +813,23 @@ // If we're in ObjC++ mode, then we want to make if (CGM.getLangOpts().CPlusPlus) { llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext); - // void *__cxa_begin_catch(void *e) - EnterCatchFn.init(&CGM, "__cxa_begin_catch", PtrTy, PtrTy); - // void __cxa_end_catch(void) - ExitCatchFn.init(&CGM, "__cxa_end_catch", VoidTy); + if (!CGM.getTriple().isWindowsMSVCEnvironment()) { + // void *__cxa_begin_catch(void *e) + EnterCatchFn.init(&CGM, "__cxa_begin_catch", PtrTy, PtrTy); + // void __cxa_end_catch(void) + ExitCatchFn.init(&CGM, "__cxa_end_catch", VoidTy); + } // void _Unwind_Resume_or_Rethrow(void*) ExceptionReThrowFn.init(&CGM, "_Unwind_Resume_or_Rethrow", VoidTy, PtrTy); } else if (R.getVersion() >= VersionTuple(1, 7)) { llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext); - // id objc_begin_catch(void *e) - EnterCatchFn.init(&CGM, "objc_begin_catch", IdTy, PtrTy); - // void objc_end_catch(void) - ExitCatchFn.init(&CGM, "objc_end_catch", VoidTy); + if (!CGM.getTriple().isWindowsMSVCEnvironment()) { + // id objc_begin_catch(void *e) + EnterCatchFn.init(&CGM, "objc_begin_catch", IdTy, PtrTy); + // void objc_end_catch(void) + ExitCatchFn.init(&CGM, "objc_end_catch", VoidTy); + } // void _Unwind_Resume_or_Rethrow(void*) ExceptionReThrowFn.init(&CGM, "objc_exception_rethrow", VoidTy, PtrTy); } Index: lib/CodeGen/CGObjCMac.cpp =================================================================== --- lib/CodeGen/CGObjCMac.cpp +++ lib/CodeGen/CGObjCMac.cpp @@ -714,12 +714,18 @@ } llvm::Constant *getObjCEndCatchFn() { + if (CGM.getTriple().isWindowsMSVCEnvironment()) + return nullptr; + return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.VoidTy, false), "objc_end_catch"); } llvm::Constant *getObjCBeginCatchFn() { + if (CGM.getTriple().isWindowsMSVCEnvironment()) + return nullptr; + llvm::Type *params[] = { Int8PtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(Int8PtrTy, params, false), @@ -7496,8 +7502,8 @@ void CGObjCNonFragileABIMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtTryStmt &S) { EmitTryCatchStmt(CGF, S, - cast(ObjCTypes.getObjCBeginCatchFn()), - cast(ObjCTypes.getObjCEndCatchFn()), + cast_or_null(ObjCTypes.getObjCBeginCatchFn()), + cast_or_null(ObjCTypes.getObjCEndCatchFn()), cast(ObjCTypes.getExceptionRethrowFn())); } Index: lib/CodeGen/CGObjCRuntime.cpp =================================================================== --- lib/CodeGen/CGObjCRuntime.cpp +++ lib/CodeGen/CGObjCRuntime.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "CGObjCRuntime.h" +#include "CGCXXABI.h" #include "CGCleanup.h" #include "CGRecordLayout.h" #include "CodeGenFunction.h" @@ -22,6 +23,7 @@ #include "clang/AST/StmtObjC.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "llvm/IR/CallSite.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace CodeGen; @@ -119,7 +121,7 @@ const VarDecl *Variable; const Stmt *Body; llvm::BasicBlock *Block; - llvm::Constant *TypeInfo; + CatchTypeInfo TypeInfo; }; struct CallObjCEndCatch final : EHScopeStack::Cleanup { @@ -169,12 +171,12 @@ // @catch(...) always matches. if (!CatchDecl) { - Handler.TypeInfo = nullptr; // catch-all + Handler.TypeInfo = CGF.CGM.getCXXABI().getCatchAllTypeInfo(); // Don't consider any other catches. break; } - Handler.TypeInfo = GetEHType(CatchDecl->getType()); + Handler.TypeInfo = {GetEHType(CatchDecl->getType()), 0}; } EHCatchScope *Catch = CGF.EHStack.pushCatch(Handlers.size()); @@ -195,44 +197,69 @@ // Emit the handlers. for (unsigned I = 0, E = Handlers.size(); I != E; ++I) { CatchHandler &Handler = Handlers[I]; + if (Handler.Block->use_empty()) { + delete Handler.Block; + continue; + } CGF.EmitBlock(Handler.Block); - llvm::Value *RawExn = CGF.getExceptionFromSlot(); - - // Enter the catch. - llvm::Value *Exn = RawExn; - if (beginCatchFn) - Exn = CGF.EmitNounwindRuntimeCall(beginCatchFn, RawExn, "exn.adjusted"); CodeGenFunction::LexicalScope cleanups(CGF, Handler.Body->getSourceRange()); - if (endCatchFn) { - // Add a cleanup to leave the catch. - bool EndCatchMightThrow = (Handler.Variable == nullptr); - - CGF.EHStack.pushCleanup(NormalAndEHCleanup, - EndCatchMightThrow, - endCatchFn); - } + bool RuntimeCopiesExn = CGF.CGM.getTriple().isWindowsMSVCEnvironment(); + assert(!(RuntimeCopiesExn && beginCatchFn) && + "Should not have begin-catch function if runtime copies exception"); + assert(!(RuntimeCopiesExn && endCatchFn) && + "Should not have end-catch function if runtime copies exception"); + + if (RuntimeCopiesExn) { + SaveAndRestore RestoreCurrentFuncletPad( + CGF.CurrentFuncletPad); + CGF.CGM.getCXXABI().emitBeginCatch(CGF, Handler.Variable); + if (Handler.Variable && Handler.Variable->getDeclName()) + EmitInitOfCatchParam(CGF, nullptr, Handler.Variable); + + CGF.EmitStmt(Handler.Body); + + // Leave any cleanups associated with the catch before we exit the + // catchpad funclet scope. + cleanups.ForceCleanup(); + } else { + llvm::Value *RawExn = CGF.getExceptionFromSlot(); + + // Enter the catch. + llvm::Value *Exn = RawExn; + if (beginCatchFn) + Exn = CGF.EmitNounwindRuntimeCall(beginCatchFn, RawExn, "exn.adjusted"); + + if (endCatchFn) { + // Add a cleanup to leave the catch. + bool EndCatchMightThrow = (Handler.Variable == nullptr); + + CGF.EHStack.pushCleanup(NormalAndEHCleanup, + EndCatchMightThrow, + endCatchFn); + } - // Bind the catch parameter if it exists. - if (const VarDecl *CatchParam = Handler.Variable) { - llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType()); - llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType); + // Bind the catch parameter if it exists. + if (const VarDecl *CatchParam = Handler.Variable) { + llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType()); + llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType); - CGF.EmitAutoVarDecl(*CatchParam); - EmitInitOfCatchParam(CGF, CastExn, CatchParam); - } + CGF.EmitAutoVarDecl(*CatchParam); + EmitInitOfCatchParam(CGF, CastExn, CatchParam); + } - CGF.ObjCEHValueStack.push_back(Exn); - CGF.EmitStmt(Handler.Body); - CGF.ObjCEHValueStack.pop_back(); + CGF.ObjCEHValueStack.push_back(Exn); + CGF.EmitStmt(Handler.Body); + CGF.ObjCEHValueStack.pop_back(); - // Leave any cleanups associated with the catch. - cleanups.ForceCleanup(); + // Leave any cleanups associated with the catch. + cleanups.ForceCleanup(); + } CGF.EmitBranchThroughCleanup(Cont); - } + } // Go back to the try-statement fallthrough. CGF.Builder.restoreIP(SavedIP); @@ -248,21 +275,30 @@ void CGObjCRuntime::EmitInitOfCatchParam(CodeGenFunction &CGF, llvm::Value *exn, const VarDecl *paramDecl) { - Address paramAddr = CGF.GetAddrOfLocalVar(paramDecl); + // If exn is nullptr, the exception object has been copied into paramAddr by + // the runtime, and we can load it from there (and don't need to store it + // back). We perform the loads lazily to avoid emitting the load instruction + // unless it's needed. + bool RuntimeCopiesExn = (exn == nullptr); switch (paramDecl->getType().getQualifiers().getObjCLifetime()) { case Qualifiers::OCL_Strong: + if (RuntimeCopiesExn) + exn = CGF.Builder.CreateLoad(paramAddr); exn = CGF.EmitARCRetainNonBlock(exn); // fallthrough case Qualifiers::OCL_None: case Qualifiers::OCL_ExplicitNone: case Qualifiers::OCL_Autoreleasing: - CGF.Builder.CreateStore(exn, paramAddr); + if (!RuntimeCopiesExn) + CGF.Builder.CreateStore(exn, paramAddr); return; case Qualifiers::OCL_Weak: + if (RuntimeCopiesExn) + exn = CGF.Builder.CreateLoad(paramAddr); CGF.EmitARCInitWeak(paramAddr, exn); return; } Index: lib/CodeGen/CGStmt.cpp =================================================================== --- lib/CodeGen/CGStmt.cpp +++ lib/CodeGen/CGStmt.cpp @@ -2251,7 +2251,9 @@ // Emit the CapturedDecl CodeGenFunction CGF(CGM, true); CGCapturedStmtRAII CapInfoRAII(CGF, new CGCapturedStmtInfo(S, K)); - llvm::Function *F = CGF.GenerateCapturedStmtFunction(S); + llvm::Function *&F = CapturedStmtGeneratedFunctionMap[&S]; + if (!F) + F = CGF.GenerateCapturedStmtFunction(S); delete CGF.CapturedStmtInfo; // Emit call to the helper function. Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -337,6 +337,10 @@ ~CGCapturedStmtRAII() { CGF.CapturedStmtInfo = PrevCapturedStmtInfo; } }; + /// Map a CapturedStmt to its generated function. + llvm::DenseMap + CapturedStmtGeneratedFunctionMap; + /// An abstract representation of regular/ObjC call/message targets. class AbstractCallee { /// The function declaration of the callee. @@ -491,6 +495,12 @@ /// has been saved. llvm::AllocaInst *SavedExnVar; + /// Whether the finally is expected to have been outlined by the frontend. + bool IsOutlined; + + /// The outlined finally body. + const CapturedStmt *FinallyBody; + public: void enter(CodeGenFunction &CGF, const Stmt *Finally, llvm::Constant *beginCatchFn, llvm::Constant *endCatchFn, Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -121,6 +121,7 @@ void emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) override; void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) override; + void emitBeginCatch(CodeGenFunction &CGF, const VarDecl *CatchParam) override; llvm::GlobalVariable *getMSCompleteObjectLocator(const CXXRecordDecl *RD, const VPtrInfo &Info); @@ -863,9 +864,13 @@ void MicrosoftCXXABI::emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *S) { + emitBeginCatch(CGF, S->getExceptionDecl()); +} + +void MicrosoftCXXABI::emitBeginCatch(CodeGenFunction &CGF, + const VarDecl *CatchParam) { // In the MS ABI, the runtime handles the copy, and the catch handler is // responsible for destruction. - VarDecl *CatchParam = S->getExceptionDecl(); llvm::BasicBlock *CatchPadBB = CGF.Builder.GetInsertBlock(); llvm::CatchPadInst *CPI = cast(CatchPadBB->getFirstNonPHI()); Index: test/CodeGenObjC/catch-lexical-block.m =================================================================== --- test/CodeGenObjC/catch-lexical-block.m +++ test/CodeGenObjC/catch-lexical-block.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -debug-info-kind=limited -fobjc-exceptions -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -debug-info-kind=limited -fexceptions -fobjc-exceptions -emit-llvm %s -o - | FileCheck %s @interface Foo @end void f0() { @try { Index: test/CodeGenObjC/exceptions-msvc.m =================================================================== --- test/CodeGenObjC/exceptions-msvc.m +++ test/CodeGenObjC/exceptions-msvc.m @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -triple i686--windows-msvc -fobjc-runtime=ios-6.0 -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X86 %s -// RUN: %clang_cc1 -triple i686--windows-msvc -fobjc-runtime=ios-6.0 -fobjc-arc -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X86 %s -// RUN: %clang_cc1 -triple x86_64--windows-msvc -fobjc-runtime=ios-6.0 -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X64 %s -// RUN: %clang_cc1 -triple x86_64--windows-msvc -fobjc-runtime=ios-6.0 -fobjc-arc -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X64 %s +// RUN: %clang_cc1 -triple i686--windows-msvc -fobjc-runtime=ios-6.0 -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X86 --enable-var-scope %s +// RUN: %clang_cc1 -triple i686--windows-msvc -fobjc-runtime=ios-6.0 -fobjc-arc -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X86,ARC --enable-var-scope %s +// RUN: %clang_cc1 -triple x86_64--windows-msvc -fobjc-runtime=ios-6.0 -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X64 --enable-var-scope %s +// RUN: %clang_cc1 -triple x86_64--windows-msvc -fobjc-runtime=ios-6.0 -fobjc-arc -fdeclspec -fexceptions -fobjc-exceptions -emit-llvm -o - %s | FileCheck -check-prefixes=CHECK,X64,ARC --enable-var-scope %s #if __has_feature(objc_arc) #define WEAK __weak @@ -67,8 +67,9 @@ @protocol P; void f(void); +void __declspec(nothrow) g(void); -void g() { +void generate_ehtypes() { @try { f(); } @catch (I *) { @@ -79,3 +80,393 @@ } @catch (id) { } } + +void basic_try_catch_1() { + @try { + f(); + } @catch (...) { + g(); + } +} + +// CHECK-LABEL: define {{.*}}void @basic_try_catch_1(){{.*}} { +// CHECK-NEXT: entry: +// CHECK-NEXT: invoke void @f() +// CHECK-NEXT: to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]] +// CHECK: [[DISPATCH]]: +// CHECK-NEXT: %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCH:[^ ]+]]] unwind to caller +// CHECK: [[INVOKECONT]]: +// CHECK-NEXT: br label %[[EHCONT:[^ ]+]] +// CHECK: [[EHCONT]]: +// CHECK-NEXT: ret void +// CHECK: [[CATCH]]: +// CHECK-NEXT: %[[CATCHPAD:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD]] to label %[[CATCHRETDEST:[^ ]+]] +// CHECK: [[CATCHRETDEST]]: +// CHECK-NEXT: br label %[[EHCONT]] +// CHECK-NEXT: } + +void basic_try_catch_2() { + @try { + f(); + } @catch (id) { + g(); + } +} + +// CHECK-LABEL: define {{.*}}void @basic_try_catch_2(){{.*}} { +// CHECK-NEXT: entry: +// CHECK-NEXT: invoke void @f() +// CHECK-NEXT: to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]] +// CHECK: [[DISPATCH]]: +// CHECK-NEXT: %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCH:[^ ]+]]] unwind to caller +// CHECK: [[INVOKECONT]]: +// CHECK-NEXT: br label %[[EHCONT:[^ ]+]] +// CHECK: [[EHCONT]]: +// CHECK-NEXT: ret void +// CHECK: [[CATCH]]: +// CHECK-NEXT: %[[CATCHPAD:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* {{.*}}@OBJC_EHTYPE_id{{.*}}, i32 0, i8* null] +// CHECK-NEXT: call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD]] to label %[[CATCHRETDEST:[^ ]+]] +// CHECK: [[CATCHRETDEST]]: +// CHECK-NEXT: br label %[[EHCONT]] +// CHECK-NEXT: } + +void basic_try_catch_3() { + @try { + f(); + } @catch (I *e) { + g(); + } +} + +// CHECK-LABEL: define {{.*}}void @basic_try_catch_3(){{.*}} { +// CHECK-NEXT: entry: +// CHECK-NEXT: %e = alloca %[[ETYPE:[^ ]+]]* +// CHECK-NEXT: invoke void @f() +// CHECK-NEXT: to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]] +// CHECK: [[DISPATCH]]: +// CHECK-NEXT: %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCH:[^ ]+]]] unwind to caller +// CHECK: [[INVOKECONT]]: +// CHECK-NEXT: br label %[[EHCONT:[^ ]+]] +// CHECK: [[EHCONT]]: +// CHECK-NEXT: ret void +// CHECK: [[CATCH]]: +// CHECK-NEXT: %[[CATCHPAD:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* {{.*}}@"OBJC_EHTYPE_$_I"{{.*}}, i32 0, %[[ETYPE]]** %e] +// ARC-NEXT: %[[ELOAD:[^ ]+]] = load %[[ETYPE]]*, %[[ETYPE]]** %e +// ARC-NEXT: %[[CAST1:[^ ]+]] = bitcast %[[ETYPE]]* %[[ELOAD]] to i8* +// ARC-NEXT: call i8* @objc_retain(i8* %[[CAST1]]){{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// ARC-NEXT: bitcast +// CHECK-NEXT: call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// ARC-NEXT: %[[CAST2:[^ ]+]] = bitcast %[[ETYPE]]** %e to i8** +// ARC-NEXT: call void @objc_storeStrong(i8** %[[CAST2]], i8* null){{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD]] to label %[[CATCHRETDEST:[^ ]+]] +// CHECK: [[CATCHRETDEST]]: +// CHECK-NEXT: br label %[[EHCONT]] +// CHECK-NEXT: } + +void multiple_catch() { + @try { + f(); + } @catch (id) { + g(); + } @catch (...) { + g(); + } +} + +// CHECK-LABEL: define {{.*}}void @multiple_catch(){{.*}} { +// CHECK-NEXT: entry: +// CHECK-NEXT: invoke void @f() +// CHECK-NEXT: to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]] +// CHECK: [[DISPATCH]]: +// CHECK-NEXT: %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCH1:[^ ]+]], label %[[CATCH2:[^ ]+]]] unwind to caller +// CHECK: [[INVOKECONT]]: +// CHECK-NEXT: br label %[[EHCONT:[^ ]+]] +// CHECK: [[EHCONT]]: +// CHECK-NEXT: ret void +// CHECK: [[CATCH1]]: +// CHECK-NEXT: %[[CATCHPAD1:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* {{.*}}@OBJC_EHTYPE_id{{.*}}, i32 0, i8* null] +// CHECK-NEXT: call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD1]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD1]] to label %[[CATCHRETDEST1:[^ ]+]] +// CHECK: [[CATCHRETDEST1]]: +// CHECK-NEXT: br label %[[EHCONT]] +// CHECK: [[CATCH2]]: +// CHECK-NEXT: %[[CATCHPAD2:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD2]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD2]] to label %[[CATCHRETDEST2:[^ ]+]] +// CHECK: [[CATCHRETDEST2]]: +// CHECK-NEXT: br label %[[EHCONT]] +// CHECK-NEXT: } + +void basic_try_finally() { + @try { + f(); + } @finally { + g(); + } +} + +// CHECK-LABEL: define {{.*}}void @basic_try_finally(){{.*}} { +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[CAPTURE1:[^ ]+]] = alloca +// CHECK-NEXT: %[[CAPTURE2:[^ ]+]] = alloca +// CHECK-NEXT: invoke void @f() +// CHECK-NEXT: to label %[[INVOKECONT:[^ ]+]] unwind label %[[DISPATCH:[^ ]+]] +// CHECK: [[DISPATCH]]: +// CHECK-NEXT: %[[CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[CATCHALL:[^ ]+]]] unwind to caller +// CHECK: [[INVOKECONT]]: +// CHECK-NEXT: call void @[[FINALLY_FUNCTION:[^ ]+]]({{.*}} %[[CAPTURE2]]) +// CHECK-NEXT: ret void +// CHECK: [[CATCHALL]]: +// CHECK-NEXT: %[[CATCHPAD:[^ ]+]] = catchpad within %[[CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @[[FINALLY_FUNCTION]]({{.*}} %[[CAPTURE1]]) +// CHECK-NEXT: call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: unreachable +// CHECK-NEXT: } + +// (CHECK-LABEL can't be used with a variable capture) +// CHECK: define internal void @[[FINALLY_FUNCTION]]{{.*}} { +// CHECK: call void @g() +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void basic_try_catch_finally() { + @try { + f(); + } @catch (id) { + f(); + } @catch (...) { + g(); + } @finally { + g(); + } +} + +// CHECK-LABEL: define {{.*}}void @basic_try_catch_finally(){{.*}} { +// CHECK: invoke void @f() +// CHECK-NEXT: to label %[[INVOKECONT1:[^ ]+]] unwind label %[[DISPATCH1:[^ ]+]] +// CHECK: [[DISPATCH1]]: +// CHECK-NEXT: %[[CATCHSWITCH1:[^ ]+]] = catchswitch within none [label %[[CATCH1:[^ ]+]], label %[[CATCH2:[^ ]+]]] unwind label %[[DISPATCH2:[^ ]+]] +// CHECK: [[INVOKECONT1]]: +// CHECK: br label %[[CLEANUP:[^ ]+]] +// CHECK: [[CLEANUP]]: +// CHECK-NEXT: call void @[[FINALLY_FUNCTION:[^ ]+]] +// CHECK: [[CATCH1]]: +// CHECK-NEXT: %[[CATCHPAD1:[^ ]+]] = catchpad within %[[CATCHSWITCH1]] [i8* {{.*}}@OBJC_EHTYPE_id{{.*}}, i32 0, i8* null] +// CHECK-NEXT: invoke void @f() [ "funclet"(token %[[CATCHPAD1]]) ] +// CHECK-NEXT: to label %[[INVOKECONT2:[^ ]+]] unwind label %[[DISPATCH2]] +// CHECK: [[DISPATCH2]]: +// CHECK-NEXT: %[[CATCHSWITCH2:[^ ]+]] = catchswitch within none [label %[[CATCHALL:[^ ]+]]] unwind to caller +// CHECK: [[INVOKECONT2]]: +// CHECK-NEXT: catchret from %[[CATCHPAD1]] to label %[[CATCHRETDEST1:[^ ]+]] +// CHECK: [[CATCHRETDEST1]]: +// CHECK: br label %[[CLEANUP]] +// CHECK: [[CATCH2]]: +// CHECK-NEXT: %[[CATCHPAD2:[^ ]+]] = catchpad within %[[CATCHSWITCH1]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @g(){{.*}} [ "funclet"(token %[[CATCHPAD2]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD2]] to label %[[CATCHRETDEST2:[^ ]+]] +// CHECK: [[CATCHRETDEST2]]: +// CHECK: br label %cleanup +// CHECK: [[CATCHALL]]: +// CHECK-NEXT: %[[CATCHPAD3:[^ ]+]] = catchpad within %[[CATCHSWITCH2]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @[[FINALLY_FUNCTION]]{{.*}} [ "funclet"(token %[[CATCHPAD3]]) ] +// CHECK-NEXT: call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[CATCHPAD3]]) ] +// CHECK-NEXT: unreachable + +// CHECK: define internal void @[[FINALLY_FUNCTION]]{{.*}} { +// CHECK: call void @g() +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void nested_try_catch_finally_1() { + f(); + @try { + f(); + @try { + f(); + } @catch (...) { + f(); + } @finally { + f(); + } + } @catch (...) { + g(); + } @finally { + g(); + } +} + +// CHECK-LABEL: define {{.*}}void @nested_try_catch_finally_1(){{.*}} { +// CHECK: call void @f() +// CHECK-NEXT: invoke void @f() +// CHECK-NEXT: to label %[[OUTER_TRY_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_CATCH_DISPATCH:[^ ]+]] +// CHECK: [[OUTER_TRY_INVOKE_CONT]]: +// CHECK: invoke void @f() +// CHECK-NEXT: to label %[[INNER_TRY_INVOKE_CONT:[^ ]+]] unwind label %[[INNER_CATCH_DISPATCH:[^ ]+]] +// CHECK: [[INNER_CATCH_DISPATCH]]: +// CHECK-NEXT: %[[INNER_CATCH_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[INNER_CATCH:[^ ]+]]] unwind label %[[INNER_FINALLY_DISPATCH:[^ ]+]] +// CHECK: [[INNER_TRY_INVOKE_CONT]]: +// CHECK: br label %[[INNER_CLEANUP:[^ ]+]] +// CHECK: [[INNER_CLEANUP]]: +// CHECK-NEXT: invoke void @[[INNER_FINALLY_FUNCTION:[^ ]+]] +// CHECK-NEXT: to label %[[INNER_CLEANUP_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_CATCH_DISPATCH]] +// CHECK: [[OUTER_CATCH_DISPATCH]]: +// CHECK-NEXT: %[[OUTER_CATCH_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[OUTER_CATCH:[^ ]+]]] unwind label %[[OUTER_FINALLY_DISPATCH:[^ ]+]] +// CHECK: [[OUTER_FINALLY_DISPATCH]]: +// CHECK-NEXT: %[[OUTER_FINALLY_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[OUTER_FINALLY:[^ ]+]]] unwind to caller +// CHECK: [[INNER_CLEANUP_INVOKE_CONT]]: +// (the cleanup generates a switch; ignore those labels) +// CHECK: {{^[^ ]+}}: +// CHECK: {{^[^ ]+}}: +// CHECK: [[OUTER_CLEANUP:[^ ]+]]: +// CHECK-NEXT: call void @[[OUTER_FINALLY_FUNCTION:[^ ]+]] +// CHECK: [[INNER_CATCH]]: +// CHECK-NEXT: %[[INNER_CATCH_CATCHPAD:[^ ]+]] = catchpad within %[[INNER_CATCH_CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: invoke void @f() [ "funclet"(token %[[INNER_CATCH_CATCHPAD]]) ] +// CHECK-NEXT: to label %[[INNER_CATCH_INVOKE_CONT:[^ ]+]] unwind label %[[INNER_FINALLY_DISPATCH]] +// CHECK: [[INNER_FINALLY_DISPATCH]]: +// CHECK-NEXT: %[[INNER_FINALLY_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[INNER_FINALLY:[^ ]+]]] unwind label %[[OUTER_CATCH_DISPATCH]] +// CHECK: [[INNER_CATCH_INVOKE_CONT]]: +// CHECK-NEXT: catchret from %[[INNER_CATCH_CATCHPAD]] to label %[[INNER_CATCH_CATCHRET_DEST:[^ ]+]] +// CHECK: [[INNER_CATCH_CATCHRET_DEST]]: +// CHECK: br label %[[INNER_CLEANUP]] +// CHECK: [[INNER_FINALLY]]: +// CHECK-NEXT: %[[INNER_FINALLY_CATCHPAD:[^ ]+]] = catchpad within %[[INNER_FINALLY_CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: invoke void @[[INNER_FINALLY_FUNCTION:[^ ]+]]{{.*}} [ "funclet"(token %[[INNER_FINALLY_CATCHPAD]]) ] +// CHECK-NEXT: to label %[[INNER_FINALLY_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_CATCH_DISPATCH]] +// CHECK: [[INNER_FINALLY_INVOKE_CONT]]: +// CHECK-NEXT: invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[INNER_FINALLY_CATCHPAD]]) ] +// CHECK-NEXT: to label %unreachable unwind label %[[OUTER_CATCH_DISPATCH]] +// CHECK: [[OUTER_CATCH]]: +// CHECK-NEXT: %[[OUTER_CATCH_CATCHPAD:[^ ]+]] = catchpad within %[[OUTER_CATCH_CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @g(){{.*}} [ "funclet"(token %[[OUTER_CATCH_CATCHPAD]]) ] +// CHECK-NEXT: catchret from %[[OUTER_CATCH_CATCHPAD]] to label %[[OUTER_CATCH_CATCHRET_DEST:[^ ]+]] +// CHECK: [[OUTER_CATCH_CATCHRET_DEST]]: +// CHECK: br label %[[OUTER_CLEANUP]] +// CHECK: [[OUTER_FINALLY]]: +// CHECK-NEXT: %[[OUTER_FINALLY_CATCHPAD:[^ ]+]] = catchpad within %[[OUTER_FINALLY_CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @[[OUTER_FINALLY_FUNCTION]]{{.*}} [ "funclet"(token %[[OUTER_FINALLY_CATCHPAD]]) ] +// CHECK-NEXT: call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[OUTER_FINALLY_CATCHPAD]]) ] +// CHECK-NEXT: unreachable + +// CHECK: define internal void @[[INNER_FINALLY_FUNCTION]]{{.*}} { +// CHECK: call void @f() +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define internal void @[[OUTER_FINALLY_FUNCTION]]{{.*}} { +// CHECK: call void @g() +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void nested_try_catch_finally_2() { + @try { + f(); + } @catch (...) { + f(); + @try { + f(); + } @catch (...) { + g(); + } @finally { + f(); + } + } @finally { + g(); + } +} + +// CHECK-LABEL: define {{.*}}void @nested_try_catch_finally_2(){{.*}} { +// CHECK: invoke void @f() +// CHECK-NEXT: to label %[[OUTER_TRY_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_CATCH_DISPATCH:[^ ]+]] +// CHECK: [[OUTER_CATCH_DISPATCH]]: +// CHECK-NEXT: %[[OUTER_CATCH_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[OUTER_CATCH:[^ ]+]]] unwind label %[[OUTER_FINALLY_DISPATCH:[^ ]+]] +// CHECK: [[OUTER_TRY_INVOKE_CONT]]: +// CHECK: br label %[[OUTER_CLEANUP:[^ ]+]] +// CHECK: [[OUTER_CLEANUP]]: +// CHECK-NEXT: call void @[[OUTER_FINALLY_FUNCTION:[^ ]+]] +// CHECK: [[OUTER_CATCH]]: +// CHECK-NEXT: %[[OUTER_CATCH_CATCHPAD:[^ ]+]] = catchpad within %[[OUTER_CATCH_CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: invoke void @f() [ "funclet"(token %[[OUTER_CATCH_CATCHPAD]]) ] +// CHECK-NEXT: to label %[[INNER_TRY:[^ ]+]] unwind label %[[OUTER_FINALLY_DISPATCH]] +// CHECK: [[INNER_TRY]]: +// CHECK-NEXT: invoke void @f() [ "funclet"(token %[[OUTER_CATCH_CATCHPAD]]) ] +// CHECK-NEXT: to label %[[INNER_TRY_INVOKE_CONT:[^ ]+]] unwind label %[[INNER_CATCH_DISPATCH:[^ ]+]] +// CHECK: [[INNER_CATCH_DISPATCH]]: +// CHECK-NEXT: %[[INNER_CATCH_CATCHSWITCH:[^ ]+]] = catchswitch within %[[OUTER_CATCH_CATCHPAD]] [label %[[INNER_CATCH:[^ ]+]]] unwind label %[[INNER_FINALLY_DISPATCH:[^ ]+]] +// CHECK: [[INNER_FINALLY_DISPATCH]]: +// CHECK-NEXT: %[[INNER_FINALLY_CATCHSWITCH:[^ ]+]] = catchswitch within %[[OUTER_CATCH_CATCHPAD]] [label %[[INNER_FINALLY:[^ ]+]]] unwind label %[[OUTER_FINALLY_DISPATCH]] +// CHECK: [[INNER_TRY_INVOKE_CONT]]: +// CHECK: br label %[[INNER_CLEANUP:[^ ]+]] +// CHECK: [[INNER_CLEANUP]]: +// CHECK-NEXT: invoke void @[[INNER_FINALLY_FUNCTION:[^ ]+]] +// CHECK-NEXT: to label %[[INNER_CLEANUP_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_FINALLY_DISPATCH]] +// CHECK: [[OUTER_FINALLY_DISPATCH]]: +// CHECK-NEXT: %[[OUTER_FINALLY_CATCHSWITCH:[^ ]+]] = catchswitch within none [label %[[OUTER_FINALLY:[^ ]+]]] unwind to caller +// CHECK: [[INNER_CLEANUP_INVOKE_CONT]]: +// CHECK: catchret from %[[OUTER_CATCH_CATCHPAD]] to label %[[OUTER_CATCH_CATCHRET_DEST:[^ ]+]] +// CHECK: [[OUTER_CATCH_CATCHRET_DEST]]: +// CHECK: br label %[[OUTER_CLEANUP]] +// CHECK: [[INNER_CATCH]]: +// CHECK-NEXT: %[[INNER_CATCH_CATCHPAD:[^ ]+]] = catchpad within %[[INNER_CATCH_CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @g(){{.*}} [ "funclet"(token %[[INNER_CATCH_CATCHPAD]]) ] +// CHECK-NEXT: catchret from %[[INNER_CATCH_CATCHPAD]] to label %[[INNER_CATCH_CATCHRET_DEST:[^ ]+]] +// CHECK: [[INNER_CATCH_CATCHRET_DEST]]: +// CHECK: br label %[[INNER_CLEANUP]] +// CHECK: [[INNER_FINALLY]]: +// CHECK-NEXT: %[[INNER_FINALLY_CATCHPAD:[^ ]+]] = catchpad within %[[INNER_FINALLY_CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: invoke void @[[INNER_FINALLY_FUNCTION]]{{.*}} [ "funclet"(token %[[INNER_FINALLY_CATCHPAD]]) ] +// CHECK-NEXT: to label %[[INNER_FINALLY_INVOKE_CONT:[^ ]+]] unwind label %[[OUTER_FINALLY_DISPATCH]] +// CHECK: [[INNER_FINALLY_INVOKE_CONT]]: +// CHECK-NEXT: invoke void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[INNER_FINALLY_CATCHPAD]]) ] +// CHECK-NEXT: to label %unreachable unwind label %[[OUTER_FINALLY_DISPATCH]] +// CHECK: [[OUTER_FINALLY]]: +// CHECK-NEXT: %[[OUTER_FINALLY_CATCHPAD:[^ ]+]] = catchpad within %[[OUTER_FINALLY_CATCHSWITCH]] [i8* null, i32 64, i8* null] +// CHECK-NEXT: call void @[[OUTER_FINALLY_FUNCTION]]{{.*}} [ "funclet"(token %[[OUTER_FINALLY_CATCHPAD]]) ] +// CHECK-NEXT: call void @_CxxThrowException(i8* null, %eh.ThrowInfo* null){{.*}} [ "funclet"(token %[[OUTER_FINALLY_CATCHPAD]]) ] +// CHECK-NEXT: unreachable + +// CHECK: define internal void @[[INNER_FINALLY_FUNCTION]]{{.*}} { +// CHECK: call void @f() +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define internal void @[[OUTER_FINALLY_FUNCTION]]{{.*}} { +// CHECK: call void @g() +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void empty_try_catch() { + @try { + } @catch (...) { + f(); + } +} + +// CHECK-LABEL: define {{.*}}void @empty_try_catch(){{.*}} { +// CHECK-NEXT: entry: +// CHECK-NEXT: br label %[[EHCONT:[^ ]+]] +// CHECK: [[EHCONT]]: +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +void empty_try_finally() { + @try { + } @finally { + f(); + } +} + +// CHECK-LABEL: define {{.*}}void @empty_try_finally(){{.*}} { +// CHECK-NEXT: entry: +// CHECK-NEXT: %[[CAPTURED:[^ ]+]] = alloca +// CHECK-NEXT: call void @[[FINALLY_FUNCTION:[^ ]+]]({{.*}} %[[CAPTURED]]) +// CHECK-NEXT: ret void +// CHECK-NEXT: } + +// CHECK: define internal void @[[FINALLY_FUNCTION]]{{.*}} { +// CHECK: call void @f() +// CHECK-NEXT: ret void +// CHECK-NEXT: }