https://github.com/HendrikHuebner created 
https://github.com/llvm/llvm-project/pull/218967

None

From e99c076eba76a2784b86a71e501b65b52a10d777 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]>
Date: Fri, 21 Aug 2026 15:10:49 +0200
Subject: [PATCH 1/2] [CodeGen][ObjC] Use C++-based EH for WASM targets

Squash HendrikHuebner:objc-wasm-exception-handling into a single reviewable 
commit.
---
 clang/lib/CodeGen/CGException.cpp            |  56 ++---
 clang/lib/CodeGen/CGObjCGNU.cpp              |  17 +-
 clang/lib/CodeGen/CGObjCRuntime.cpp          | 130 +++++++++---
 clang/lib/CodeGen/CodeGenFunction.h          |  10 +-
 clang/lib/Driver/ToolChains/Clang.cpp        |   3 +-
 clang/test/CodeGenObjC/wasm32-eh-arc.m       |  29 +++
 clang/test/CodeGenObjC/wasm32-eh.m           | 202 +++++++++++++++++++
 clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm |  66 ++++++
 8 files changed, 454 insertions(+), 59 deletions(-)
 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 b0fb3b4d85d15..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,7 +163,7 @@ static const EHPersonality &getObjCPersonality(const 
TargetInfo &Target,
   case ObjCRuntime::GNUstep:
     if (T.isOSCygMing())
       return EHPersonality::GNU_CPlusPlus_SEH;
-    else if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
+    if (L.ObjCRuntime.getVersion() >= VersionTuple(1, 7))
       return EHPersonality::GNUstep_ObjC;
     [[fallthrough]];
   case ObjCRuntime::GCC:
@@ -200,8 +202,11 @@ 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;
+  if (Triple.isWasm())
+    return EHPersonality::GNU_Wasm_CPlusPlus;
 
   switch (L.ObjCRuntime.getKind()) {
   // In the fragile ABI, just use C++ exception handling and hope
@@ -218,8 +223,9 @@ 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.isOSCygMing())
+      return EHPersonality::GNU_CPlusPlus_SEH;
+    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 +1213,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 +1345,9 @@ 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, {});
+  if (EHPersonality::get(*this).isWasmPersonality() && !HasCatchAll &&
+      WasmCatchStartBlock) {
+    WasmEmitFallthroughRethrow(WasmCatchStartBlock);
   }
 
   EmitBlock(ContBB);
diff --git a/clang/lib/CodeGen/CGObjCGNU.cpp b/clang/lib/CodeGen/CGObjCGNU.cpp
index 32a1afe310629..e4581feb5a21c 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(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 a83a4ce67a9c6..442c2990d54d8 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,22 @@ 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) {
+      CGF.ErrorUnsupported(Finally,
+                           "@finally is not implemented for WebAssembly");
+    }
+  }
 
   SmallVector<CatchHandler, 8> Handlers;
 
@@ -182,61 +195,122 @@ 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)
-            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());
 
+  // 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 +336,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 +348,10 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
     CGF.EmitBranchThroughCleanup(Cont);
   }
 
+  if (IsWasm && !HasCatchAll && WasmCatchStartBlock) {
+    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/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..4b989e6122fdd
--- /dev/null
+++ b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm
@@ -0,0 +1,66 @@
+// 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: [[CLEANUPPAD:%.*]] = cleanuppad within none []
+// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token 
[[CLEANUPPAD]]) ]
+// 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 %{{.*}}
+
+__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: [[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: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token 
[[CATCHPAD]]) ]
+// CHECK: [[OUTER_SWITCH:%.*]] = catchswitch within none [label %{{.*}}] 
unwind to caller
+// CHECK: [[OUTER_PAD:%.*]] = catchpad within [[OUTER_SWITCH]] [ptr null]

From 11bf4e8db15ed99e41a386ada1cb2af2c5b5968e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]>
Date: Fri, 21 Aug 2026 15:11:49 +0200
Subject: [PATCH 2/2] [CodeGen][ObjC] Implement @finally for WebAssembly EH

