diff options
author | Heejin Ahn <aheejin@gmail.com> | 2018-05-31 22:18:13 +0000 |
---|---|---|
committer | Heejin Ahn <aheejin@gmail.com> | 2018-05-31 22:18:13 +0000 |
commit | 23b1d1526478c1b1a3167b28cfc1cd31695a2bf7 (patch) | |
tree | 7b28f886923335d8b42f93b51c7d9589e10da3d3 | |
parent | 5498e63d06e8760e5d1d73754a919f4d6d19886c (diff) | |
download | clang-23b1d1526478c1b1a3167b28cfc1cd31695a2bf7.tar.gz |
[WebAssembly] Use Windows EH instructions for Wasm EH
Summary:
Because wasm control flow needs to be structured, using WinEH
instructions to support wasm EH brings several benefits. This patch
makes wasm EH uses Windows EH instructions, with some changes:
1. Because wasm uses a single catch block to catch all C++ exceptions,
this merges all catch clauses into a single catchpad, within which we
test the EH selector as in Itanium EH.
2. Generates a call to `__clang_call_terminate` in case a cleanup
throws. Wasm does not have a runtime to handle this.
3. In case there is no catch-all clause, inserts a call to
`__cxa_rethrow` at the end of a catchpad in order to unwind to an
enclosing EH scope.
Reviewers: majnemer, dschuff
Subscribers: jfb, sbc100, jgravelle-google, sunfish, cfe-commits
Differential Revision: https://reviews.llvm.org/D44931
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@333703 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/CodeGen/CGCXXABI.h | 11 | ||||
-rw-r--r-- | lib/CodeGen/CGCleanup.cpp | 13 | ||||
-rw-r--r-- | lib/CodeGen/CGCleanup.h | 7 | ||||
-rw-r--r-- | lib/CodeGen/CGException.cpp | 171 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenFunction.h | 3 | ||||
-rw-r--r-- | lib/CodeGen/ItaniumCXXABI.cpp | 8 | ||||
-rw-r--r-- | lib/CodeGen/MicrosoftCXXABI.cpp | 14 | ||||
-rw-r--r-- | test/CodeGenCXX/wasm-eh.cpp | 384 |
8 files changed, 588 insertions, 23 deletions
diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index a1158d926c..65b50e14f4 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -607,6 +607,17 @@ CGCXXABI *CreateItaniumCXXABI(CodeGenModule &CGM); /// 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); + } +}; } } diff --git a/lib/CodeGen/CGCleanup.cpp b/lib/CodeGen/CGCleanup.cpp index c5f935bdef..92745bcdcd 100644 --- a/lib/CodeGen/CGCleanup.cpp +++ b/lib/CodeGen/CGCleanup.cpp @@ -971,16 +971,21 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { SaveAndRestore<llvm::Instruction *> 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) { diff --git a/lib/CodeGen/CGCleanup.h b/lib/CodeGen/CGCleanup.h index 105c5629d5..5530350b9b 100644 --- a/lib/CodeGen/CGCleanup.h +++ b/lib/CodeGen/CGCleanup.h @@ -627,16 +627,21 @@ struct EHPersonality { static const EHPersonality MSVC_except_handler; static const EHPersonality MSVC_C_specific_handler; static const EHPersonality MSVC_CxxFrameHandler3; + static const EHPersonality GNU_Wasm_CPlusPlus; /// 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_CPlusPlus; } + bool isMSVCXXPersonality() const { return this == &MSVC_CxxFrameHandler3; } }; } diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index b37ecaa735..d38de34134 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -111,6 +111,8 @@ const EHPersonality EHPersonality::MSVC_C_specific_handler = { "__C_specific_handler", nullptr }; const EHPersonality EHPersonality::MSVC_CxxFrameHandler3 = { "__CxxFrameHandler3", nullptr }; +const EHPersonality +EHPersonality::GNU_Wasm_CPlusPlus = { "__gxx_wasm_personality_v0", nullptr }; static const EHPersonality &getCPersonality(const llvm::Triple &T, const LangOptions &L) { @@ -161,6 +163,9 @@ static const EHPersonality &getCXXPersonality(const llvm::Triple &T, 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_CPlusPlus; return EHPersonality::GNU_CPlusPlus; } @@ -574,7 +579,7 @@ void CodeGenFunction::EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { llvm::BasicBlock * CodeGenFunction::getEHDispatchBlock(EHScopeStack::stable_iterator si) { if (EHPersonality::get(*this).usesFuncletPads()) - return getMSVCDispatchBlock(si); + return getFuncletEHDispatchBlock(si); // The dispatch block for the end of the scope chain is a block that // just resumes unwinding. @@ -622,7 +627,7 @@ CodeGenFunction::getEHDispatchBlock(EHScopeStack::stable_iterator si) { } llvm::BasicBlock * -CodeGenFunction::getMSVCDispatchBlock(EHScopeStack::stable_iterator SI) { +CodeGenFunction::getFuncletEHDispatchBlock(EHScopeStack::stable_iterator SI) { // Returning nullptr indicates that the previous dispatch block should unwind // to caller. if (SI == EHStack.stable_end()) @@ -916,10 +921,121 @@ static void emitCatchPadBlock(CodeGenFunction &CGF, EHCatchScope &CatchScope) { 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 *WasmCatchStartBlock = CGF.createBasicBlock("catch.start"); + CatchSwitch->addHandler(WasmCatchStartBlock); + CGF.EmitBlockAfterUses(WasmCatchStartBlock); + + // Create a catchpad instruction. + SmallVector<llvm::Value *, 4> 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); + } + auto *CPI = 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, CPI); + CGF.Builder.CreateStore(Exn, CGF.getExceptionSlot()); + llvm::CallInst *Selector = CGF.Builder.CreateCall(GetSelectorFn, CPI); + + 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); @@ -1007,6 +1123,7 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { unsigned NumHandlers = S.getNumHandlers(); EHCatchScope &CatchScope = cast<EHCatchScope>(*EHStack.begin()); assert(CatchScope.getNumHandlers() == NumHandlers); + llvm::BasicBlock *DispatchBlock = CatchScope.getCachedEHDispatchBlock(); // If the catch was not required, bail out now. if (!CatchScope.hasEHBranches()) { @@ -1039,6 +1156,22 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { doImplicitRethrow = isa<CXXDestructorDecl>(CurCodeDecl) || isa<CXXConstructorDecl>(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<llvm::Instruction *> RestoreCurrentFuncletPad( + CurrentFuncletPad); + llvm::BasicBlock *WasmCatchStartBlock = nullptr; + if (EHPersonality::get(*this).isWasmPersonality()) { + auto *CatchSwitch = + cast<llvm::CatchSwitchInst>(DispatchBlock->getFirstNonPHI()); + WasmCatchStartBlock = CatchSwitch->hasUnwindDest() + ? CatchSwitch->getSuccessor(1) + : CatchSwitch->getSuccessor(0); + auto *CPI = cast<llvm::CatchPadInst>(WasmCatchStartBlock->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 @@ -1046,7 +1179,9 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { // 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); @@ -1091,6 +1226,27 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { 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(WasmCatchStartBlock); + // 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 = WasmCatchStartBlock; + while (llvm::TerminatorInst *TI = RethrowBlock->getTerminator()) { + auto *BI = cast<llvm::BranchInst>(TI); + assert(BI->isConditional()); + RethrowBlock = BI->getSuccessor(1); + } + assert(RethrowBlock != WasmCatchStartBlock && RethrowBlock->empty()); + Builder.SetInsertPoint(RethrowBlock); + CGM.getCXXABI().emitRethrow(*this, /*isNoReturn=*/true); + } + EmitBlock(ContBB); incrementProfileCounter(&S); } @@ -1369,8 +1525,17 @@ llvm::BasicBlock *CodeGenFunction::getTerminateFunclet() { 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, CurrentFuncletPad); + } llvm::CallInst *terminateCall = - CGM.getCXXABI().emitTerminateForUnexpectedException(*this, nullptr); + CGM.getCXXABI().emitTerminateForUnexpectedException(*this, Exn); terminateCall->setDoesNotReturn(); Builder.CreateUnreachable(); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 56b07e5f85..3ee66b007b 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -877,7 +877,8 @@ public: llvm::BasicBlock *getEHResumeBlock(bool isCleanup); llvm::BasicBlock *getEHDispatchBlock(EHScopeStack::stable_iterator scope); - llvm::BasicBlock *getMSVCDispatchBlock(EHScopeStack::stable_iterator scope); + llvm::BasicBlock * + getFuncletEHDispatchBlock(EHScopeStack::stable_iterator scope); /// An object to manage conditionally-evaluated expressions. class ConditionalEvaluation { diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 86536cdc06..2a3564a4f5 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -469,6 +469,7 @@ public: 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 { @@ -4098,3 +4099,10 @@ ItaniumCXXABI::LoadVTablePtr(CodeGenFunction &CGF, Address This, const CXXRecordDecl *RD) { return {CGF.GetVTablePtr(This, CGM.Int8PtrTy, RD), RD}; } + +void WebAssemblyCXXABI::emitBeginCatch(CodeGenFunction &CGF, + const CXXCatchStmt *C) { + CGF.EHStack.pushCleanup<CatchRetScope>( + NormalCleanup, cast<llvm::CatchPadInst>(CGF.CurrentFuncletPad)); + ItaniumCXXABI::emitBeginCatch(CGF, C); +} diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index a8260b49ed..a58982bd0c 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -859,20 +859,6 @@ void MicrosoftCXXABI::emitRethrow(CodeGenFunction &CGF, bool isNoReturn) { 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 diff --git a/test/CodeGenCXX/wasm-eh.cpp b/test/CodeGenCXX/wasm-eh.cpp new file mode 100644 index 0000000000..7fb4d38f0b --- /dev/null +++ b/test/CodeGenCXX/wasm-eh.cpp @@ -0,0 +1,384 @@ +// 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_wasm_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(token %[[CATCHPAD]]) +// CHECK-NEXT: store i8* %[[EXN]], i8** %exn.slot +// CHECK-NEXT: %[[SELECTOR:.*]] = call i32 @llvm.wasm.get.ehselector(token %[[CATCHPAD]]) +// 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(token %[[CLEANUPPAD1]]) +// 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 within a try 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 + +// Nested try-catches within a catch +void test8() { + try { + may_throw(); + } catch (int) { + try { + may_throw(); + } catch (int) { + may_throw(); + } + } +} + +// CHECK-LABEL: @_Z5test8v() +// CHECK: invoke void @_Z9may_throwv() + +// CHECK: %[[CATCHSWITCH0:.*]] = catchswitch within none + +// CHECK: %[[CATCHPAD0:.*]] = catchpad within %[[CATCHSWITCH0]] [i8* bitcast (i8** @_ZTIi to i8*)] + +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD0]]) ] + +// CHECK: %[[CATCHSWITCH1:.*]] = catchswitch within %[[CATCHPAD0]] + +// CHECK: %[[CATCHPAD1:.*]] = catchpad within %[[CATCHSWITCH1]] [i8* bitcast (i8** @_ZTIi to i8*)] + +// CHECK: invoke void @_Z9may_throwv() [ "funclet"(token %[[CATCHPAD1]]) ] + +// CHECK: catchret from %[[CATCHPAD1]] to label + +// CHECK: invoke void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD1]]) ] + +// CHECK: catchret from %[[CATCHPAD0]] to label + +// CHECK: call void @__cxa_rethrow() {{.*}} [ "funclet"(token %[[CATCHPAD0]]) ] +// CHECK: unreachable + +// CHECK: %[[CLEANUPPAD0:.*]] = cleanuppad within %[[CATCHPAD1]] [] +// CHECK: cleanupret from %[[CLEANUPPAD0]] unwind label + +// CHECK: %[[CLEANUPPAD1:.*]] = cleanuppad within %[[CATCHPAD0]] [] +// CHECK: cleanupret from %[[CLEANUPPAD1]] unwind to caller + +// CHECK: unreachable |