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

From 03fcf7441eb80b8d97b1d774ce37efa24b3bba0b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]>
Date: Wed, 26 Aug 2026 19:44:16 +0200
Subject: [PATCH 1/2] [CodeGen][ObjC] Implement @finally for WebAssembly EH

---
 clang/lib/CodeGen/CGException.cpp             |  39 +-
 clang/lib/CodeGen/CGObjCRuntime.cpp           |  13 +-
 .../CodeGenObjC/gnustep2-wasm32-finally.m     | 699 ++++++++++++++++++
 clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm  | 155 ++++
 4 files changed, 892 insertions(+), 14 deletions(-)
 create mode 100644 clang/test/CodeGenObjC/gnustep2-wasm32-finally.m

diff --git a/clang/lib/CodeGen/CGException.cpp 
b/clang/lib/CodeGen/CGException.cpp
index bc09fe767de45..fe6b050ac9100 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -1403,6 +1403,7 @@ namespace {
 
       // If the end of the finally is reachable, check whether this was
       // for EH.  If so, rethrow.
+      const bool IsWasm = EHPersonality::get(CGF).isWasmPersonality();
       if (CGF.HaveInsertPoint()) {
         llvm::BasicBlock *RethrowBB = CGF.createBasicBlock("finally.rethrow");
         llvm::BasicBlock *ContBB = CGF.createBasicBlock("finally.cont");
@@ -1412,15 +1413,15 @@ namespace {
         CGF.Builder.CreateCondBr(ShouldRethrow, RethrowBB, ContBB);
 
         CGF.EmitBlock(RethrowBB);
-        if (SavedExnVar) {
-          CGF.EmitRuntimeCallOrInvoke(RethrowFn, CGF.Builder.CreateAlignedLoad(
-                                                     CGF.Int8PtrTy, 
SavedExnVar,
-                                                     CGF.getPointerAlign()));
-
+        if (IsWasm || !SavedExnVar) {
+          CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {});
         } else {
-          CGF.EmitRuntimeCallOrInvoke(RethrowFn);
+          CGF.EmitRuntimeCallOrInvoke(
+              RethrowFn,
+              CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar,
+                                            CGF.getPointerAlign()));
+          CGF.Builder.CreateUnreachable();
         }
-        CGF.Builder.CreateUnreachable();
 
         CGF.EmitBlock(ContBB);
 
@@ -1506,15 +1507,34 @@ void CodeGenFunction::FinallyInfo::exit(CodeGenFunction 
&CGF) {
   EHCatchScope &catchScope = cast<EHCatchScope>(*CGF.EHStack.begin());
   llvm::BasicBlock *catchBB = catchScope.getHandler(0).Block;
 
+  llvm::BasicBlock *DispatchBlock = nullptr;
+  if (catchScope.hasEHBranches())
+    DispatchBlock = catchScope.getCachedEHDispatchBlock();
   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.
@@ -1535,6 +1555,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 099622f690678..e539e905a5d00 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -150,22 +150,18 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
   if (S.getNumCatchStmts())
     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 (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) {
-    if (!useFunclets) {
+    if (!IsMSVC) {
       // 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");
-    } else if (IsMSVC) {
+    } else {
       CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true);
       if (!CGF.CurSEHParent)
         CGF.CurSEHParent = cast<NamedDecl>(CGF.CurFuncDecl);
@@ -224,6 +220,7 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
   }
 
   // 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;
@@ -312,8 +309,10 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
   CGF.Builder.restoreIP(SavedIP);
 
   // Pop out of the finally.
-  if (!useFunclets && S.getFinallyStmt())
+  if (!IsMSVC && S.getFinallyStmt()) {
+    CGF.CurrentFuncletPad = SavedFuncletPad;
     FinallyInfo.exit(CGF);
+  }
 
   if (Cont.isValid())
     CGF.EmitBlock(Cont.getBlock());
diff --git a/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m 
b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m
new file mode 100644
index 0000000000000..ddd145ea3ae15
--- /dev/null
+++ b/clang/test/CodeGenObjC/gnustep2-wasm32-finally.m
@@ -0,0 +1,699 @@
+// REQUIRES: webassembly-registered-target
+// 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();
+
+void emptyFinally(void) {
+  @try {
+    mayThrowObjC();
+  } @finally {
+  }
+}
+
+// CHECK-LABEL: define{{.*}} @emptyFinally
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK:       catch.dispatch:
+// CHECK-NEXT:  [[EMPTY_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind to caller
+// CHECK:       catch.start:
+// CHECK-NEXT:  [[EMPTY_PAD:%.*]] = catchpad within [[EMPTY_SWITCH]] [ptr null]
+// CHECK:       br label %finally.catchall
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       cleanup:
+// CHECK-NEXT:  %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup
+// CHECK:       finally.cont:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:    i32 0, label %cleanup.cont
+// CHECK-NEXT:    i32 2, label %unreachable
+// CHECK-NEXT:  ]
+// CHECK:       cleanup.cont:
+// CHECK-NEXT:  ret void
+// CHECK:       finally.catchall:
+// CHECK-NEXT:  %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh
+// CHECK-NEXT:  store i32 2, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[EMPTY_PAD]] to label %catchret.dest
+// CHECK:       catchret.dest:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[EMPTY_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch1, label 
%finally.cleanup.cont
+// CHECK:       finally.endcatch1:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont2 unwind label %terminate
+// CHECK:       invoke.cont2:
+// CHECK-NEXT:  br label %finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[EMPTY_CLEANUP]] unwind to caller
+
+int finallySimple(Object *object) {
+  int value = 0;
+  @try {
+    mayThrowObjC();
+    value = 1;
+  } @catch (...) {
+    value = 2;
+  } @finally {
+    value += object != (Object *)0;
+  }
+  return value;
+}
+
+// CHECK-LABEL: define{{.*}} @finallySimple
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK:       catch.dispatch:
+// CHECK-NEXT:  [[SIMPLE_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind label %catch.dispatch2
+// CHECK:       catch.start:
+// CHECK-NEXT:  [[SIMPLE_PAD:%.*]] = catchpad within [[SIMPLE_SWITCH]] [ptr 
null]
+// CHECK:       br label %catch
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  store i32 1, ptr %value
+// CHECK-NEXT:  store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       cleanup:
+// CHECK:       %add = add nsw i32
+// CHECK-NEXT:  store i32 %add, ptr %value
+// CHECK-NEXT:  %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup
+// CHECK:       finally.cont:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:    i32 0, label %cleanup.cont
+// CHECK-NEXT:    i32 2, label %eh.cont
+// CHECK-NEXT:    i32 3, label %unreachable
+// CHECK-NEXT:  ]
+// CHECK:       cleanup.cont:
+// CHECK-NEXT:  br label %eh.cont
+// CHECK:       eh.cont:
+// CHECK-NEXT:  %{{.*}} = load i32, ptr %value
+// CHECK-NEXT:  ret i32 %{{.*}}
+// CHECK:       catch:
+// CHECK-NEXT:  %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %exn.adjusted = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT:  store i32 2, ptr %value
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont1 unwind label %catch.dispatch2
+// CHECK:       catch.dispatch2:
+// CHECK-NEXT:  [[SIMPLE_FINALLY_SWITCH:%.*]] = catchswitch within none [label 
%catch.start3] unwind to caller
+// CHECK:       catch.start3:
+// CHECK-NEXT:  [[SIMPLE_FINALLY_PAD:%.*]] = catchpad within 
[[SIMPLE_FINALLY_SWITCH]] [ptr null]
+// CHECK:       br label %finally.catchall
+// CHECK:       invoke.cont1:
+// CHECK-NEXT:  catchret from [[SIMPLE_PAD]] to label %catchret.dest
+// CHECK:       catchret.dest:
+// CHECK-NEXT:  store i32 2, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       finally.catchall:
+// CHECK-NEXT:  %exn4 = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn4)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh
+// CHECK-NEXT:  store i32 3, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[SIMPLE_FINALLY_PAD]] to label %catchret.dest5
+// CHECK:       catchret.dest5:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[SIMPLE_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch6, label 
%finally.cleanup.cont
+// CHECK:       finally.endcatch6:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont7 unwind label %terminate
+// CHECK:       invoke.cont7:
+// CHECK-NEXT:  br label %finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[SIMPLE_CLEANUP]] unwind to caller
+
+int finallyNoCatch(Object *object) {
+  int value = 0;
+  @try {
+    mayThrowObjC();
+    value = 1;
+  } @finally {
+    value += object != (Object *)0;
+  }
+  return value;
+}
+
+// CHECK-LABEL: define{{.*}} @finallyNoCatch
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK:       catch.dispatch:
+// CHECK-NEXT:  [[NO_CATCH_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind to caller
+// CHECK:       catch.start:
+// CHECK-NEXT:  [[NO_CATCH_PAD:%.*]] = catchpad within [[NO_CATCH_SWITCH]] 
[ptr null]
+// CHECK:       br label %finally.catchall
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  store i32 1, ptr %value
+// CHECK-NEXT:  store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       cleanup:
+// CHECK:       %add = add nsw i32
+// CHECK-NEXT:  store i32 %add, ptr %value
+// CHECK-NEXT:  %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup
+// CHECK:       finally.cont:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:    i32 0, label %cleanup.cont
+// CHECK-NEXT:    i32 2, label %unreachable
+// CHECK-NEXT:  ]
+// CHECK:       cleanup.cont:
+// CHECK-NEXT:  %{{.*}} = load i32, ptr %value
+// CHECK-NEXT:  ret i32 %{{.*}}
+// CHECK:       finally.catchall:
+// CHECK-NEXT:  %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh
+// CHECK-NEXT:  store i32 2, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[NO_CATCH_PAD]] to label %catchret.dest
+// CHECK:       catchret.dest:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[NO_CATCH_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch1, label 
%finally.cleanup.cont
+// CHECK:       finally.endcatch1:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont2 unwind label %terminate
+// CHECK:       invoke.cont2:
+// CHECK-NEXT:  br label %finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[NO_CATCH_CLEANUP]] unwind to caller
+
+int throwInTryFinally(Object *object) {
+  int value = 0;
+  @try {
+    @throw (id)0;
+  } @catch (...) {
+    value = 1;
+  } @finally {
+    value += object != (Object *)0;
+  }
+  return value;
+}
+
+// CHECK-LABEL: define{{.*}} @throwInTryFinally
+// CHECK:       invoke void @objc_exception_throw
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK:       catch.dispatch:
+// CHECK-NEXT:  [[TRY_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind label %catch.dispatch2
+// CHECK:       catch.start:
+// CHECK-NEXT:  [[TRY_PAD:%.*]] = catchpad within [[TRY_SWITCH]] [ptr null]
+// CHECK:       br label %catch
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  unreachable
+// CHECK:       catch:
+// CHECK-NEXT:  %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %exn.adjusted = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT:  store i32 1, ptr %value
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont1 unwind label %catch.dispatch2
+// CHECK:       catch.dispatch2:
+// CHECK-NEXT:  [[TRY_FINALLY_SWITCH:%.*]] = catchswitch within none [label 
%catch.start3] unwind to caller
+// CHECK:       catch.start3:
+// CHECK-NEXT:  [[TRY_FINALLY_PAD:%.*]] = catchpad within 
[[TRY_FINALLY_SWITCH]] [ptr null]
+// CHECK:       br label %finally.catchall
+// CHECK:       invoke.cont1:
+// CHECK-NEXT:  catchret from [[TRY_PAD]] to label %catchret.dest
+// CHECK:       catchret.dest:
+// CHECK-NEXT:  store i32 2, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       finally.catchall:
+// CHECK-NEXT:  %exn4 = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn4)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh
+// CHECK-NEXT:  store i32 3, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[TRY_FINALLY_PAD]] to label %catchret.dest5
+// CHECK:       catchret.dest5:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       cleanup:
+// CHECK:       %add = add nsw i32
+// CHECK-NEXT:  store i32 %add, ptr %value
+// CHECK-NEXT:  %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup
+// CHECK:       finally.cont:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:    i32 2, label %eh.cont
+// CHECK-NEXT:    i32 3, label %unreachable
+// CHECK-NEXT:  ]
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[TRY_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch6, label 
%finally.cleanup.cont
+// CHECK:       finally.endcatch6:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont7 unwind label %terminate
+// CHECK:       invoke.cont7:
+// CHECK-NEXT:  br label %finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[TRY_CLEANUP]] unwind to caller
+// CHECK:       eh.cont:
+// CHECK-NEXT:  %{{.*}} = load i32, ptr %value
+// CHECK-NEXT:  ret i32 %{{.*}}
+
+int throwInCatchFinally(Object *object) {
+  @try {
+    mayThrowObjC();
+  } @catch (...) {
+    @throw;
+  } @finally {
+    (void)object;
+  }
+}
+
+// CHECK-LABEL: define{{.*}} @throwInCatchFinally
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK:       catch.dispatch:
+// CHECK-NEXT:  [[CATCH_THROW_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind label %catch.dispatch2
+// CHECK:       catch.start:
+// CHECK-NEXT:  [[CATCH_THROW_PAD:%.*]] = catchpad within 
[[CATCH_THROW_SWITCH]] [ptr null]
+// CHECK:       br label %catch
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       cleanup:
+// CHECK-NEXT:  %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %{{.*}} = load ptr, ptr %object.addr
+// CHECK-NEXT:  %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup5
+// CHECK:       finally.cont:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:    i32 0, label %cleanup.cont
+// CHECK-NEXT:    i32 3, label %unreachable
+// CHECK-NEXT:  ]
+// CHECK:       cleanup.cont:
+// CHECK-NEXT:  br label %eh.cont
+// CHECK:       eh.cont:
+// CHECK-NEXT:  %{{.*}} = load i32, ptr %retval
+// CHECK-NEXT:  ret i32 %{{.*}}
+// CHECK:       catch:
+// CHECK-NEXT:  %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %exn.adjusted = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[CATCH_THROW_CLEANUP:%.*]] = cleanuppad within 
[[CATCH_THROW_PAD]] []
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont1 unwind label %terminate
+// CHECK:       invoke.cont1:
+// CHECK-NEXT:  cleanupret from [[CATCH_THROW_CLEANUP]] unwind label 
%catch.dispatch2
+// CHECK:       catch.dispatch2:
+// CHECK-NEXT:  [[CATCH_FINALLY_SWITCH:%.*]] = catchswitch within none [label 
%catch.start3] unwind to caller
+// CHECK:       catch.start3:
+// CHECK-NEXT:  [[CATCH_FINALLY_PAD:%.*]] = catchpad within 
[[CATCH_FINALLY_SWITCH]] [ptr null]
+// CHECK:       br label %finally.catchall
+// CHECK:       finally.catchall:
+// CHECK-NEXT:  %exn4 = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn4)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh
+// CHECK-NEXT:  store i32 3, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[CATCH_FINALLY_PAD]] to label %catchret.dest
+// CHECK:       catchret.dest:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       ehcleanup5:
+// CHECK-NEXT:  [[CATCH_FINALLY_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch6, label 
%finally.cleanup.cont
+// CHECK:       finally.endcatch6:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont7 unwind label %terminate8
+// CHECK:       invoke.cont7:
+// CHECK-NEXT:  br label %finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[CATCH_FINALLY_CLEANUP]] unwind to caller
+
+int throwInFinally(Object *object) {
+  @try {
+    mayThrowObjC();
+  } @finally {
+    @throw object;
+  }
+}
+
+// CHECK-LABEL: define{{.*}} @throwInFinally
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK:       catch.dispatch:
+// CHECK-NEXT:  [[THROW_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind to caller
+// CHECK:       catch.start:
+// CHECK-NEXT:  [[THROW_PAD:%.*]] = catchpad within [[THROW_SWITCH]] [ptr null]
+// CHECK:       br label %finally.catchall
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       cleanup:
+// CHECK-NEXT:  %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %{{.*}} = load ptr, ptr %object.addr
+// CHECK-NEXT:  invoke void @objc_exception_throw(ptr %{{.*}})
+// CHECK-NEXT:          to label %invoke.cont1 unwind label %ehcleanup
+// CHECK:       invoke.cont1:
+// CHECK-NEXT:  unreachable
+// CHECK:       finally.catchall:
+// CHECK-NEXT:  %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh
+// CHECK-NEXT:  store i32 2, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[THROW_PAD]] to label %catchret.dest
+// CHECK:       catchret.dest:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[THROW_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch2, label 
%finally.cleanup.cont
+// CHECK:       finally.endcatch2:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont3 unwind label %terminate
+// CHECK:       invoke.cont3:
+// CHECK-NEXT:  br label %finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[THROW_CLEANUP]] unwind to caller
+// CHECK-NOT:  finally.rethrow:
+
+int throwInFinallyNoException(Object *object) {
+  @try {
+  } @finally {
+    @throw object;
+  }
+}
+
+// CHECK-LABEL: define{{.*}} @throwInFinallyNoException
+// CHECK:       entry:
+// CHECK:       %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %{{.*}} = load ptr, ptr %object.addr
+// CHECK-NEXT:  invoke void @objc_exception_throw(ptr %{{.*}})
+// CHECK-NEXT:          to label %invoke.cont unwind label %ehcleanup
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  unreachable
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[NO_EXCEPTION_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch1, label 
%finally.cleanup.cont
+// CHECK:       finally.endcatch1:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont2 unwind label %terminate
+// CHECK:       invoke.cont2:
+// CHECK-NEXT:  br label %finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[NO_EXCEPTION_CLEANUP]] unwind to caller
+// CHECK-NOT:  catchswitch within none
+// CHECK-NOT:  finally.rethrow:
+
+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;
+}
+
+// CHECK-LABEL: define{{.*}} @nestedTryCatchFinally
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK:       catch.dispatch:
+// CHECK-NEXT:  [[NEST_OUTER_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind label %catch.dispatch3
+// CHECK:       catch.start:
+// CHECK-NEXT:  [[NEST_OUTER_PAD:%.*]] = catchpad within [[NEST_OUTER_SWITCH]] 
[ptr null]
+// CHECK:       br label %catch
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       cleanup:
+// CHECK-NEXT:  %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot
+// CHECK:       %add = add nsw i32
+// CHECK-NEXT:  store i32 %add, ptr %value
+// CHECK-NEXT:  %finally.shouldthrow = load i1, ptr %finally.for-eh1
+// CHECK-NEXT:  br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup
+// CHECK:       finally.cont:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:    i32 0, label %cleanup.cont
+// CHECK-NEXT:    i32 4, label %eh.cont
+// CHECK-NEXT:    i32 5, label %unreachable
+// CHECK-NEXT:  ]
+// CHECK:       cleanup.cont:
+// CHECK-NEXT:  br label %eh.cont
+// CHECK:       eh.cont:
+// CHECK-NEXT:  store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup22
+// CHECK:       cleanup22:
+// CHECK-NEXT:  %cleanup.dest.saved23 = load i32, ptr %cleanup.dest.slot
+// CHECK:       %add24 = add nsw i32
+// CHECK-NEXT:  store i32 %add24, ptr %value
+// CHECK-NEXT:  %finally.shouldthrow25 = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.shouldthrow25, label %finally.rethrow26, label 
%finally.cont27
+// CHECK:       finally.rethrow26:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup28
+// CHECK:       finally.cont27:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved23, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest33 = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest33, label %unreachable [
+// CHECK-NEXT:    i32 0, label %cleanup.cont34
+// CHECK-NEXT:    i32 2, label %eh.cont35
+// CHECK-NEXT:    i32 3, label %unreachable
+// CHECK-NEXT:  ]
+// CHECK:       cleanup.cont34:
+// CHECK-NEXT:  br label %eh.cont35
+// CHECK:       eh.cont35:
+// CHECK-NEXT:  %{{.*}} = load i32, ptr %value
+// CHECK-NEXT:  ret i32 %{{.*}}
+// CHECK:       catch:
+// CHECK-NEXT:  %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT:  store i32 1, ptr %value
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont2 unwind label %catch.dispatch3
+// CHECK:       catch.dispatch3:
+// CHECK-NEXT:  [[NEST_INNER_SWITCH:%.*]] = catchswitch within none [label 
%catch.start4] unwind label %catch.dispatch9
+// CHECK:       catch.start4:
+// CHECK-NEXT:  [[NEST_INNER_PAD:%.*]] = catchpad within [[NEST_INNER_SWITCH]] 
[ptr null]
+// CHECK:       br label %finally.catchall
+// CHECK:       invoke.cont2:
+// CHECK-NEXT:  catchret from [[NEST_OUTER_PAD]] to label %catchret.dest
+// CHECK:       catchret.dest:
+// CHECK-NEXT:  store i32 4, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       finally.catchall:
+// CHECK-NEXT:  %exn5 = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn5)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh1
+// CHECK-NEXT:  store i32 5, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[NEST_INNER_PAD]] to label %catchret.dest6
+// CHECK:       catchret.dest6:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[NEST_INNER_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh1
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch7, label 
%finally.cleanup.cont
+// CHECK:       finally.endcatch7:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont8 unwind label %terminate
+// CHECK:       invoke.cont8:
+// CHECK-NEXT:  br label %finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[NEST_INNER_CLEANUP]] unwind label 
%catch.dispatch9
+// CHECK:       catch.dispatch9:
+// CHECK-NEXT:  [[NEST_OUTER_FINALLY_SWITCH:%.*]] = catchswitch within none 
[label %catch.start10] unwind label %catch.dispatch16
+// CHECK:       catch.start10:
+// CHECK-NEXT:  [[NEST_OUTER_FINALLY_PAD:%.*]] = catchpad within 
[[NEST_OUTER_FINALLY_SWITCH]] [ptr null]
+// CHECK:       br label %catch11
+// CHECK:       catch11:
+// CHECK-NEXT:  %exn12 = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn12)
+// CHECK-NEXT:  store i32 3, ptr %value
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont14 unwind label %catch.dispatch16
+// CHECK:       catch.dispatch16:
+// CHECK-NEXT:  [[NEST_FALLTHROUGH_SWITCH:%.*]] = catchswitch within none 
[label %catch.start17] unwind to caller
+// CHECK:       catch.start17:
+// CHECK-NEXT:  [[NEST_FALLTHROUGH_PAD:%.*]] = catchpad within 
[[NEST_FALLTHROUGH_SWITCH]] [ptr null]
+// CHECK:       br label %finally.catchall18
+// CHECK:       invoke.cont14:
+// CHECK-NEXT:  catchret from [[NEST_OUTER_FINALLY_PAD]] to label 
%catchret.dest15
+// CHECK:       catchret.dest15:
+// CHECK-NEXT:  store i32 2, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup22
+// CHECK:       finally.catchall18:
+// CHECK-NEXT:  %exn19 = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn19)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh
+// CHECK-NEXT:  store i32 3, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[NEST_FALLTHROUGH_PAD]] to label 
%catchret.dest21
+// CHECK:       catchret.dest21:
+// CHECK-NEXT:  br label %cleanup22
+// CHECK:       ehcleanup28:
+// CHECK-NEXT:  [[NEST_OUTER_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch29 = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch29, label %finally.endcatch30, label 
%finally.cleanup.cont32
+// CHECK:       finally.endcatch30:
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont31 unwind label %terminate36
+// CHECK:       invoke.cont31:
+// CHECK-NEXT:  br label %finally.cleanup.cont32
+// CHECK:       finally.cleanup.cont32:
+// CHECK-NEXT:  cleanupret from [[NEST_OUTER_CLEANUP]] unwind to caller
+
+int gotoOutFinally(Object *object) {
+  int value = 0;
+  @try {
+    value = 1;
+    goto done;
+  } @finally {
+    value += object != (Object *)0;
+  }
+done:
+  return value;
+}
+
+// CHECK-LABEL: define{{.*}} @gotoOutFinally
+// CHECK:       entry:
+// CHECK:       store i32 1, ptr %value
+// CHECK-NEXT:  store i32 3, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot
+// CHECK:       %add = add nsw i32
+// CHECK-NEXT:  store i32 %add, ptr %value
+// CHECK-NEXT:  %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup
+// CHECK:       finally.cont:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:    i32 3, label %done
+// CHECK-NEXT:  ]
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[GOTO_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch1, label 
%finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[GOTO_CLEANUP]] unwind to caller
+// CHECK:       done:
+// CHECK-NEXT:  %{{.*}} = load i32, ptr %value
+// CHECK-NEXT:  ret i32 %{{.*}}
+
+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{{.*}} @gotoOutCatchFinally
+// CHECK:       invoke void @mayThrowObjC()
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK:       catch.dispatch:
+// CHECK-NEXT:  [[GOTO_CATCH_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind label %catch.dispatch2
+// CHECK:       catch.start:
+// CHECK-NEXT:  [[GOTO_CATCH_PAD:%.*]] = catchpad within [[GOTO_CATCH_SWITCH]] 
[ptr null]
+// CHECK:       br label %catch
+// CHECK:       invoke.cont:
+// CHECK-NEXT:  store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       cleanup:
+// CHECK:       %add = add nsw i32
+// CHECK-NEXT:  store i32 %add, ptr %value
+// CHECK-NEXT:  %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK:       finally.rethrow:
+// CHECK-NEXT:  invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup
+// CHECK:       finally.cont:
+// CHECK-NEXT:  store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT:  %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT:  switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT:    i32 0, label %cleanup.cont
+// CHECK-NEXT:    i32 3, label %unreachable
+// CHECK-NEXT:    i32 4, label %done
+// CHECK-NEXT:  ]
+// CHECK:       cleanup.cont:
+// CHECK-NEXT:  br label %eh.cont
+// CHECK:       eh.cont:
+// CHECK-NEXT:  br label %done
+// CHECK:       done:
+// CHECK-NEXT:  %{{.*}} = load i32, ptr %value
+// CHECK-NEXT:  ret i32 %{{.*}}
+// CHECK:       catch:
+// CHECK-NEXT:  %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT:  store i32 1, ptr %value
+// CHECK-NEXT:  store i32 4, ptr %cleanup.dest.slot
+// CHECK-NEXT:  invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont1 unwind label %catch.dispatch2
+// CHECK:       catch.dispatch2:
+// CHECK-NEXT:  [[GOTO_FINALLY_SWITCH:%.*]] = catchswitch within none [label 
%catch.start3] unwind to caller
+// CHECK:       catch.start3:
+// CHECK-NEXT:  [[GOTO_FINALLY_PAD:%.*]] = catchpad within 
[[GOTO_FINALLY_SWITCH]] [ptr null]
+// CHECK:       br label %finally.catchall
+// CHECK:       invoke.cont1:
+// CHECK-NEXT:  catchret from [[GOTO_CATCH_PAD]] to label %catchret.dest
+// CHECK:       catchret.dest:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       finally.catchall:
+// CHECK-NEXT:  %exn4 = load ptr, ptr %exn.slot
+// CHECK-NEXT:  %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn4)
+// CHECK-NEXT:  store i1 true, ptr %finally.for-eh
+// CHECK-NEXT:  store i32 3, ptr %cleanup.dest.slot
+// CHECK-NEXT:  catchret from [[GOTO_FINALLY_PAD]] to label %catchret.dest5
+// CHECK:       catchret.dest5:
+// CHECK-NEXT:  br label %cleanup
+// CHECK:       ehcleanup:
+// CHECK-NEXT:  [[GOTO_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT:  %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT:  br i1 %finally.endcatch, label %finally.endcatch6, label 
%finally.cleanup.cont
+// CHECK:       finally.cleanup.cont:
+// CHECK-NEXT:  cleanupret from [[GOTO_CLEANUP]] unwind to caller
diff --git a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm 
b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm
index 3af09f7055b98..958c656bbf1fd 100644
--- a/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm
+++ b/clang/test/CodeGenObjCXX/wasm32-eh-objcxx.mm
@@ -33,6 +33,161 @@ int cxxDestructorsAroundCatch() {
 
 extern void mayThrowObjC();
 
+int cleanupInTryFinally() {
+  @try {
+    ThrowingDestructor object;
+    mayThrowObjC();
+  } @finally {
+  }
+  return 0;
+}
+
+// CHECK-LABEL: define{{.*}} @_Z{{[0-9]+}}cleanupInTryFinallyv
+// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv()
+// CHECK-NEXT:          to label %invoke.cont unwind label %ehcleanup
+// CHECK: invoke.cont:
+// CHECK-NEXT: %{{.*}} = invoke noundef ptr 
@_ZN18ThrowingDestructorD1Ev{{.*}}%object
+// CHECK-NEXT:          to label %invoke.cont1 unwind label %catch.dispatch
+// CHECK: invoke.cont1:
+// CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT: br label %cleanup
+// CHECK: cleanup:
+// CHECK-NEXT: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT: %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK: finally.rethrow:
+// CHECK-NEXT: invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup4
+// CHECK: finally.cont:
+// CHECK-NEXT: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT: %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT: switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT: i32 0, label %cleanup.cont
+// CHECK-NEXT: i32 2, label %unreachable
+// CHECK-NEXT: ]
+// CHECK: cleanup.cont:
+// CHECK-NEXT: ret i32 0
+// CHECK: ehcleanup:
+// CHECK-NEXT: [[TRY_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT: %{{.*}} = invoke noundef ptr 
@_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[TRY_CLEANUP]]) ]
+// CHECK-NEXT:          to label %invoke.cont2 unwind label %terminate
+// CHECK: invoke.cont2:
+// CHECK-NEXT: cleanupret from [[TRY_CLEANUP]] unwind label %catch.dispatch
+// CHECK: catch.dispatch:
+// CHECK-NEXT: [[TRY_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind to caller
+// CHECK: catch.start:
+// CHECK-NEXT: [[TRY_PAD:%.*]] = catchpad within [[TRY_SWITCH]] [ptr null]
+// CHECK: br label %finally.catchall
+// CHECK: finally.catchall:
+// CHECK-NEXT: %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT: %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT: store i1 true, ptr %finally.for-eh
+// CHECK-NEXT: store i32 2, ptr %cleanup.dest.slot
+// CHECK-NEXT: catchret from [[TRY_PAD]] to label %catchret.dest
+// CHECK: catchret.dest:
+// CHECK-NEXT: br label %cleanup
+// CHECK: ehcleanup4:
+// CHECK-NEXT: [[TRY_FINALLY_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT: %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT: br i1 %finally.endcatch, label %finally.endcatch5, label 
%finally.cleanup.cont
+// CHECK: finally.endcatch5:
+// CHECK-NEXT: invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont6 unwind label %terminate7
+// CHECK: invoke.cont6:
+// CHECK-NEXT: br label %finally.cleanup.cont
+// CHECK: finally.cleanup.cont:
+// CHECK-NEXT: cleanupret from [[TRY_FINALLY_CLEANUP]] unwind to caller
+
+int cleanupInCatchFinally() {
+  @try {
+    mayThrowObjC();
+  } @catch (...) {
+    ThrowingDestructor object;
+    return 1;
+  } @finally {
+  }
+  return 0;
+}
+
+// CHECK-LABEL: define{{.*}} @_Z{{[0-9]+}}cleanupInCatchFinallyv
+// CHECK: invoke void @_Z{{[0-9]+}}mayThrowObjCv()
+// CHECK-NEXT:          to label %invoke.cont unwind label %catch.dispatch
+// CHECK: catch.dispatch:
+// CHECK-NEXT: [[CATCH_SWITCH:%.*]] = catchswitch within none [label 
%catch.start] unwind label %catch.dispatch4
+// CHECK: catch.start:
+// CHECK-NEXT: [[CATCH_PAD:%.*]] = catchpad within [[CATCH_SWITCH]] [ptr null]
+// CHECK: br label %catch
+// CHECK: invoke.cont:
+// CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot
+// CHECK-NEXT: br label %cleanup
+// CHECK: cleanup:
+// CHECK-NEXT: %cleanup.dest.saved = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT: %finally.shouldthrow = load i1, ptr %finally.for-eh
+// CHECK-NEXT: br i1 %finally.shouldthrow, label %finally.rethrow, label 
%finally.cont
+// CHECK: finally.rethrow:
+// CHECK-NEXT: invoke void @__cxa_rethrow()
+// CHECK-NEXT:          to label %unreachable unwind label %ehcleanup8
+// CHECK: finally.cont:
+// CHECK-NEXT: store i32 %cleanup.dest.saved, ptr %cleanup.dest.slot
+// CHECK-NEXT: %cleanup.dest = load i32, ptr %cleanup.dest.slot
+// CHECK-NEXT: switch i32 %cleanup.dest, label %unreachable [
+// CHECK-NEXT: i32 0, label %cleanup.cont
+// CHECK-NEXT: i32 1, label %return
+// CHECK-NEXT: i32 3, label %unreachable
+// CHECK-NEXT: ]
+// CHECK: cleanup.cont:
+// CHECK-NEXT: br label %eh.cont
+// CHECK: eh.cont:
+// CHECK-NEXT: store i32 0, ptr %retval
+// CHECK-NEXT: br label %return
+// CHECK: catch:
+// CHECK-NEXT: %exn = load ptr, ptr %exn.slot
+// CHECK-NEXT: %exn.adjusted = call ptr @__cxa_begin_catch(ptr %exn)
+// CHECK-NEXT: store i32 1, ptr %retval
+// CHECK-NEXT: store i32 1, ptr %cleanup.dest.slot
+// CHECK-NEXT: %{{.*}} = invoke noundef ptr 
@_ZN18ThrowingDestructorD1Ev{{.*}}[ "funclet"(token [[CATCH_PAD]]) ]
+// CHECK-NEXT:          to label %invoke.cont1 unwind label %ehcleanup
+// CHECK: invoke.cont1:
+// CHECK-NEXT: invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont2 unwind label %catch.dispatch4
+// CHECK: invoke.cont2:
+// CHECK-NEXT: catchret from [[CATCH_PAD]] to label %catchret.dest
+// CHECK: ehcleanup:
+// CHECK-NEXT: [[CATCH_CLEANUP:%.*]] = cleanuppad within [[CATCH_PAD]] []
+// CHECK-NEXT: invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont3 unwind label %terminate
+// CHECK: invoke.cont3:
+// CHECK-NEXT: cleanupret from [[CATCH_CLEANUP]] unwind label %catch.dispatch4
+// CHECK: catch.dispatch4:
+// CHECK-NEXT: [[FINALLY_SWITCH:%.*]] = catchswitch within none [label 
%catch.start5] unwind to caller
+// CHECK: catch.start5:
+// CHECK-NEXT: [[FINALLY_PAD:%.*]] = catchpad within [[FINALLY_SWITCH]] [ptr 
null]
+// CHECK: br label %finally.catchall
+// CHECK: catchret.dest:
+// CHECK-NEXT: br label %cleanup
+// CHECK: finally.catchall:
+// CHECK-NEXT: %exn6 = load ptr, ptr %exn.slot
+// CHECK-NEXT: %{{.*}} = call ptr @__cxa_begin_catch(ptr %exn6)
+// CHECK-NEXT: store i1 true, ptr %finally.for-eh
+// CHECK-NEXT: store i32 3, ptr %cleanup.dest.slot
+// CHECK-NEXT: catchret from [[FINALLY_PAD]] to label %catchret.dest7
+// CHECK: catchret.dest7:
+// CHECK-NEXT: br label %cleanup
+// CHECK: ehcleanup8:
+// CHECK-NEXT: [[CATCH_FINALLY_CLEANUP:%.*]] = cleanuppad within none []
+// CHECK-NEXT: %finally.endcatch = load i1, ptr %finally.for-eh
+// CHECK-NEXT: br i1 %finally.endcatch, label %finally.endcatch9, label 
%finally.cleanup.cont
+// CHECK: finally.endcatch9:
+// CHECK-NEXT: invoke void @__cxa_end_catch()
+// CHECK-NEXT:          to label %invoke.cont10 unwind label %terminate11
+// CHECK: invoke.cont10:
+// CHECK-NEXT: br label %finally.cleanup.cont
+// CHECK: finally.cleanup.cont:
+// CHECK-NEXT: cleanupret from [[CATCH_FINALLY_CLEANUP]] unwind to caller
+// CHECK: return:
+// CHECK-NEXT: %{{.*}} = load i32, ptr %retval
+// CHECK-NEXT: ret i32 %{{.*}}
+
 int combinedCxxObjcEH() {
   @try {
     try {

From c83ca6fe059b414b3782f81a8d21e156ddba45dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hendrik=20H=C3=BCbner?= <[email protected]>
Date: Thu, 27 Aug 2026 10:36:41 +0200
Subject: [PATCH 2/2] restore

---
 clang/lib/CodeGen/CGException.cpp | 21 ++++++++++-----------
 1 file changed, 10 insertions(+), 11 deletions(-)

diff --git a/clang/lib/CodeGen/CGException.cpp 
b/clang/lib/CodeGen/CGException.cpp
index fe6b050ac9100..ffa6af9a55ce6 100644
--- a/clang/lib/CodeGen/CGException.cpp
+++ b/clang/lib/CodeGen/CGException.cpp
@@ -1403,7 +1403,6 @@ namespace {
 
       // If the end of the finally is reachable, check whether this was
       // for EH.  If so, rethrow.
-      const bool IsWasm = EHPersonality::get(CGF).isWasmPersonality();
       if (CGF.HaveInsertPoint()) {
         llvm::BasicBlock *RethrowBB = CGF.createBasicBlock("finally.rethrow");
         llvm::BasicBlock *ContBB = CGF.createBasicBlock("finally.cont");
@@ -1413,15 +1412,15 @@ namespace {
         CGF.Builder.CreateCondBr(ShouldRethrow, RethrowBB, ContBB);
 
         CGF.EmitBlock(RethrowBB);
-        if (IsWasm || !SavedExnVar) {
-          CGF.EmitNoreturnRuntimeCallOrInvoke(RethrowFn, {});
+        if (SavedExnVar) {
+          CGF.EmitRuntimeCallOrInvoke(RethrowFn, CGF.Builder.CreateAlignedLoad(
+                                                     CGF.Int8PtrTy, 
SavedExnVar,
+                                                     CGF.getPointerAlign()));
+
         } else {
-          CGF.EmitRuntimeCallOrInvoke(
-              RethrowFn,
-              CGF.Builder.CreateAlignedLoad(CGF.Int8PtrTy, SavedExnVar,
-                                            CGF.getPointerAlign()));
-          CGF.Builder.CreateUnreachable();
+          CGF.EmitRuntimeCallOrInvoke(RethrowFn);
         }
+        CGF.Builder.CreateUnreachable();
 
         CGF.EmitBlock(ContBB);
 
@@ -1522,9 +1521,9 @@ void CodeGenFunction::FinallyInfo::exit(CodeGenFunction 
&CGF) {
     if (EHPersonality::get(CGF).isWasmPersonality() && DispatchBlock) {
       auto *CatchSwitch =
           cast<llvm::CatchSwitchInst>(DispatchBlock->getFirstNonPHIIt());
-      llvm::BasicBlock *CatchStartBlock =
-          CatchSwitch->hasUnwindDest() ? CatchSwitch->getSuccessor(1)
-                                       : CatchSwitch->getSuccessor(0);
+      llvm::BasicBlock *CatchStartBlock = CatchSwitch->hasUnwindDest()
+                                              ? CatchSwitch->getSuccessor(1)
+                                              : CatchSwitch->getSuccessor(0);
       CPI = cast<llvm::CatchPadInst>(CatchStartBlock->getFirstNonPHIIt());
       CGF.CurrentFuncletPad = CPI;
     }

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

Reply via email to