Add WebAssembly @finally lowering, funclet-aware cleanup handling, and focused 
CodeGen/Sema coverage.
---
 clang/lib/CodeGen/CGException.cpp             |  40 +++-
 clang/lib/CodeGen/CGObjCRuntime.cpp           |  10 +-
 .../CodeGenObjC/gnustep2-wasm32-arc-rethrow.m |  17 ++
 clang/test/CodeGenObjC/gnustep2-wasm32-eh.m   |  35 +++
 .../CodeGenObjC/gnustep2-wasm32-empty-catch.m |  15 ++
 .../CodeGenObjC/gnustep2-wasm32-finally.m     | 176 +++++++++++++++
 clang/test/CodeGenObjC/wasm32-eh-arc.m        |  29 ---
 clang/test/CodeGenObjC/wasm32-eh.m            | 202 ------------------
 .../gnustep2-wasm32-arc-cxx-catch.mm          |  21 ++
 .../gnustep2-wasm32-cxx-destructor.mm         |  28 +++
 clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm  |  66 ------
 clang/test/SemaObjC/wasm-try-goto.m           |  48 +++++
 12 files changed, 378 insertions(+), 309 deletions(-)
 create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-arc-rethrow.m
 create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-eh.m
 create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-empty-catch.m
 create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-finally.m
 delete mode 100644 clang/test/CodeGenObjC/wasm32-eh-arc.m
 delete mode 100644 clang/test/CodeGenObjC/wasm32-eh.m
 create mode 100644 clang/test/CodeGenObjCXX/gnustep2-wasm32-arc-cxx-catch.mm
 create mode 100644 clang/test/CodeGenObjCXX/gnustep2-wasm32-cxx-destructor.mm
 delete mode 100644 clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm
 create mode 100644 clang/test/SemaObjC/wasm-try-goto.m

diff --git a/clang/lib/CodeGen/CGException.cpp 
b/clang/lib/CodeGen/CGException.cpp
index f8391a0d041f2..ac362e9108d8c 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -1415,14 +1415,19 @@ namespace {
         CGF.Builder.CreateCondBr(ShouldRethrow, RethrowBB, ContBB);
 
         CGF.EmitBlock(RethrowBB);
-        if (SavedExnVar) {
-          CGF.EmitRuntimeCallOrInvoke(RethrowFn,
-            CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar,
-                                          CGF.getPointerAlign()));
+        if (EHPersonality::get(CGF).isWasmPersonality()) {
+          CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {});
         } else {
-          CGF.EmitRuntimeCallOrInvoke(RethrowFn);
+          if (SavedExnVar) {
+            CGF.EmitRuntimeCallOrInvoke(
+                RethrowFn,
+                CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar,
+                                              CGF.getPointerAlign()));
+          } else {
+            CGF.EmitRuntimeCallOrInvoke(RethrowFn);
+          }
+          CGF.Builder.CreateUnreachable();
         }
-        CGF.Builder.CreateUnreachable();
 
         CGF.EmitBlock(ContBB);
 
