https://github.com/HendrikHuebner created https://github.com/llvm/llvm-project/pull/215562
This PR adds WebAssembly exception handling support for Objective-C/C++ `@try`/`@catch`. `@finally` will be implemented in a subsequent patch. This PR is based on #183753 and was co-authored by @hmelder. I addressed the review comments on the old PR, fixed an issue and added more tests. The FileCheck assertions were generated with AI assistance. From d8c68edc92522a33caf5087addb7df7fd6bb8938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]> Date: Thu, 6 Aug 2026 15:51:43 +0200 Subject: [PATCH 1/3] [CodeGen][ObjC] Use C++-based EH for WASM targets The Wasm EH implementation in Clang pretty much hard-codes __gxx_wasm_personality_v0 by calling the veneer function _Unwind_CallPersonality instead of calling the personality function directly. While it is possible to remove _Unwind_CallPersonality and instead generate its body in CG, this will add a couple of instructions in each catch block. Doable, but we can also do the following: Since we already have C++-based EH for MinGW in CGObjCGNU, reusing it for Wasm saves us from implementing our own personality function and objc_begin_catch/objc_end_catch functions. Co-authored-by: hmelder <[email protected]> --- clang/lib/CodeGen/CGException.cpp | 52 ++++++---- clang/lib/CodeGen/CGObjCGNU.cpp | 12 +-- clang/lib/CodeGen/CGObjCRuntime.cpp | 105 ++++++++++++++++++-- clang/lib/CodeGen/CodeGenFunction.h | 10 +- clang/lib/Driver/ToolChains/Clang.cpp | 3 +- clang/test/CodeGenObjC/gnustep2-wasm32-eh.m | 35 +++++++ 6 files changed, 177 insertions(+), 40 deletions(-) create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-eh.m diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index b0fb3b4d85d15..83f1e3dd29e35 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -161,6 +161,8 @@ static const EHPersonality &getObjCPersonality(const TargetInfo &Target, case ObjCRuntime::GNUstep: if (T.isOSCygMing()) return EHPersonality::GNU_CPlusPlus_SEH; + else if (T.isWasm()) + return EHPersonality::GNU_Wasm_CPlusPlus; else if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7)) return EHPersonality::GNUstep_ObjC; [[fallthrough]]; @@ -200,7 +202,8 @@ static const EHPersonality &getCXXPersonality(const TargetInfo &Target, static const EHPersonality &getObjCXXPersonality(const TargetInfo &Target, const CodeGenOptions &CGOpts, const LangOptions &L) { - if (Target.getTriple().isWindowsMSVCEnvironment()) + auto Triple = Target.getTriple(); + if (Triple.isWindowsMSVCEnvironment()) return EHPersonality::MSVC_CxxFrameHandler3; switch (L.ObjCRuntime.getKind()) { @@ -218,8 +221,12 @@ static const EHPersonality &getObjCXXPersonality(const TargetInfo &Target, return getObjCPersonality(Target, CGOpts, L); case ObjCRuntime::GNUstep: - return Target.getTriple().isOSCygMing() ? EHPersonality::GNU_CPlusPlus_SEH - : EHPersonality::GNU_ObjCXX; + if (Triple.isWasm()) + return EHPersonality::GNU_Wasm_CPlusPlus; + else if (Triple.isOSCygMing()) + return EHPersonality::GNU_CPlusPlus_SEH; + else + return EHPersonality::GNU_ObjCXX; // The GCC runtime's personality function inherently doesn't support // mixed EH. Use the ObjC personality just to avoid returning null. @@ -1207,11 +1214,30 @@ static void emitCatchDispatchBlock(CodeGenFunction &CGF, } } -void CodeGenFunction::popCatchScope() { +llvm::BasicBlock *CodeGenFunction::popCatchScope() { EHCatchScope &catchScope = cast<EHCatchScope>(*EHStack.begin()); + llvm::BasicBlock *dispatchBlock = catchScope.getCachedEHDispatchBlock(); if (catchScope.hasEHBranches()) emitCatchDispatchBlock(*this, catchScope); EHStack.popCatch(); + return dispatchBlock; +} + +void CodeGenFunction::WasmEmitFallthroughRethrow( + llvm::BasicBlock *WasmCatchStartBlock) { + assert(WasmCatchStartBlock); + // Navigate for the "rethrow" block. For CXX exceptions this was 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::Instruction *TI = RethrowBlock->getTerminatorOrNull()) + RethrowBlock = cast<llvm::CondBrInst>(TI)->getSuccessor(1); + assert(RethrowBlock != WasmCatchStartBlock && RethrowBlock->empty()); + Builder.SetInsertPoint(RethrowBlock); + llvm::Function *RethrowInCatchFn = + CGM.getIntrinsic(llvm::Intrinsic::wasm_rethrow); + EmitNoreturnRuntimeCallOrInvoke(RethrowInCatchFn, {}); } void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { @@ -1320,24 +1346,8 @@ 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::Instruction *TI = RethrowBlock->getTerminatorOrNull()) - RethrowBlock = cast<llvm::CondBrInst>(TI)->getSuccessor(1); - assert(RethrowBlock != WasmCatchStartBlock && RethrowBlock->empty()); - Builder.SetInsertPoint(RethrowBlock); - llvm::Function *RethrowInCatchFn = - CGM.getIntrinsic(llvm::Intrinsic::wasm_rethrow); - EmitNoreturnRuntimeCallOrInvoke(RethrowInCatchFn, {}); + WasmEmitFallthroughRethrow(WasmCatchStartBlock); } EmitBlock(ContBB); diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index 32a1afe310629..1e7f6dc6a4e8d 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -2373,12 +2373,13 @@ CGObjCGNU::CGObjCGNU(CodeGenModule &cgm, unsigned runtimeABIVersion, MetaClassPtrAlias(nullptr), RuntimeVersion(runtimeABIVersion), ProtocolVersion(protocolClassVersion), ClassABIVersion(classABI) { + auto Triple = cgm.getContext().getTargetInfo().getTriple(); + msgSendMDKind = VMContext.getMDKindID("GNUObjCMessageSend"); - usesSEHExceptions = - cgm.getContext().getTargetInfo().getTriple().isWindowsMSVCEnvironment(); + usesSEHExceptions = Triple.isWindowsMSVCEnvironment(); usesCxxExceptions = - cgm.getContext().getTargetInfo().getTriple().isOSCygMing() && - isRuntime(ObjCRuntime::GNUstep, 2); + (Triple.isOSCygMing() && isRuntime(ObjCRuntime::GNUstep, 2)) || + Triple.isWasm(); CodeGenTypes &Types = CGM.getTypes(); IntTy = cast<llvm::IntegerType>( @@ -4155,8 +4156,7 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { if (!ClassAliases.empty()) { llvm::Type *ArgTypes[2] = {PtrTy, PtrToInt8Ty}; llvm::FunctionType *RegisterAliasTy = - llvm::FunctionType::get(Builder.getVoidTy(), - ArgTypes, false); + llvm::FunctionType::get(BoolTy, ArgTypes, false); llvm::Function *RegisterAlias = llvm::Function::Create( RegisterAliasTy, llvm::GlobalValue::ExternalWeakLinkage, "class_registerAlias_np", diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp index a83a4ce67a9c6..cca1b37431055 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "CGObjCRuntime.h" +#include "Address.h" #include "CGCXXABI.h" #include "CGCleanup.h" #include "CGRecordLayout.h" @@ -23,6 +24,8 @@ #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SaveAndRestore.h" using namespace clang; @@ -148,12 +151,24 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, Cont = CGF.getJumpDestInCurrentScope("eh.cont"); bool useFunclets = EHPersonality::get(CGF).usesFuncletPads(); + bool IsWasm = EHPersonality::get(CGF).isWasmPersonality(); + bool IsMSVC = EHPersonality::get(CGF).isMSVCPersonality(); CodeGenFunction::FinallyInfo FinallyInfo; - if (!useFunclets) - if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) - FinallyInfo.enter(CGF, Finally->getFinallyBody(), - beginCatchFn, endCatchFn, exceptionRethrowFn); + if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) { + if (!useFunclets) { + // The finally statement is executed as a cleanup for the normal and + // exceptional control flow out of a try-catch block. This is all + // implemented in FinallyInfo. Here we enter a new EHCatchScope. + FinallyInfo.enter(CGF, Finally->getFinallyBody(), beginCatchFn, + endCatchFn, exceptionRethrowFn); + } else if (IsWasm) { + // dispatchBlock is finally.catchall + // emitWasmCatchPadBlock() + // CurrentFuncletPad = ... + llvm_unreachable("@finally not implemented for WASM"); + } + } SmallVector<CatchHandler, 8> Handlers; @@ -182,12 +197,13 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, Handler.TypeInfo = GetEHType(CatchDecl->getType()); } + // Create a new catch scope EHCatchScope *Catch = CGF.EHStack.pushCatch(Handlers.size()); for (unsigned I = 0, E = Handlers.size(); I != E; ++I) Catch->setHandler(I, { Handlers[I].TypeInfo, Handlers[I].Flags }, Handlers[I].Block); } - if (useFunclets) + if (IsMSVC) if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) { CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true); if (!CGF.CurSEHParent) @@ -212,31 +228,92 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, // Emit the try body. CGF.EmitStmt(S.getTryBody()); + // lpad or catch.dispatch (the dispatch block) has now been emitted + // + // Here an example: + // void may_throw(); + // @try { + // may_throw(); + // } @catch(id a) { + // } @catch(id b) { + // [...] + // + // With funclet-based exception handling, the dispatch block is created in + // getEHDispatchBlock() <- getInvokeDestImpl() <- EmitCall(). + // The following IR is emitted in this case: + // On aarch64-linux-gnu (landing-pad based) + // %call = invoke i32 @may_throw() + // to label %invoke.cont unwind label %lpad, !dbg !19 + // On aarch64-pc-windows-msvc (funclet based) + // %call = invoke i32 @may_throw() + // to label %invoke.cont unwind label %catch.dispatch, !dbg !17 + // Leave the try. - if (S.getNumCatchStmts()) - CGF.popCatchScope(); + llvm::BasicBlock *DispatchBlock = nullptr; + if (S.getNumCatchStmts()) { + // The dispatch block that was created during the emission of the try block + // was cached. We retrieve it when popping the current catch scope. + DispatchBlock = CGF.popCatchScope(); + } + + // On Windows and WASM, the new exception handling instructions are used. + // + // Continuing with the previous example, on Windows, we emit one catchpad for + // every catch handler. This is not the case for WASM where all catch handlers + // merged into one big catchpad: + // + // catch.dispatch: + // %0 = catchswitch within none [label %catch.start] unwind to caller + // catch.start: + // %1 = catchpad within %0 [ptr @__objc_id_type_info, ptr null] + // [...] + // br i1 %matches, label %catch, label %catch2 + // + // We save the old funclet pad here before we traverse each catch handler. + SaveAndRestore RestoreCurrentFuncletPad(CGF.CurrentFuncletPad); + llvm::BasicBlock *WasmCatchStartBlock = nullptr; + llvm::CatchPadInst *CPI = nullptr; + if (!!DispatchBlock && IsWasm) { + auto *CatchSwitch = + cast<llvm::CatchSwitchInst>(DispatchBlock->getFirstNonPHIIt()); + WasmCatchStartBlock = CatchSwitch->hasUnwindDest() + ? CatchSwitch->getSuccessor(1) + : CatchSwitch->getSuccessor(0); + CPI = cast<llvm::CatchPadInst>(WasmCatchStartBlock->getFirstNonPHIIt()); + CGF.CurrentFuncletPad = CPI; + } // Remember where we were. CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP(); - // Emit the handlers. + // Emit the handlers. If there is no catch-all handler, we need to emit a + // fallthrough block in WASM. We therefore need to know if we have a + // catch-all handler in this catch scope. + bool HasCatchAll = false; for (CatchHandler &Handler : Handlers) { + HasCatchAll |= Handler.TypeInfo == nullptr; CGF.EmitBlock(Handler.Block); CodeGenFunction::LexicalScope Cleanups(CGF, Handler.Body->getSourceRange()); SaveAndRestore RevertAfterScope(CGF.CurrentFuncletPad); - if (useFunclets) { + if (IsMSVC) { llvm::BasicBlock::iterator CPICandidate = Handler.Block->getFirstNonPHIIt(); if (CPICandidate != Handler.Block->end()) { - if (auto *CPI = dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate)) { + CPI = dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate); + if (!!CPI) { CGF.CurrentFuncletPad = CPI; CPI->setOperand(2, CGF.getExceptionSlot().emitRawPointer(CGF)); - CGF.EHStack.pushCleanup<CatchRetScope>(NormalCleanup, CPI); } } } + if (!!CPI) { + // A catchpad requires a matching catchret instruction. We emit this in + // form of a cleanup. + CGF.EHStack.pushCleanup<CatchRetScope>(NormalCleanup, CPI); + } + llvm::Value *RawExn = CGF.getExceptionFromSlot(); // Enter the catch. @@ -262,6 +339,8 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, EmitInitOfCatchParam(CGF, CastExn, CatchParam); } + // The body of the handler might have more try-catch blocks, so we need to + // save the current exception before emitting the body. CGF.ObjCEHValueStack.push_back(Exn); CGF.EmitStmt(Handler.Body); CGF.ObjCEHValueStack.pop_back(); @@ -272,6 +351,10 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, CGF.EmitBranchThroughCleanup(Cont); } + if (IsWasm && !HasCatchAll) { + CGF.WasmEmitFallthroughRethrow(WasmCatchStartBlock); + } + // Go back to the try-statement fallthrough. CGF.Builder.restoreIP(SavedIP); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e89d754c309c8..b907a1c86c2e7 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1326,7 +1326,15 @@ class CodeGenFunction : public CodeGenTypeCache { /// popCatchScope - Pops the catch scope at the top of the EHScope /// stack, emitting any required code (other than the catch handlers /// themselves). - void popCatchScope(); + llvm::BasicBlock *popCatchScope(); + + // This function should be called after emitting all catch clauses and none + // of them were 'catch-all' clauses. + // 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. + void WasmEmitFallthroughRethrow(llvm::BasicBlock *WasmCatchStartBlock); llvm::BasicBlock *getEHResumeBlock(bool isCleanup); llvm::BasicBlock *getEHDispatchBlock(EHScopeStack::stable_iterator scope); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index d2e22920aa432..efbf5d28f56fe 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -8638,7 +8638,8 @@ ObjCRuntime Clang::AddObjCRuntimeArgs(const ArgList &args, if ((runtime.getKind() == ObjCRuntime::GNUstep) && (runtime.getVersion() >= VersionTuple(2, 0))) if (!getToolChain().getTriple().isOSBinFormatELF() && - !getToolChain().getTriple().isOSBinFormatCOFF()) { + !getToolChain().getTriple().isOSBinFormatCOFF() && + !getToolChain().getTriple().isOSBinFormatWasm()) { getToolChain().getDriver().Diag( diag::err_drv_gnustep_objc_runtime_incompatible_binary) << runtime.getVersion().getMajor(); diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m b/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m new file mode 100644 index 0000000000000..9b4482be569a0 --- /dev/null +++ b/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fobjc-exceptions -fexceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -fobjc-runtime=gnustep-2.2 -o - %s | FileCheck %s + +void may_throw(void) { + @throw (id) 1; +} + +int main(void) { + int retval = 0; + @try { + may_throw(); + // CHECK: invoke void @may_throw() + // CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] + } + // Check that the dispatch block has been emitted correctly. + // CHECK: [[CATCH_DISPATCH]]: + // CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCH_START:.*]] unwind to caller + + + // The native WASM EH uses the new exception handling IR instructions + // (catchswitch, catchpad, etc.) that are also used when targeting Windows MSVC. + // For SEH, we emit a catchpad instruction for each catch statement. On WASM, we + // merge all catch statements into one big catch block. + + // CHECK: catchpad within %[[CATCHSWITCH]] [ptr @__objc_id_type_info, ptr null] + + // We use the cxa functions instead of objc_{begin,end}_catch. + // CHECK: call ptr @__cxa_begin_catch + @catch(id a) { + retval = 1; + } + @catch(...) { + retval = 2; + } + return retval; +} From f9b5f5997b5de29d66a5611866b5881ff81f2593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]> Date: Fri, 7 Aug 2026 14:47:23 +0200 Subject: [PATCH 2/3] Address review comments, add more tests and fix formatting issues. --- clang/lib/CodeGen/CGException.cpp | 18 +- clang/lib/CodeGen/CGObjCGNU.cpp | 7 +- clang/lib/CodeGen/CGObjCRuntime.cpp | 40 ++-- clang/test/CodeGenObjC/gnustep2-wasm32-eh.m | 35 ---- clang/test/CodeGenObjC/wasm32-eh-arc.m | 29 +++ clang/test/CodeGenObjC/wasm32-eh.m | 202 +++++++++++++++++++ clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm | 102 ++++++++++ 7 files changed, 364 insertions(+), 69 deletions(-) delete mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-eh.m create mode 100644 clang/test/CodeGenObjC/wasm32-eh-arc.m create mode 100644 clang/test/CodeGenObjC/wasm32-eh.m create mode 100644 clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index 83f1e3dd29e35..f8391a0d041f2 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -150,6 +150,8 @@ static const EHPersonality &getObjCPersonality(const TargetInfo &Target, const llvm::Triple &T = Target.getTriple(); if (T.isWindowsMSVCEnvironment()) return EHPersonality::MSVC_CxxFrameHandler3; + if (T.isWasm()) + return EHPersonality::GNU_Wasm_CPlusPlus; switch (L.ObjCRuntime.getKind()) { case ObjCRuntime::FragileMacOSX: @@ -161,9 +163,7 @@ static const EHPersonality &getObjCPersonality(const TargetInfo &Target, case ObjCRuntime::GNUstep: if (T.isOSCygMing()) return EHPersonality::GNU_CPlusPlus_SEH; - else if (T.isWasm()) - return EHPersonality::GNU_Wasm_CPlusPlus; - else if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7)) + if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7)) return EHPersonality::GNUstep_ObjC; [[fallthrough]]; case ObjCRuntime::GCC: @@ -205,6 +205,8 @@ static const EHPersonality &getObjCXXPersonality(const TargetInfo &Target, auto Triple = Target.getTriple(); if (Triple.isWindowsMSVCEnvironment()) return EHPersonality::MSVC_CxxFrameHandler3; + if (Triple.isWasm()) + return EHPersonality::GNU_Wasm_CPlusPlus; switch (L.ObjCRuntime.getKind()) { // In the fragile ABI, just use C++ exception handling and hope @@ -221,12 +223,9 @@ static const EHPersonality &getObjCXXPersonality(const TargetInfo &Target, return getObjCPersonality(Target, CGOpts, L); case ObjCRuntime::GNUstep: - if (Triple.isWasm()) - return EHPersonality::GNU_Wasm_CPlusPlus; - else if (Triple.isOSCygMing()) + if (Triple.isOSCygMing()) return EHPersonality::GNU_CPlusPlus_SEH; - else - return EHPersonality::GNU_ObjCXX; + return EHPersonality::GNU_ObjCXX; // The GCC runtime's personality function inherently doesn't support // mixed EH. Use the ObjC personality just to avoid returning null. @@ -1346,7 +1345,8 @@ void CodeGenFunction::ExitCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock) { Builder.CreateBr(ContBB); } - if (EHPersonality::get(*this).isWasmPersonality() && !HasCatchAll) { + if (EHPersonality::get(*this).isWasmPersonality() && !HasCatchAll && + WasmCatchStartBlock) { WasmEmitFallthroughRethrow(WasmCatchStartBlock); } diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp index 1e7f6dc6a4e8d..e4581feb5a21c 100644 --- a/clang/lib/CodeGen/CGObjCGNU.cpp +++ b/clang/lib/CodeGen/CGObjCGNU.cpp @@ -4156,7 +4156,7 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { if (!ClassAliases.empty()) { llvm::Type *ArgTypes[2] = {PtrTy, PtrToInt8Ty}; llvm::FunctionType *RegisterAliasTy = - llvm::FunctionType::get(BoolTy, ArgTypes, false); + llvm::FunctionType::get(Builder.getVoidTy(), ArgTypes, false); llvm::Function *RegisterAlias = llvm::Function::Create( RegisterAliasTy, llvm::GlobalValue::ExternalWeakLinkage, "class_registerAlias_np", @@ -4338,15 +4338,14 @@ void CGObjCGNU::EmitThrowStmt(CodeGenFunction &CGF, // that was passed into the `@catch` block, then this code path is not // reached and we will instead call `objc_exception_throw` with an explicit // argument. - llvm::CallBase *Throw = CGF.EmitRuntimeCallOrInvoke(ExceptionReThrowFn); - Throw->setDoesNotReturn(); + CGF.EmitNoreturnRuntimeCallOrInvoke(ExceptionReThrowFn, {}); } else { ExceptionAsObject = CGF.Builder.CreateBitCast(ExceptionAsObject, IdTy); llvm::CallBase *Throw = CGF.EmitRuntimeCallOrInvoke(ExceptionThrowFn, ExceptionAsObject); Throw->setDoesNotReturn(); + CGF.Builder.CreateUnreachable(); } - CGF.Builder.CreateUnreachable(); if (ClearInsertionPoint) CGF.Builder.ClearInsertionPoint(); } diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp index cca1b37431055..263b81331084d 100644 --- a/clang/lib/CodeGen/CGObjCRuntime.cpp +++ b/clang/lib/CodeGen/CGObjCRuntime.cpp @@ -162,11 +162,10 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, // implemented in FinallyInfo. Here we enter a new EHCatchScope. FinallyInfo.enter(CGF, Finally->getFinallyBody(), beginCatchFn, endCatchFn, exceptionRethrowFn); - } else if (IsWasm) { - // dispatchBlock is finally.catchall - // emitWasmCatchPadBlock() - // CurrentFuncletPad = ... - llvm_unreachable("@finally not implemented for WASM"); + } else { + CGF.ErrorUnsupported( + Finally, + "@finally is not implemented for funclet based exception handling"); } } @@ -203,27 +202,26 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, Catch->setHandler(I, { Handlers[I].TypeInfo, Handlers[I].Flags }, Handlers[I].Block); } - if (IsMSVC) + if (IsMSVC) { if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) { - CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true); - if (!CGF.CurSEHParent) - CGF.CurSEHParent = cast<NamedDecl>(CGF.CurFuncDecl); - // Outline the finally block. - const Stmt *FinallyBlock = Finally->getFinallyBody(); - HelperCGF.startOutlinedSEHHelper(CGF, /*isFilter*/false, FinallyBlock); - - // Emit the original filter expression, convert to i32, and return. - HelperCGF.EmitStmt(FinallyBlock); + CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true); + if (!CGF.CurSEHParent) + CGF.CurSEHParent = cast<NamedDecl>(CGF.CurFuncDecl); + // Outline the finally block. + const Stmt *FinallyBlock = Finally->getFinallyBody(); + HelperCGF.startOutlinedSEHHelper(CGF, /*isFilter*/ false, FinallyBlock); - HelperCGF.FinishFunction(FinallyBlock->getEndLoc()); + // Emit the original filter expression, convert to i32, and return. + HelperCGF.EmitStmt(FinallyBlock); - llvm::Function *FinallyFunc = HelperCGF.CurFn; + HelperCGF.FinishFunction(FinallyBlock->getEndLoc()); + llvm::Function *FinallyFunc = HelperCGF.CurFn; - // Push a cleanup for __finally blocks. - CGF.pushSEHCleanup(NormalAndEHCleanup, FinallyFunc); + // Push a cleanup for __finally blocks. + CGF.pushSEHCleanup(NormalAndEHCleanup, FinallyFunc); } - + } // Emit the try body. CGF.EmitStmt(S.getTryBody()); @@ -351,7 +349,7 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, CGF.EmitBranchThroughCleanup(Cont); } - if (IsWasm && !HasCatchAll) { + if (IsWasm && !HasCatchAll && WasmCatchStartBlock) { CGF.WasmEmitFallthroughRethrow(WasmCatchStartBlock); } diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m b/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m deleted file mode 100644 index 9b4482be569a0..0000000000000 --- a/clang/test/CodeGenObjC/gnustep2-wasm32-eh.m +++ /dev/null @@ -1,35 +0,0 @@ -// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fobjc-exceptions -fexceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -fobjc-runtime=gnustep-2.2 -o - %s | FileCheck %s - -void may_throw(void) { - @throw (id) 1; -} - -int main(void) { - int retval = 0; - @try { - may_throw(); - // CHECK: invoke void @may_throw() - // CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] - } - // Check that the dispatch block has been emitted correctly. - // CHECK: [[CATCH_DISPATCH]]: - // CHECK-NEXT: %[[CATCHSWITCH:.*]] = catchswitch within none [label %[[CATCH_START:.*]] unwind to caller - - - // The native WASM EH uses the new exception handling IR instructions - // (catchswitch, catchpad, etc.) that are also used when targeting Windows MSVC. - // For SEH, we emit a catchpad instruction for each catch statement. On WASM, we - // merge all catch statements into one big catch block. - - // CHECK: catchpad within %[[CATCHSWITCH]] [ptr @__objc_id_type_info, ptr null] - - // We use the cxa functions instead of objc_{begin,end}_catch. - // CHECK: call ptr @__cxa_begin_catch - @catch(id a) { - retval = 1; - } - @catch(...) { - retval = 2; - } - return retval; -} diff --git a/clang/test/CodeGenObjC/wasm32-eh-arc.m b/clang/test/CodeGenObjC/wasm32-eh-arc.m new file mode 100644 index 0000000000000..1100e3bfbf28d --- /dev/null +++ b/clang/test/CodeGenObjC/wasm32-eh-arc.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fobjc-arc -fexceptions -fobjc-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck --enable-var-scope %s +__attribute__((objc_root_class)) @interface Object @end +extern void mayThrowObjC(); + +int arcRethrow(Object *value) { + @try { + mayThrowObjC(); + } @catch (id caught) { + @throw; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @arcRethrow +// CHECK: invoke void @mayThrowObjC() +// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_id_type_info] +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[RETHROW:.*]] +// CHECK: [[RETHROW]]: +// CHECK-NEXT: call void @llvm.wasm.rethrow() +// CHECK-NEXT: unreachable +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %{{.*}} +// CHECK: [[CATCH]]: +// CHECK: invoke void @__cxa_rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK-NEXT: to label %unreachable unwind label diff --git a/clang/test/CodeGenObjC/wasm32-eh.m b/clang/test/CodeGenObjC/wasm32-eh.m new file mode 100644 index 0000000000000..e35d81de51bf5 --- /dev/null +++ b/clang/test/CodeGenObjC/wasm32-eh.m @@ -0,0 +1,202 @@ +// RUN: %clang_cc1 -triple wasm32-unknown-emscripten -fobjc-exceptions -fexceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -fobjc-runtime=gnustep-2.2 -o - %s | FileCheck --enable-var-scope %s + +__attribute__((objc_root_class)) @interface Object +@end + +@interface ExceptionA : Object +@end + +@interface ExceptionB : Object +@end + +void mayThrow(void) { + @throw (id)1; +} + +int basicCatchAll(void) { + @try { + mayThrow(); + } @catch (...) { + return 1; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @basicCatchAll +// CHECK: invoke void @mayThrow() +// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr null] +// CHECK: br label %[[CATCH_ALL:.*]] +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[CATCH_ALL]]: +// CHECK: call ptr @__cxa_begin_catch +// CHECK: call void @__cxa_end_catch() +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %return + +int twoTypedHandlers(void) { + @try { + mayThrow(); + } @catch (ExceptionA *exception) { + return 1; + } @catch (ExceptionB *exception) { + return 2; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @twoTypedHandlers +// CHECK: invoke void @mayThrow() +// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_ExceptionA, ptr @__objc_eh_typeinfo_ExceptionB] +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[CATCH_FALLTHROUGH:.*]] +// CHECK: [[CATCH_FALLTHROUGH]]: +// CHECK: br i1 %{{.*}}, label %[[CATCH2:.*]], label %[[RETHROW:.*]] +// CHECK: [[RETHROW]]: +// CHECK-NEXT: call void @llvm.wasm.rethrow() +// CHECK-NEXT: unreachable +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[EH_CONT]]: +// CHECK: br label %return +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %return +// CHECK: [[CATCH2]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST2:.*]] +// CHECK: [[CATCHRET_DEST2]]: +// CHECK-NEXT: br label %return + +int typedHandlerAndCatchAll(void) { + @try { + mayThrow(); + } @catch (ExceptionA *exception) { + return 1; + } @catch (...) { + return 2; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @typedHandlerAndCatchAll +// CHECK: invoke void @mayThrow() +// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_ExceptionA, ptr null] +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[CATCH_ALL:.*]] +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[EH_CONT]]: +// CHECK: br label %return +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %return +// CHECK: [[CATCH_ALL]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST_ALL:.*]] +// CHECK: [[CATCHRET_DEST_ALL]]: +// CHECK-NEXT: br label %return + +int nestedTryCatch(void) { + @try { + @try { + mayThrow(); + } @catch (ExceptionA *exception) { + return 1; + } + } @catch (...) { + return 2; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @nestedTryCatch +// CHECK: invoke void @mayThrow() +// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind label %[[CATCH_DISPATCH1:.*]] +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_ExceptionA] +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[RETHROW:.*]] +// CHECK: [[RETHROW]]: +// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK-NEXT: to label %[[UNREACHABLE:.*]] unwind label %[[CATCH_DISPATCH1]] +// CHECK: [[CATCH_DISPATCH1]]: +// CHECK-NEXT: [[CATCHSWITCH1:%.*]] = catchswitch within none [label %[[CATCH_START2:.*]]] unwind to caller +// CHECK: [[CATCH_START2]]: +// CHECK-NEXT: [[CATCHPAD1:%.*]] = catchpad within [[CATCHSWITCH1]] [ptr null] +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[EH_CONT]]: +// CHECK: br label %[[EH_CONT2:.*]] +// CHECK: [[EH_CONT2]]: +// CHECK: br label %return +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %return +// CHECK: [[CATCH_ALL:.*]]: +// CHECK: catchret from [[CATCHPAD1]] to label %[[CATCHRET_DEST_ALL:.*]] +// CHECK: [[CATCHRET_DEST_ALL]]: +// CHECK-NEXT: br label %return + +int emptyCatch(void) { + @try { + mayThrow(); + } @catch (ExceptionA *exception) { + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @emptyCatch +// CHECK: invoke void @mayThrow() +// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_ExceptionA] +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[RETHROW:.*]] +// CHECK: [[RETHROW]]: +// CHECK-NEXT: call void @llvm.wasm.rethrow() +// CHECK-NEXT: unreachable +// CHECK: [[INVOKE_CONT]]: +// CHECK: br label %[[EH_CONT:.*]] +// CHECK: [[EH_CONT]]: +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %[[CATCHRET_DEST:.*]] +// CHECK: [[CATCHRET_DEST]]: +// CHECK-NEXT: br label %[[EH_CONT]] + +int explicitRethrow(void) { + @try { + mayThrow(); + } @catch (...) { + @throw; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @explicitRethrow +// CHECK: invoke void @mayThrow() +// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr null] +// CHECK: br label %[[CATCH_ALL:.*]] +// CHECK: [[CATCH_ALL]]: +// CHECK: invoke void @__cxa_rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK-NEXT: to label %[[UNREACHABLE:.*]] unwind label %{{.*}} +// CHECK: [[UNREACHABLE]]: +// CHECK-NEXT: unreachable diff --git a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm new file mode 100644 index 0000000000000..2c4ef5dfaf68d --- /dev/null +++ b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm @@ -0,0 +1,102 @@ +// RUN: %clang_cc1 -target-feature +exception-handling -triple wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fexceptions -fobjc-exceptions -fcxx-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck --enable-var-scope %s + +struct ThrowingDestructor { + ~ThrowingDestructor() noexcept(false); +}; + +extern void mayThrowCXX(); + +int cxxDestructorsAroundCatch() { + try { + ThrowingDestructor guard; + mayThrowCXX(); + } catch (...) { + ThrowingDestructor caught; + return 1; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @_Z25cxxDestructorsAroundCatchv +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowCXXv() +// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[EHCLEANUP:.*]] +// CHECK: [[INVOKE_CONT]]: +// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev +// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[EHCLEANUP]]: +// CHECK: [[CLEANUPPAD:%.*]] = cleanuppad within none [] +// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CLEANUPPAD]]) ] +// CHECK: cleanupret from [[CLEANUPPAD]] unwind label %[[CATCH_DISPATCH]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr null] +// CHECK: br label %[[CATCH_ALL:.*]] +// CHECK: [[CATCH_ALL]]: +// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK: catchret from [[CATCHPAD]] to label %{{.*}} +// CHECK: [[CLEANUPPAD1:%.*]] = cleanuppad within [[CATCHPAD]] [] +// CHECK: cleanupret from [[CLEANUPPAD1]] unwind to caller + +__attribute__((objc_root_class)) @interface Object +@end + +extern void mayThrowObjC(); + +int combinedCxxObjcEH() { + @try { + try { + mayThrowCXX(); + } catch (Object *exception) { + @try { + mayThrowObjC(); + } @catch (Object *nestedException) { + return 1; + } + return 2; + } catch (int value) { + return value; + } + } @catch (...) { + return 3; + } + return 0; +} + +// CHECK-LABEL: define{{.*}} @_Z{{[0-9]+}}combinedCxxObjcEHv +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowCXXv() +// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH:.*]] +// CHECK: [[CATCH_DISPATCH]]: +// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind label %[[CATCH_DISPATCH1:.*]] +// CHECK: [[CATCH_START]]: +// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_Object, ptr @_ZTIi] +// CHECK: br i1 %{{.*}}, label %[[CATCH2:.*]], label %[[CATCH_FALLTHROUGH:.*]] +// CHECK: [[CATCH2]]: +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv() +// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH5:.*]] +// CHECK: [[CATCH_DISPATCH5]]: +// CHECK-NEXT: [[CATCHSWITCH1:%.*]] = catchswitch within [[CATCHPAD]] [label %[[CATCH_START6:.*]]] unwind label %[[EHCLEANUP:.*]] +// CHECK: [[CATCH_START6]]: +// CHECK-NEXT: [[CATCHPAD1:%.*]] = catchpad within [[CATCHSWITCH1]] [ptr @__objc_eh_typeinfo_Object] +// CHECK: br i1 %{{.*}}, label %[[CATCH9:.*]], label %[[RETHROW8:.*]] +// CHECK: [[RETHROW8]]: +// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD1]]) ] +// CHECK: [[CATCH_FALLTHROUGH]]: +// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[RETHROW:.*]] +// CHECK: [[CATCH]]: +// CHECK: catchret from [[CATCHPAD]] to label %{{.*}} +// CHECK: [[RETHROW]]: +// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] +// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH1]] +// CHECK: [[CATCH_DISPATCH1]]: +// CHECK-NEXT: [[CATCHSWITCH2:%.*]] = catchswitch within none [label %[[CATCH_START15:.*]]] unwind to caller +// CHECK: [[CATCH_START15]]: +// CHECK-NEXT: [[CATCHPAD2:%.*]] = catchpad within [[CATCHSWITCH2]] [ptr null] +// CHECK: br label %[[CATCH_ALL:.*]] +// CHECK: [[CATCH9]]: +// CHECK: catchret from [[CATCHPAD1]] to label %{{.*}} +// CHECK: [[EHCLEANUP]]: +// CHECK: [[CLEANUPPAD:%.*]] = cleanuppad within [[CATCHPAD]] [] +// CHECK: cleanupret from [[CLEANUPPAD]] unwind label %[[CATCH_DISPATCH1]] +// CHECK: [[CATCH_ALL]]: +// CHECK: catchret from [[CATCHPAD2]] to label %{{.*}} From 1654a644170e582df07311e9650e1275c47409bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]> Date: Tue, 11 Aug 2026 15:29:20 +0200 Subject: [PATCH 3/3] Simplify test assertions --- clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm | 58 ++++---------------- 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm index 2c4ef5dfaf68d..4b989e6122fdd 100644 --- a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm +++ b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm @@ -19,24 +19,13 @@ int cxxDestructorsAroundCatch() { // CHECK-LABEL: define{{.*}} @_Z25cxxDestructorsAroundCatchv // CHECK: invoke void @_Z{{[0-9]+}}mayThrowCXXv() -// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[EHCLEANUP:.*]] -// CHECK: [[INVOKE_CONT]]: -// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev -// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH:.*]] -// CHECK: [[EHCLEANUP]]: // CHECK: [[CLEANUPPAD:%.*]] = cleanuppad within none [] // CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CLEANUPPAD]]) ] -// CHECK: cleanupret from [[CLEANUPPAD]] unwind label %[[CATCH_DISPATCH]] -// CHECK: [[CATCH_DISPATCH]]: -// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind to caller -// CHECK: [[CATCH_START]]: -// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr null] -// CHECK: br label %[[CATCH_ALL:.*]] -// CHECK: [[CATCH_ALL]]: +// CHECK: cleanupret from [[CLEANUPPAD]] unwind label %{{.*}} +// CHECK: [[CATCHSWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind to caller +// CHECK: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr null] // CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CATCHPAD]]) ] // CHECK: catchret from [[CATCHPAD]] to label %{{.*}} -// CHECK: [[CLEANUPPAD1:%.*]] = cleanuppad within [[CATCHPAD]] [] -// CHECK: cleanupret from [[CLEANUPPAD1]] unwind to caller __attribute__((objc_root_class)) @interface Object @end @@ -65,38 +54,13 @@ int combinedCxxObjcEH() { // CHECK-LABEL: define{{.*}} @_Z{{[0-9]+}}combinedCxxObjcEHv // CHECK: invoke void @_Z{{[0-9]+}}mayThrowCXXv() -// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH:.*]] -// CHECK: [[CATCH_DISPATCH]]: -// CHECK-NEXT: [[CATCHSWITCH:%.*]] = catchswitch within none [label %[[CATCH_START:.*]]] unwind label %[[CATCH_DISPATCH1:.*]] -// CHECK: [[CATCH_START]]: -// CHECK-NEXT: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_Object, ptr @_ZTIi] -// CHECK: br i1 %{{.*}}, label %[[CATCH2:.*]], label %[[CATCH_FALLTHROUGH:.*]] -// CHECK: [[CATCH2]]: -// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv() -// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH5:.*]] -// CHECK: [[CATCH_DISPATCH5]]: -// CHECK-NEXT: [[CATCHSWITCH1:%.*]] = catchswitch within [[CATCHPAD]] [label %[[CATCH_START6:.*]]] unwind label %[[EHCLEANUP:.*]] -// CHECK: [[CATCH_START6]]: -// CHECK-NEXT: [[CATCHPAD1:%.*]] = catchpad within [[CATCHSWITCH1]] [ptr @__objc_eh_typeinfo_Object] -// CHECK: br i1 %{{.*}}, label %[[CATCH9:.*]], label %[[RETHROW8:.*]] -// CHECK: [[RETHROW8]]: -// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD1]]) ] -// CHECK: [[CATCH_FALLTHROUGH]]: -// CHECK: br i1 %{{.*}}, label %[[CATCH:.*]], label %[[RETHROW:.*]] -// CHECK: [[CATCH]]: +// CHECK: [[CATCHSWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind label %{{.*}} +// CHECK: [[CATCHPAD:%.*]] = catchpad within [[CATCHSWITCH]] [ptr @__objc_eh_typeinfo_Object, ptr @_ZTIi] +// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv() [ "funclet"(token [[CATCHPAD]]) ] +// CHECK: [[NESTED_SWITCH:%.*]] = catchswitch within [[CATCHPAD]] [label %{{.*}}] unwind label %{{.*}} +// CHECK: [[NESTED_PAD:%.*]] = catchpad within [[NESTED_SWITCH]] [ptr @__objc_eh_typeinfo_Object] +// CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[NESTED_PAD]]) ] // CHECK: catchret from [[CATCHPAD]] to label %{{.*}} -// CHECK: [[RETHROW]]: // CHECK: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ] -// CHECK-NEXT: to label %{{.*}} unwind label %[[CATCH_DISPATCH1]] -// CHECK: [[CATCH_DISPATCH1]]: -// CHECK-NEXT: [[CATCHSWITCH2:%.*]] = catchswitch within none [label %[[CATCH_START15:.*]]] unwind to caller -// CHECK: [[CATCH_START15]]: -// CHECK-NEXT: [[CATCHPAD2:%.*]] = catchpad within [[CATCHSWITCH2]] [ptr null] -// CHECK: br label %[[CATCH_ALL:.*]] -// CHECK: [[CATCH9]]: -// CHECK: catchret from [[CATCHPAD1]] to label %{{.*}} -// CHECK: [[EHCLEANUP]]: -// CHECK: [[CLEANUPPAD:%.*]] = cleanuppad within [[CATCHPAD]] [] -// CHECK: cleanupret from [[CLEANUPPAD]] unwind label %[[CATCH_DISPATCH1]] -// CHECK: [[CATCH_ALL]]: -// CHECK: catchret from [[CATCHPAD2]] to label %{{.*}} +// CHECK: [[OUTER_SWITCH:%.*]] = catchswitch within none [label %{{.*}}] unwind to caller +// CHECK: [[OUTER_PAD:%.*]] = catchpad within [[OUTER_SWITCH]] [ptr null] _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
