Index: lib/CodeGen/CGCXXABI.h =================================================================== --- lib/CodeGen/CGCXXABI.h +++ lib/CodeGen/CGCXXABI.h @@ -607,6 +607,18 @@ /// Creates a Microsoft-family ABI. CGCXXABI *CreateMicrosoftCXXABI(CodeGenModule &CGM); +struct CatchRetScope final : EHScopeStack::Cleanup { + llvm::CatchPadInst *CPI; + + CatchRetScope(llvm::CatchPadInst *CPI) : CPI(CPI) {} + + void Emit(CodeGenFunction &CGF, Flags flags) override { + llvm::BasicBlock *BB = CGF.createBasicBlock("catchret.dest"); + CGF.Builder.CreateCatchRet(CPI, BB); + CGF.EmitBlock(BB); + } +}; + } } Index: lib/CodeGen/CGCleanup.h =================================================================== --- lib/CodeGen/CGCleanup.h +++ lib/CodeGen/CGCleanup.h @@ -627,16 +627,21 @@ static const EHPersonality MSVC_except_handler; static const EHPersonality MSVC_C_specific_handler; static const EHPersonality MSVC_CxxFrameHandler3; + static const EHPersonality GNU_Wasm_CPlusCPlus; /// Does this personality use landingpads or the family of pad instructions /// designed to form funclets? - bool usesFuncletPads() const { return isMSVCPersonality(); } + bool usesFuncletPads() const { + return isMSVCPersonality() || isWasmPersonality(); + } bool isMSVCPersonality() const { return this == &MSVC_except_handler || this == &MSVC_C_specific_handler || this == &MSVC_CxxFrameHandler3; } + bool isWasmPersonality() const { return this == &GNU_Wasm_CPlusCPlus; } + bool isMSVCXXPersonality() const { return this == &MSVC_CxxFrameHandler3; } }; } Index: lib/CodeGen/CGCleanup.cpp =================================================================== --- lib/CodeGen/CGCleanup.cpp +++ lib/CodeGen/CGCleanup.cpp @@ -971,16 +971,21 @@ SaveAndRestore RestoreCurrentFuncletPad( CurrentFuncletPad); llvm::CleanupPadInst *CPI = nullptr; - if (!EHPersonality::get(*this).usesFuncletPads()) { - EHStack.pushTerminate(); - PushedTerminate = true; - } else { + + const EHPersonality &Personality = EHPersonality::get(*this); + if (Personality.usesFuncletPads()) { llvm::Value *ParentPad = CurrentFuncletPad; if (!ParentPad) ParentPad = llvm::ConstantTokenNone::get(CGM.getLLVMContext()); CurrentFuncletPad = CPI = Builder.CreateCleanupPad(ParentPad); } + // Non-MSVC personalities need to terminate when an EH cleanup throws. + if (!Personality.isMSVCPersonality()) { + EHStack.pushTerminate(); + PushedTerminate = true; + } + // We only actually emit the cleanup code if the cleanup is either // active or was used before it was deactivated. if (EHActiveFlag.isValid() || IsActive) { Index: lib/CodeGen/CGException.cpp =================================================================== --- lib/CodeGen/CGException.cpp +++ lib/CodeGen/CGException.cpp @@ -111,6 +111,8 @@ EHPersonality::MSVC_C_specific_handler = { "__C_specific_handler", nullptr }; const EHPersonality EHPersonality::MSVC_CxxFrameHandler3 = { "__CxxFrameHandler3", nullptr }; +const EHPersonality +EHPersonality::GNU_Wasm_CPlusCPlus = { "__gxx_personality_v0", nullptr }; static const EHPersonality &getCPersonality(const llvm::Triple &T, const LangOptions &L) { @@ -161,6 +163,8 @@ return EHPersonality::MSVC_CxxFrameHandler3; if (L.SEHExceptions) return EHPersonality::GNU_CPlusPlus_SEH; + if (T.getArch() == llvm::Triple::wasm32 || T.getArch() == llvm::Triple::wasm64) + return EHPersonality::GNU_Wasm_CPlusCPlus; return EHPersonality::GNU_CPlusPlus; } @@ -920,10 +924,121 @@ CGF.Builder.restoreIP(SavedIP); } +// Wasm uses Windows-style EH instructions, but it merges all catch clauses into +// one big catchpad, within which we use Itanium's landingpad-style selector +// comparison instructions. +static void emitWasmCatchPadBlock(CodeGenFunction &CGF, + EHCatchScope &CatchScope) { + llvm::BasicBlock *DispatchBlock = CatchScope.getCachedEHDispatchBlock(); + assert(DispatchBlock); + + CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveIP(); + CGF.EmitBlockAfterUses(DispatchBlock); + + llvm::Value *ParentPad = CGF.CurrentFuncletPad; + if (!ParentPad) + ParentPad = llvm::ConstantTokenNone::get(CGF.getLLVMContext()); + llvm::BasicBlock *UnwindBB = + CGF.getEHDispatchBlock(CatchScope.getEnclosingEHScope()); + + unsigned NumHandlers = CatchScope.getNumHandlers(); + llvm::CatchSwitchInst *CatchSwitch = + CGF.Builder.CreateCatchSwitch(ParentPad, UnwindBB, NumHandlers); + + // We don't use a landingpad instruction, so generate intrinsic calls to + // provide exception and selector values. + llvm::BasicBlock *CatchStartBlock = CGF.createBasicBlock("catch.start"); + CatchSwitch->addHandler(CatchStartBlock); + CGF.EmitBlockAfterUses(CatchStartBlock); + + // Create a catchpad instruction. + SmallVector CatchTypes; + for (unsigned I = 0, E = NumHandlers; I < E; ++I) { + const EHCatchScope::Handler &Handler = CatchScope.getHandler(I); + CatchTypeInfo TypeInfo = Handler.Type; + if (!TypeInfo.RTTI) + TypeInfo.RTTI = llvm::Constant::getNullValue(CGF.VoidPtrTy); + CatchTypes.push_back(TypeInfo.RTTI); + } + CGF.Builder.CreateCatchPad(CatchSwitch, CatchTypes); + + // Create calls to wasm.get.exception and wasm.get.ehselector intrinsics. + // Before they are lowered appropriately later, they provide values for the + // exception and selector. + llvm::Value *GetExnFn = + CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_get_exception); + llvm::Value *GetSelectorFn = + CGF.CGM.getIntrinsic(llvm::Intrinsic::wasm_get_ehselector); + llvm::CallInst *Exn = CGF.Builder.CreateCall(GetExnFn); + CGF.Builder.CreateStore(Exn, CGF.getExceptionSlot()); + llvm::CallInst *Selector = CGF.Builder.CreateCall(GetSelectorFn); + + llvm::Value *TypeIDFn = CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_typeid_for); + + // If there's only a single catch-all, branch directly to its handler. + if (CatchScope.getNumHandlers() == 1 && + CatchScope.getHandler(0).isCatchAll()) { + CGF.Builder.CreateBr(CatchScope.getHandler(0).Block); + CGF.Builder.restoreIP(SavedIP); + return; + } + + // Test against each of the exception types we claim to catch. + for (unsigned I = 0, E = NumHandlers;; ++I) { + assert(I < E && "ran off end of handlers!"); + const EHCatchScope::Handler &Handler = CatchScope.getHandler(I); + CatchTypeInfo TypeInfo = Handler.Type; + if (!TypeInfo.RTTI) + TypeInfo.RTTI = llvm::Constant::getNullValue(CGF.VoidPtrTy); + + // Figure out the next block. + llvm::BasicBlock *NextBlock; + + bool EmitNextBlock = false, NextIsEnd = false; + + // If this is the last handler, we're at the end, and the next block is a + // block that contains a call to the rethrow function, so we can unwind to + // the enclosing EH scope. The call itself will be generated later. + if (I + 1 == E) { + NextBlock = CGF.createBasicBlock("rethrow"); + EmitNextBlock = true; + NextIsEnd = true; + + // If the next handler is a catch-all, we're at the end, and the + // next block is that handler. + } else if (CatchScope.getHandler(I + 1).isCatchAll()) { + NextBlock = CatchScope.getHandler(I + 1).Block; + NextIsEnd = true; + + // Otherwise, we're not at the end and we need a new block. + } else { + NextBlock = CGF.createBasicBlock("catch.fallthrough"); + EmitNextBlock = true; + } + + // Figure out the catch type's index in the LSDA's type table. + llvm::CallInst *TypeIndex = CGF.Builder.CreateCall(TypeIDFn, TypeInfo.RTTI); + TypeIndex->setDoesNotThrow(); + + llvm::Value *MatchesTypeIndex = + CGF.Builder.CreateICmpEQ(Selector, TypeIndex, "matches"); + CGF.Builder.CreateCondBr(MatchesTypeIndex, Handler.Block, NextBlock); + + if (EmitNextBlock) + CGF.EmitBlock(NextBlock); + if (NextIsEnd) + break; + } + + CGF.Builder.restoreIP(SavedIP); +} + /// Emit the structure of the dispatch block for the given catch scope. /// It is an invariant that the dispatch block already exists. static void emitCatchDispatchBlock(CodeGenFunction &CGF, EHCatchScope &catchScope) { + if (EHPersonality::get(CGF).isWasmPersonality()) + return emitWasmCatchPadBlock(CGF, catchScope); if (EHPersonality::get(CGF).usesFuncletPads()) return emitCatchPadBlock(CGF, catchScope); @@ -1011,6 +1126,7 @@ unsigned NumHandlers = S.getNumHandlers(); EHCatchScope &CatchScope = cast(*EHStack.begin()); assert(CatchScope.getNumHandlers() == NumHandlers); + llvm::BasicBlock *DispatchBlock = CatchScope.getCachedEHDispatchBlock(); // If the catch was not required, bail out now. if (!CatchScope.hasEHBranches()) { @@ -1043,6 +1159,23 @@ doImplicitRethrow = isa(CurCodeDecl) || isa(CurCodeDecl); + // Wasm uses Windows-style EH instructions, but merges all catch clauses into + // one big catchpad. So we save the old funclet pad here before we traverse + // each catch handler. + SaveAndRestore RestoreCurrentFuncletPad( + CurrentFuncletPad); + llvm::BasicBlock *CatchStartBlock = nullptr; + if (EHPersonality::get(*this).isWasmPersonality()) { + llvm::CatchSwitchInst *CatchSwitch = + cast(DispatchBlock->getFirstNonPHI()); + CatchStartBlock = CatchSwitch->hasUnwindDest() + ? CatchSwitch->getSuccessor(1) + : CatchSwitch->getSuccessor(0); + llvm::CatchPadInst *CPI = + cast(CatchStartBlock->getFirstNonPHI()); + CurrentFuncletPad = CPI; + } + // Perversely, we emit the handlers backwards precisely because we // want them to appear in source order. In all of these cases, the // catch block will have exactly one predecessor, which will be a @@ -1050,7 +1183,9 @@ // a catch-all, one of the dispatch blocks will branch to two // different handlers, and EmitBlockAfterUses will cause the second // handler to be moved before the first. + bool HasCatchAll = false; for (unsigned I = NumHandlers; I != 0; --I) { + HasCatchAll |= Handlers[I-1].isCatchAll(); llvm::BasicBlock *CatchBlock = Handlers[I-1].Block; EmitBlockAfterUses(CatchBlock); @@ -1095,6 +1230,27 @@ Builder.CreateBr(ContBB); } + // Because in wasm we merge all catch clauses into one big catchpad, in case + // none of the types in catch handlers matches after we test against each of + // them, we should unwind to the next EH enclosing scope. We generate a call + // to rethrow function here to do that. + if (EHPersonality::get(*this).isWasmPersonality() && !HasCatchAll) { + assert(CatchStartBlock); + // Navigate for the "rethrow" block we created in emitWasmCatchPadBlock(). + // Wasm uses landingpad-style conditional branches to compare selectors, so + // we follow the false destination for each of the cond branches to reach + // the rethrow block. + llvm::BasicBlock *RethrowBlock = CatchStartBlock; + while (llvm::TerminatorInst *TI = RethrowBlock->getTerminator()) { + llvm::BranchInst *BI = cast(TI); + assert(BI->isConditional()); + RethrowBlock = BI->getSuccessor(1); + } + assert(RethrowBlock != CatchStartBlock && RethrowBlock->empty()); + Builder.SetInsertPoint(RethrowBlock); + CGM.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true); + } + EmitBlock(ContBB); incrementProfileCounter(&S); } @@ -1373,8 +1529,17 @@ CurrentFuncletPad = Builder.CreateCleanupPad(ParentPad); // Emit the __std_terminate call. + llvm::Value *Exn = nullptr; + // In case of wasm personality, we need to pass the exception value to + // __clang_call_terminate function. + if (getLangOpts().CPlusPlus && + EHPersonality::get(*this).isWasmPersonality()) { + llvm::Value *GetExnFn = + CGM.getIntrinsic(llvm::Intrinsic::wasm_get_exception); + Exn = Builder.CreateCall(GetExnFn); + } llvm::CallInst *terminateCall = - CGM.getCXXABI().emitTerminateForUnexpectedException(*this, nullptr); + CGM.getCXXABI().emitTerminateForUnexpectedException(*this, Exn); terminateCall->setDoesNotReturn(); Builder.CreateUnreachable(); Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -468,6 +468,7 @@ explicit WebAssemblyCXXABI(CodeGen::CodeGenModule &CGM) : ItaniumCXXABI(CGM, /*UseARMMethodPtrABI=*/true, /*UseARMGuardVarABI=*/true) {} + void emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *C) override; private: bool HasThisReturn(GlobalDecl GD) const override { @@ -4043,3 +4044,10 @@ const CXXRecordDecl *RD) { return {CGF.GetVTablePtr(This, CGM.Int8PtrTy, RD), RD}; } + +void WebAssemblyCXXABI::emitBeginCatch(CodeGenFunction &CGF, + const CXXCatchStmt *C) { + CGF.EHStack.pushCleanup( + NormalCleanup, cast(CGF.CurrentFuncletPad)); + ItaniumCXXABI::emitBeginCatch(CGF, C); +} Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -861,20 +861,6 @@ CGF.EmitRuntimeCallOrInvoke(Fn, Args); } -namespace { -struct CatchRetScope final : EHScopeStack::Cleanup { - llvm::CatchPadInst *CPI; - - CatchRetScope(llvm::CatchPadInst *CPI) : CPI(CPI) {} - - void Emit(CodeGenFunction &CGF, Flags flags) override { - llvm::BasicBlock *BB = CGF.createBasicBlock("catchret.dest"); - CGF.Builder.CreateCatchRet(CPI, BB); - CGF.EmitBlock(BB); - } -}; -} - void MicrosoftCXXABI::emitBeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *S) { // In the MS ABI, the runtime handles the copy, and the catch handler is Index: test/CodeGenCXX/wasm-eh.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/wasm-eh.cpp @@ -0,0 +1,346 @@ +// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -emit-llvm -o - -std=c++11 | FileCheck %s +// RUN: %clang_cc1 %s -triple wasm64-unknown-unknown -fms-extensions -fexceptions -fcxx-exceptions -emit-llvm -o - -std=c++11 | FileCheck %s + +void may_throw(); +void dont_throw() noexcept; + +struct Cleanup { + ~Cleanup() { dont_throw(); } +}; + +// Multiple catch clauses w/o catch-all +void test0() { + try { + may_throw(); + } catch (int) { + dont_throw(); + } catch (double) { + dont_throw(); + } +} + +// CHECK-LABEL: define void @_Z5test0v() {{.*}} personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) + +// CHECK: %[[INT_ALLOCA:.*]] = alloca i32 +// CHECK: invoke void @_Z9may_throwv() +// CHECK-NEXT: to label %[[NORMAL_BB:.*]] unwind label %[[CATCHDISPATCH_BB:.*]] + +// CHECK: [[CATCHDISPATCH_BB]]: +// CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller + +// CHECK: [[CATCHSTART_BB]]: +// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] +// CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception() +// CHECK-NEXT: store i8* %[[EXN]], i8** %exn.slot +// CHECK-NEXT: %[[SELECTOR:.*]] = call i32 @llvm.wasm.get.ehselector() +// CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #2 +// CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]] +// CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_INT_BB:.*]], label %[[CATCH_FALLTHROUGH_BB:.*]] + +// CHECK: [[CATCH_INT_BB]]: +// CHECK-NEXT: %[[EXN:.*]] = load i8*, i8** %exn.slot +// CHECK-NEXT: %[[ADDR:.*]] = call i8* @__cxa_begin_catch(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: %[[ADDR_CAST:.*]] = bitcast i8* %[[ADDR]] to i32* +// CHECK-NEXT: %[[INT_VAL:.*]] = load i32, i32* %[[ADDR_CAST]] +// CHECK-NEXT: store i32 %[[INT_VAL]], i32* %[[INT_ALLOCA]] +// CHECK-NEXT: call void @_Z10dont_throwv() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB0:.*]] + +// CHECK: [[CATCHRET_DEST_BB0]]: +// CHECK-NEXT: br label %[[TRY_CONT_BB:.*]] + +// CHECK: [[CATCH_FALLTHROUGH_BB]] +// CHECK-NEXT: %[[TYPEID:.*]] = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTId to i8*)) #2 +// CHECK-NEXT: %[[MATCHES:.*]] = icmp eq i32 %[[SELECTOR]], %[[TYPEID]] +// CHECK-NEXT: br i1 %[[MATCHES]], label %[[CATCH_FLOAT_BB:.*]], label %[[RETHROW_BB:.*]] + +// CHECK: [[CATCH_FLOAT_BB]]: +// CHECK: catchret from %[[CATCHPAD]] to label %[[CATCHRET_DEST_BB1:.*]] + +// CHECK: [[CATCHRET_DEST_BB1]]: +// CHECK-NEXT: br label %[[TRY_CONT_BB]] + +// CHECK: [[RETHROW_BB]]: +// CHECK-NEXT: call void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: unreachable + + +// Single catch-all +void test1() { + try { + may_throw(); + } catch (...) { + dont_throw(); + } +} + +// CATCH-LABEL: @_Z5test1v() + +// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller + +// CHECK: [[CATCHSTART_BB]]: +// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null] +// CHECK: br label %[[CATCH_ALL_BB:.*]] + +// CHECK: [[CATCH_ALL_BB]]: +// CHECK: catchret from %[[CATCHPAD]] to label + + +// Multiple catch clauses w/ catch-all +void test2() { + try { + may_throw(); + } catch (int) { + dont_throw(); + } catch (...) { + dont_throw(); + } +} + +// CHECK-LABEL: @_Z5test2v() + +// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind to caller + +// CHECK: [[CATCHSTART_BB]]: +// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null] +// CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[CATCH_ALL_BB:.*]] + +// CHECK: [[CATCH_INT_BB]]: +// CHECK: catchret from %[[CATCHPAD]] to label + +// CHECK: [[CATCH_ALL_BB]]: +// CHECK: catchret from %[[CATCHPAD]] to label + + +// Cleanup +void test3() { + Cleanup c; + may_throw(); +} + +// CHECK-LABEL: @_Z5test3v() + +// CHECK: invoke void @_Z9may_throwv() +// CHECK-NEXT: to label {{.*}} unwind label %[[EHCLEANUP_BB:.*]] + +// CHECK: [[EHCLEANUP_BB]]: +// CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within none [] +// CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ] +// CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller + + +// Possibly throwing function call within a catch +void test4() { + try { + may_throw(); + } catch (int) { + may_throw(); + } +} + +// CHECK-LABEL: @_Z5test4v() + +// CHECK: %[[CATCHSWITCH]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller + +// CHECK: [[CATCHSTART_BB]]: +// CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)] + +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB:.*]] + +// CHECK: [[INVOKE_CONT_BB]]: +// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD]] to label + +// CHECK: [[EHCLEANUP_BB]]: +// CHECK-NEXT: %[[CLEANUPPAD:.*]] = cleanuppad within %[[CATCHPAD]] [] +// CHECK-NEXT: call void @__cxa_end_catch() {{.*}} [ "funclet"(token %[[CLEANUPPAD]]) ] +// CHECK-NEXT: cleanupret from %[[CLEANUPPAD]] unwind to caller + + +// Possibly throwing function call within a catch-all +void test5() { + try { + may_throw(); + } catch (...) { + may_throw(); + } +} + +// CHECK-LABEL: @_Z5test5v() + +// CHECK: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB]]] unwind to caller + +// CHECK: [[CATCHSTART_BB]]: +// CHECK: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* null] + +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: to label %[[INVOKE_CONT_BB0:.*]] unwind label %[[EHCLEANUP_BB:.*]] + +// CHECK: [[INVOKE_CONT_BB0]]: +// CHECK-NEXT: call void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: catchret from %[[CATCHPAD]] to label + +// CHECK: [[EHCLEANUP_BB]]: +// CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD]] [] +// CHECK-NEXT: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD0]]) ] +// CHECK-NEXT: to label %[[INVOKE_CONT_BB1:.*]] unwind label %[[TERMINATE_BB:.*]] + +// CHECK: [[INVOKE_CONT_BB1]]: +// CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind to caller + +// CHECK: [[TERMINATE_BB]]: +// CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CLEANUPPAD0]] [] +// CHECK-NEXT: %[[EXN:.*]] = call i8* @llvm.wasm.get.exception() +// CHECK-NEXT: call void @__clang_call_terminate(i8* %[[EXN]]) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ] +// CHECK-NEXT: unreachable + +// CHECK-LABEL: define {{.*}} void @__clang_call_terminate(i8*) +// CHECK-NEXT: call i8* @__cxa_begin_catch(i8* %{{.*}}) +// CHECK-NEXT: call void @_ZSt9terminatev() +// CHECK-NEXT: unreachable + + +// Try-catch with cleanups +void test6() { + Cleanup c1; + try { + Cleanup c2; + may_throw(); + } catch (int) { + Cleanup c3; + may_throw(); + } +} + +// CHECK-LABEL: @_Z5test6v() +// CHECK: invoke void @_Z9may_throwv() +// CHECK-NEXT: to label %{{.*}} unwind label %[[EHCLEANUP_BB0:.*]] + +// CHECK: [[EHCLEANUP_BB0]]: +// CHECK-NEXT: %[[CLEANUPPAD0:.*]] = cleanuppad within none [] +// CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* {{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD0]]) ] +// CHECK-NEXT: cleanupret from %[[CLEANUPPAD0]] unwind label %[[CATCH_DISPATCH_BB:.*]] + +// CHECK: [[CATCH_DISPATCH_BB]]: +// CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCHSTART_BB:.*]]] unwind label %[[EHCLEANUP_BB1:.*]] + +// CHECK: [[CATCHSTART_BB]]: +// CHECK-NEXT: %[[CATCHPAD:.*]] = catchpad within %[[CATCHSWITCH]] [i8* bitcast (i8** @_ZTIi to i8*)] +// CHECK: br i1 %{{.*}}, label %[[CATCH_INT_BB:.*]], label %[[RETHROW_BB:.*]] + +// CHECK: [[CATCH_INT_BB]]: +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: to label %[[INVOKE_CONT_BB:.*]] unwind label %[[EHCLEANUP_BB2:.*]] + +// CHECK: [[INVOKE_CONT_BB]]: +// CHECK: catchret from %[[CATCHPAD]] to label %{{.*}} + +// CHECK: [[RETHROW_BB]]: +// CHECK-NEXT: invoke void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD]]) ] +// CHECK-NEXT: to label %[[UNREACHABLE_BB:.*]] unwind label %[[EHCLEANUP_BB1:.*]] + +// CHECK: [[EHCLEANUP_BB2]]: +// CHECK-NEXT: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD]] [] +// CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD2]]) ] +// CHECK-NEXT: cleanupret from %[[CLEANUPPAD2]] unwind label %[[EHCLEANUP_BB3:.*]] + +// CHECK: [[EHCLEANUP_BB3]]: +// CHECK-NEXT: %[[CLEANUPPAD3:.*]] = cleanuppad within %[[CATCHPAD]] [] +// CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label %[[EHCLEANUP_BB1:.*]] + +// CHECK: [[EHCLEANUP_BB1]]: +// CHECK-NEXT: %[[CLEANUPPAD1:.*]] = cleanuppad within none [] +// CHECK-NEXT: call %struct.Cleanup* @_ZN7CleanupD1Ev(%struct.Cleanup* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD1]]) ] +// CHECK-NEXT: cleanupret from %[[CLEANUPPAD1]] unwind to caller + +// CHECK: [[UNREACHABLE_BB]]: +// CHECK-NEXT: unreachable + + +// Nested try-catches with cleanups +void test7() { + Cleanup c1; + may_throw(); + try { + Cleanup c2; + may_throw(); + try { + Cleanup c3; + may_throw(); + } catch (int) { + may_throw(); + } catch (double) { + may_throw(); + } + } catch (int) { + may_throw(); + } catch (...) { + may_throw(); + } +} + +// CHECK-LABEL: @_Z5test7v() +// CHECK: invoke void @_Z9may_throwv() + +// CHECK: invoke void @_Z9may_throwv() + +// CHECK: invoke void @_Z9may_throwv() + +// CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within none [] +// CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label + +// CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none + +// CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*), i8* bitcast (i8** @_ZTId to i8*)] + +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] + +// CHECK: catchret from %[[CATCHPAD0]] to label + +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] + +// CHECK: catchret from %[[CATCHPAD0]] to label + +// CHECK: invoke void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ] + +// CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] [] +// CHECK: cleanupret from %[[CLEANUPPAD1]] unwind label + +// CHECK: %[[CLEANUPPAD2:.*]] = cleanuppad within %[[CATCHPAD0]] [] +// CHECK: cleanupret from %[[CLEANUPPAD2]] unwind label + +// CHECK: %[[CLEANUPPAD3:.*]] = cleanuppad within none [] +// CHECK: cleanupret from %[[CLEANUPPAD3]] unwind label + +// CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within none + +// CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*), i8* null] + +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] + +// CHECK: catchret from %[[CATCHPAD1]] to label + +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] + +// CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CATCHPAD1]]) ] + +// CHECK: catchret from %[[CATCHPAD1]] to label + +// CHECK: %[[CLEANUPPAD4:.*]] = cleanuppad within %[[CATCHPAD1]] [] +// CHECK: invoke void @__cxa_end_catch() [ "funclet"(token %[[CLEANUPPAD4]]) ] + +// CHECK: cleanupret from %[[CLEANUPPAD4]] unwind label + +// CHECK: %[[CLEANUPPAD5:.*]] = cleanuppad within %[[CATCHPAD1]] [] +// CHECK: cleanupret from %[[CLEANUPPAD5]] unwind label + +// CHECK: %[[CLEANUPPAD6:.*]] = cleanuppad within none [] +// CHECK: cleanupret from %[[CLEANUPPAD6]] unwind to caller + +// CHECK: unreachable + +// CHECK: %[[CLEANUPPAD7:.*]] = cleanuppad within %[[CLEANUPPAD4]] [] +// CHECK: call void @__clang_call_terminate(i8* %{{.*}}) {{.*}} [ "funclet"(token %[[CLEANUPPAD7]]) ] +// CHECK: unreachable