@@ -1508,15 +1513,31 @@ void CodeGenFunction::FinallyInfo::exit(CodeGenFunction 
&CGF) {
   EHCatchScope &catchScope = cast<EHCatchScope>(*CGF.EHStack.begin());
   llvm::BasicBlock *catchBB = catchScope.getHandler(0).Block;
 
-  CGF.popCatchScope();
+  llvm::BasicBlock *DispatchBlock = CGF.popCatchScope();
+
+  llvm::CatchPadInst *CPI = nullptr;
 
   // If there are any references to the catch-all block, emit it.
   if (catchBB->use_empty()) {
     delete catchBB;
   } else {
+    SaveAndRestore RestoreCurrentFuncletPad(CGF.CurrentFuncletPad);
+    if (EHPersonality::get(CGF).isWasmPersonality() && DispatchBlock) {
+      auto *CatchSwitch =
+          cast<llvm::CatchSwitchInst>(DispatchBlock->getFirstNonPHIIt());
+      llvm::BasicBlock *CatchStartBlock =
+          CatchSwitch->hasUnwindDest() ? CatchSwitch->getSuccessor(1)
+                                       : CatchSwitch->getSuccessor(0);
+      CPI = cast<llvm::CatchPadInst>(CatchStartBlock->getFirstNonPHIIt());
+      CGF.CurrentFuncletPad = CPI;
+    }
+
     CGBuilderTy::InsertPoint savedIP = CGF.Builder.saveAndClearIP();
     CGF.EmitBlock(catchBB);
 
+    if (CPI)
+      CGF.EHStack.pushCleanup<CatchRetScope>(NormalCleanup, CPI);
+
     llvm::Value *exn = nullptr;
 
     // If there's a begin-catch function, call it.
@@ -1537,6 +1558,11 @@ void CodeGenFunction::FinallyInfo::exit(CodeGenFunction 
&CGF) {
     // Thread a jump through the finally cleanup.
     CGF.EmitBranchThroughCleanup(RethrowDest);
 
+    // The catchret must be emitted while the catchpad is active. The branch
+    // through the finally cleanup is then resolved after leaving the catchpad.
+    if (CPI)
+      CGF.PopCleanupBlock();
+
     CGF.Builder.restoreIP(savedIP);
   }
 
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp 
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index 442c2990d54d8..4d3c0caf3520b 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -156,15 +156,12 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
 
   CodeGenFunction::FinallyInfo FinallyInfo;
   if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) {
-    if (!useFunclets) {
+    if (!useFunclets || IsWasm) {
       // 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) {
-      CGF.ErrorUnsupported(Finally,
-                           "@finally is not implemented for WebAssembly");
     }
   }
 
@@ -267,6 +264,7 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
   //   br i1 %matches, label %catch, label %catch2
   //
   // We save the old funclet pad here before we traverse each catch handler.
+  llvm::Instruction *SavedFuncletPad = CGF.CurrentFuncletPad;
   SaveAndRestore RestoreCurrentFuncletPad(CGF.CurrentFuncletPad);
   llvm::BasicBlock *WasmCatchStartBlock = nullptr;
   llvm::CatchPadInst *CPI = nullptr;
@@ -356,8 +354,10 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
   CGF.Builder.restoreIP(SavedIP);
 
   // Pop out of the finally.
-  if (!useFunclets && S.getFinallyStmt())
+  if ((!useFunclets || IsWasm) && S.getFinallyStmt()) {
+    CGF.CurrentFuncletPad = SavedFuncletPad;
     FinallyInfo.exit(CGF);
+  }
 
   if (Cont.isValid())
     CGF.EmitBlock(Cont.getBlock());
diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-arc-rethrow.m 
b/clang/test/CodeGenObjC/gnustep2-wasm32-arc-rethrow.m
new file mode 100644
index 0000000000000..7ab24ddee96ef
--- /dev/null
+++ b/clang/test/CodeGenObjC/gnustep2-wasm32-arc-rethrow.m
@@ -0,0 +1,17 @@
+// 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 %s
+__attribute__((objc_root_class)) @interface Object @end
+extern void mayThrowObjC();
+
+int arcRethrow(Object *value) {
+  @try {
+    mayThrowObjC();
+  } @catch (...) {
+    @throw;
+  }
+  return 0;
+}
+
+// CHECK-LABEL: define{{.*}} @arcRethrow
+// CHECK: [[CATCHPAD:%.*]] = catchpad within
+// CHECK: invoke void @__cxa_rethrow(){{.*}}[ "funclet"(token [[CATCHPAD]]) ]
+// CHECK-NEXT: to label %unreachable unwind label
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;
+}
diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-empty-catch.m 
b/clang/test/CodeGenObjC/gnustep2-wasm32-empty-catch.m
new file mode 100644
index 0000000000000..1391c94354d73
--- /dev/null
+++ b/clang/test/CodeGenObjC/gnustep2-wasm32-empty-catch.m
@@ -0,0 +1,15 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-emscripten 
-fobjc-runtime=gnustep-2.2 -fexceptions -fobjc-exceptions -exception-model=wasm 
-mllvm -wasm-enable-eh -emit-llvm -o - %s | FileCheck %s
+__attribute__((objc_root_class)) @interface Object @end
+
+int emptyCatch(Object *object) {
+  @try {
+    (void)object;
+  } @catch (Object *caught) {
+    return caught != (Object *)0;
+  }
+  return 0;
+}
+
+// CHECK-LABEL: define{{.*}} @emptyCatch
+// CHECK-NOT: catchswitch
+// CHECK: ret i32
diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m 
b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m
new file mode 100644
index 0000000000000..33766c842b378
--- /dev/null
+++ b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m
@@ -0,0 +1,176 @@
+// RUN: %clang_cc1 -target-feature +exception-handling -triple 
wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fexceptions 
-fobjc-exceptions -exception-model=wasm -mllvm -wasm-enable-eh -emit-llvm -o - 
%s | FileCheck %s
+
+__attribute__((objc_root_class)) @interface Object @end
+extern void mayThrowObjC();
+
+int finallySimple(Object *object) {
+  int value = 0;
+  @try {
+    mayThrowObjC();
+    value = 1;
+  } @catch (...) {
+    value = 2;
+  } @finally {
+    value += object != (Object *)0;
+  }
+  return value;
+}
+
+int finallyNoCatch(Object *object) {
+  int value = 0;
+  @try {
+    mayThrowObjC();
+    value = 1;
+  } @finally {
+    value += object != (Object *)0;
+  }
+  return value;
+}
+
+int throwInTryFinally(Object *object) {
+  int value = 0;
+  @try {
+    @throw (id)0;
+  } @catch (...) {
+    value = 1;
+  } @finally {
+    value += object != (Object *)0;
+  }
+  return value;
+}
+
+int throwInCatchFinally(Object *object) {
+  @try {
+    mayThrowObjC();
+  } @catch (...) {
+    @throw;
+  } @finally {
+    (void)object;
+  }
+}
+
+int throwInFinally(Object *object) {
+  @try {
+    mayThrowObjC();
+  } @catch (...) {
+  } @finally {
+    @throw object;
+  }
+}
+
+int nestedTryCatchFinally(Object *object) {
+  int value = 0;
+  @try {
+    @try {
+      mayThrowObjC();
+    } @catch (...) {
+      value = 1;
+    } @finally {
+      value += 2;
+    }
+  } @catch (...) {
+    value = 3;
+  } @finally {
+    value += object != (Object *)0;
+  }
+  return value;
+}
+
+int gotoOutFinally(Object *object) {
+  int value = 0;
+  @try {
+    value = 1;
+    goto done;
+  } @finally {
+    value += object != (Object *)0;
+  }
+done:
+  return value;
+}
+
+int gotoOutCatchFinally(Object *object) {
+  int value = 0;
+  @try {
+    mayThrowObjC();
+  } @catch (...) {
+    value = 1;
+    goto done;
+  } @finally {
+    value += object != (Object *)0;
+  }
+done:
+  return value;
+}
+
+// CHECK-LABEL: define{{.*}} @finallySimple
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK-NEXT:  to label %{{.*}} unwind label %{{.*}}
+// CHECK:       catch.dispatch:
+// CHECK:       catchswitch within none
+// CHECK:       catch.start:
+// CHECK:       [[TRY_PAD:%.*]] = catchpad within
+// CHECK:       invoke.cont:
+// CHECK:       br label %cleanup
+// CHECK:       cleanup:
+// CHECK:       [[FINALLY_SHOULD_THROW:%.*]] = load i1, ptr 
{{.*}}finally.for-eh
+// CHECK:       br i1 [[FINALLY_SHOULD_THROW]], label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:  to label %unreachable unwind label %ehcleanup
+// CHECK:       catch:
+// CHECK:       call ptr @__cxa_begin_catch(ptr {{.*}}){{.*}}[ "funclet"(token 
[[TRY_PAD]]) ]
+// CHECK:       invoke void @__cxa_end_catch() [ "funclet"(token [[TRY_PAD]]) ]
+// CHECK:       catchswitch within none
+// CHECK:       catch.start{{.*}}:
+// CHECK:       [[FINALLY_PAD:%.*]] = catchpad within
+// CHECK:       catchret from [[TRY_PAD]]
+// CHECK:       finally.catchall:
+// CHECK:       call ptr @__cxa_begin_catch(ptr {{.*}}){{.*}}[ "funclet"(token 
[[FINALLY_PAD]]) ]
+// CHECK:       catchret from [[FINALLY_PAD]]
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[CLEANUP_PAD:%.*]] = cleanuppad within none []
+// CHECK:       invoke void @__cxa_end_catch() [ "funclet"(token 
[[CLEANUP_PAD]]) ]
+
+// CHECK-LABEL: define{{.*}} @finallyNoCatch
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK:       catchswitch within none
+// CHECK:       catchpad within
+// CHECK:       finally.catchall:
+// CHECK:       call ptr @__cxa_begin_catch
+// CHECK:       catchret from
+// CHECK:       br label %cleanup
+
+// CHECK-LABEL: define{{.*}} @throwInTryFinally
+// CHECK:       invoke void @objc_exception_throw
+// CHECK:       catchpad within
+// CHECK:       finally.catchall:
+// CHECK:       call ptr @__cxa_begin_catch
+// CHECK:       invoke void @__cxa_rethrow()
+
+// CHECK-LABEL: define{{.*}} @throwInCatchFinally
+// CHECK:       catchswitch within none
+// CHECK:       invoke void @__cxa_rethrow()
+// CHECK:       finally.catchall:
+// CHECK:       catchret from
+
+// CHECK-LABEL: define{{.*}} @throwInFinally
+// CHECK:       invoke void @objc_exception_throw
+// CHECK:       catchswitch within none
+// CHECK:       finally.catchall:
+
+// CHECK-LABEL: define{{.*}} @nestedTryCatchFinally
+// CHECK:       catchswitch within none
+// CHECK:       catchswitch within none
+// CHECK:       finally.catchall{{.*}}:
+// CHECK:       finally.catchall{{.*}}:
+
+// CHECK-LABEL: define{{.*}} @gotoOutFinally
+// CHECK:       store i32 1, ptr %value
+// CHECK:       store i32 3, ptr %cleanup.dest.slot
+// CHECK:       br i1 {{.*}}, label %finally.rethrow, label %finally.cont
+// CHECK:       ret i32
+
+// CHECK-LABEL: define{{.*}} @gotoOutCatchFinally
+// CHECK:       catchswitch within none
+// CHECK:       catchret from
+// CHECK:       br label %cleanup
diff --git a/clang/test/CodeGenObjC/wasm32-eh-arc.m 
b/clang/test/CodeGenObjC/wasm32-eh-arc.m
deleted file mode 100644
index 1100e3bfbf28d..0000000000000
--- a/clang/test/CodeGenObjC/wasm32-eh-arc.m
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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
deleted file mode 100644
index e35d81de51bf5..0000000000000
--- a/clang/test/CodeGenObjC/wasm32-eh.m
+++ /dev/null
@@ -1,202 +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 --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/gnustep2-wasm32-arc-cxx-catch.mm 
b/clang/test/CodeGenObjCXX/gnustep2-wasm32-arc-cxx-catch.mm
new file mode 100644
index 0000000000000..846b13afc8549
--- /dev/null
+++ b/clang/test/CodeGenObjCXX/gnustep2-wasm32-arc-cxx-catch.mm
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -target-feature +exception-handling -triple 
wasm32-unknown-emscripten -fobjc-runtime=gnustep-2.2 -fobjc-arc -fexceptions 
-fobjc-exceptions -fcxx-exceptions -exception-model=wasm -mllvm -wasm-enable-eh 
-emit-llvm -o - %s | FileCheck %s
+
+__attribute__((objc_root_class)) @interface Object @end
+extern void mayThrowCXX();
+
+int arcObjectAcrossCxxCatch(Object *object) {
+  try {
+    Object *local = object;
+    mayThrowCXX();
+    (void)local;
+  } catch (...) {
+    return object != (Object *)0;
+  }
+  return 0;
+}
+
+// CHECK-LABEL: define{{.*}} @_Z23arcObjectAcrossCxxCatchP6Object
+// CHECK: [[CATCHPAD:%.*]] = catchpad within
+// CHECK: call void @__cxa_end_catch() [ "funclet"(token [[CATCHPAD]]) ]
+// CHECK: catchret from [[CATCHPAD]]
+// CHECK: call void @llvm.objc.storeStrong(ptr {{.*}}, ptr null)
diff --git a/clang/test/CodeGenObjCXX/gnustep2-wasm32-cxx-destructor.mm 
b/clang/test/CodeGenObjCXX/gnustep2-wasm32-cxx-destructor.mm
new file mode 100644
index 0000000000000..104937143c639
--- /dev/null
+++ b/clang/test/CodeGenObjCXX/gnustep2-wasm32-cxx-destructor.mm
@@ -0,0 +1,28 @@
+// 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 %s
+
+struct Guard {
+  ~Guard() noexcept(false);
+};
+
+extern void mayThrowCXX();
+
+int cxxDestructorsAroundCatch() {
+  try {
+    Guard guard;
+    mayThrowCXX();
+  } catch (...) {
+    Guard caught;
+    return 1;
+  }
+  return 0;
+}
+
+// CHECK-LABEL: define{{.*}} @_Z25cxxDestructorsAroundCatchv
+// CHECK: [[TRY_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK: invoke{{.*}} @_ZN5GuardD1Ev{{.*}}[ "funclet"(token [[TRY_CLEANUP]]) ]
+// CHECK: cleanupret from [[TRY_CLEANUP]]
+// CHECK: [[CATCHPAD:%.*]] = catchpad within
+// CHECK: invoke{{.*}} @_ZN5GuardD1Ev{{.*}}[ "funclet"(token [[CATCHPAD]]) ]
+// CHECK: catchret from [[CATCHPAD]]
+// CHECK: [[CATCH_CLEANUP:%.*]] = cleanuppad within [[CATCHPAD]] []
+// CHECK: cleanupret from [[CATCH_CLEANUP]]
diff --git a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm 
b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm
deleted file mode 100644
index 4b989e6122fdd..0000000000000
--- a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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: [[CLEANUPPAD:%.*]] = cleanuppad within none []
-// CHECK: invoke{{.*}} @_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token 
[[CLEANUPPAD]]) ]
-// 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 %{{.*}}
-
-__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: [[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: invoke void @llvm.wasm.rethrow(){{.*}}[ "funclet"(token 
[[CATCHPAD]]) ]
-// CHECK: [[OUTER_SWITCH:%.*]] = catchswitch within none [label %{{.*}}] 
unwind to caller
-// CHECK: [[OUTER_PAD:%.*]] = catchpad within [[OUTER_SWITCH]] [ptr null]
diff --git a/clang/test/SemaObjC/wasm-try-goto.m 
b/clang/test/SemaObjC/wasm-try-goto.m
new file mode 100644
index 0000000000000..2b6535feeb791
--- /dev/null
+++ b/clang/test/SemaObjC/wasm-try-goto.m
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -triple wasm32-unknown-emscripten 
-fobjc-runtime=gnustep-2.2 -fobjc-exceptions -fsyntax-only -verify %s
+
+void gotoIntoTry(void) {
+  goto try_label; // expected-error{{cannot jump from this goto statement to 
its label}}
+  @try { // expected-note{{jump bypasses initialization of @try block}}
+  try_label:
+    ;
+  } @finally {
+  }
+}
+
+void gotoIntoCatch(void) {
+  goto catch_label; // expected-error{{cannot jump from this goto statement to 
its label}}
+  @try {
+  } @catch (...) { // expected-note{{jump bypasses initialization of @catch 
block}}
+  catch_label:
+    ;
+  } @finally {
+  }
+}
+
+void gotoIntoFinally(void) {
+  goto finally_label; // expected-error{{cannot jump from this goto statement 
to its label}}
+  @try {
+  } @finally { // expected-note{{jump bypasses initialization of @finally 
block}}
+  finally_label:
+    ;
+  }
+}
+
+void gotoOutOfFinally(void) {
+  @try {
+    goto done;
+  } @finally {
+  }
+done:
+  ;
+}
+
+void gotoOutOfCatch(void) {
+  @try {
+  } @catch (...) {
+    goto done;
+  } @finally {
+  }
+done:
+  ;
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to