https://github.com/hmelder created 
https://github.com/llvm/llvm-project/pull/176779

When targeting a platform that does not have funclet-based EH, we push the 
finally cleanup (normal edge) and catchall (unwind edge) onto the EHStack 
_before_ pushing all catch handlers. The try statement is then emitted and 
catch handlers popped from EHStack. Last, the finally cleanup is popped from 
EHStack.

For funclet-based EH, we outline and push the finally funclet (of type 
`NormalAndEHCleanup`) onto the EHStack _after_ pushing the catch handlers and 
never pop it. This results in a crash during codegen when we try to emit the 
catch handlers. Not popping the finally cleanup from the EHStack results in 
incorrect calls to cleanup handlers in nested try/catch/finally statements.

I fixed the two issues by:
1. Pushing the finally cleanup first, and
2. Popping it at the end of `CGObjCRuntime::EmitTryCatchStmt`.




>From d4a718f4ad86349925b64788c0b3eb387f5d375e Mon Sep 17 00:00:00 2001
From: hmelder <[email protected]>
Date: Mon, 19 Jan 2026 13:57:33 +0000
Subject: [PATCH 1/2] [ObjC][SEH] Push cleanup before catch handlers

The cleanup funclet for an `@finally` statement needs to be pushed onto
EHStack before the catch handlers are pushed.
---
 clang/lib/CodeGen/CGObjCRuntime.cpp | 51 +++++++++++++++--------------
 1 file changed, 27 insertions(+), 24 deletions(-)

diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp 
b/clang/lib/CodeGen/CGObjCRuntime.cpp
index 3d47dc9560c65..923118d859d54 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -155,6 +155,26 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
       FinallyInfo.enter(CGF, Finally->getFinallyBody(),
                         beginCatchFn, endCatchFn, exceptionRethrowFn);
 
+  if (useFunclets)
+    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);
+
+      HelperCGF.FinishFunction(FinallyBlock->getEndLoc());
+
+      llvm::Function *FinallyFunc = HelperCGF.CurFn;
+
+      // Push a cleanup for __finally blocks.
+      CGF.pushSEHCleanup(NormalAndEHCleanup, FinallyFunc);
+    }
+
   SmallVector<CatchHandler, 8> Handlers;
 
 
@@ -187,28 +207,6 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
       Catch->setHandler(I, { Handlers[I].TypeInfo, Handlers[I].Flags }, 
Handlers[I].Block);
   }
 
-  if (useFunclets)
-    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);
-
-        HelperCGF.FinishFunction(FinallyBlock->getEndLoc());
-
-        llvm::Function *FinallyFunc = HelperCGF.CurFn;
-
-
-        // Push a cleanup for __finally blocks.
-        CGF.pushSEHCleanup(NormalAndEHCleanup, FinallyFunc);
-    }
-
-
   // Emit the try body.
   CGF.EmitStmt(S.getTryBody());
 
@@ -276,8 +274,13 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
   CGF.Builder.restoreIP(SavedIP);
 
   // Pop out of the finally.
-  if (!useFunclets && S.getFinallyStmt())
-    FinallyInfo.exit(CGF);
+  if (S.getFinallyStmt()) {
+    if (useFunclets) {
+      CGF.PopCleanupBlock();
+    } else {
+      FinallyInfo.exit(CGF);
+    }
+  }
 
   if (Cont.isValid())
     CGF.EmitBlock(Cont.getBlock());

>From 8fdc8dbba69fa3d7599f968f2851cdf1e17f1816 Mon Sep 17 00:00:00 2001
From: hmelder <[email protected]>
Date: Mon, 19 Jan 2026 15:27:04 +0000
Subject: [PATCH 2/2] [ObjC][SEH] Add regression test for finally cleanup

---
 clang/test/CodeGenObjC/exceptions-seh.m | 65 +++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 clang/test/CodeGenObjC/exceptions-seh.m

diff --git a/clang/test/CodeGenObjC/exceptions-seh.m 
b/clang/test/CodeGenObjC/exceptions-seh.m
new file mode 100644
index 0000000000000..e62414ee14117
--- /dev/null
+++ b/clang/test/CodeGenObjC/exceptions-seh.m
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple aarch64-pc-windows -emit-llvm -fexceptions 
-fobjc-exceptions -fobjc-runtime=gnustep-2.2 -o - %s | FileCheck %s
+
+void may_throw(void);
+void puts(const char *);
+
+int main(void) {
+       @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. We capture the
+    // normal and unwind edge for later checks.
+    // CHECK: [[CATCH_DISPATCH]]:
+    // CHECK-NEXT: %[[CATCHSWITCH_OUTER:.*]] = catchswitch within none [label 
%[[CATCH_A:.*]], label %[[CATCH_B:.*]]] unwind label 
%[[EH_CLEANUP_OUTER_FINALLY:.*]]
+
+    // Check if normal edge leads to a call to the outer finally funclet
+    // CHECK: [[INVOKE_CONT]]:
+    // CHECK: call void @"?fin$0@0@main@@"
+
+       @catch(id a) {
+        // CHECK: %[[CATCHPAD_A:.*]] = catchpad within %[[CATCHSWITCH_OUTER]]
+               puts("catch");
+        @try {
+                       may_throw();
+            // CHECK: invoke void @may_throw() [ "funclet"(token %{{.*}}) ]
+            // CHECK-NEXT: to label %[[INVOKE_CONT_INNER:.*]] unwind label 
%[[CATCH_DISPATCH_INNER:.*]]
+
+            // CHECK: [[CATCH_DISPATCH_INNER]]:
+            // CHECK-NEXT: %{{.*}} = catchswitch within %[[CATCHPAD_A]] [label 
%[[CATCHPAD_A_INNER:.*]]] unwind label %[[EH_CLEANUP_INNER_FINALLY:.*]]
+
+            // Check if normal edge leads to a call to the inner finally 
funclet and calls
+            // CHECK: [[INVOKE_CONT_INNER]]:
+            // CHECK: invoke void @"?fin$1@0@main@@"
+               } @catch(...) {
+            // CHECK: [[CATCHPAD_A_INNER]]:
+            // CHECK: to label %invoke.cont{{[0-9]+}} unwind label 
%[[EH_CLEANUP_INNER_FINALLY]]
+                       puts("inner catch all");
+               } @finally {
+            // CHECK: [[EH_CLEANUP_INNER_FINALLY]]:
+            // CHECK: invoke void @"?fin$1@0@main@@"{{.*}}
+            // CHECK-NEXT: to label %invoke.cont{{[0-9]+}} unwind label 
%[[EH_CLEANUP_OUTER_FINALLY]]
+                       puts("inner finally");
+               }
+               return 42;
+       }
+
+       @catch(id b) {
+        // CHECK: %[[CATCHPAD_B:.*]] = catchpad within %[[CATCHSWITCH_OUTER]]
+        // CHECK: to label %invoke.cont{{[0-9]+}} unwind label 
%[[EH_CLEANUP_OUTER_FINALLY]]
+               puts("catch 2");
+               return 43;
+       }
+
+    // Check that the cleanuppad from the SEH finally funclet was correctly 
emitted.
+    // CHECK: [[EH_CLEANUP_OUTER_FINALLY]]:
+    // CHECK: %[[CLEANUP_PAD:.*]] = cleanuppad
+    // CHECK: call void @"?fin$0@0@main@@"
+    // CHECK: cleanupret from %[[CLEANUP_PAD]] unwind to caller
+       @finally {
+               puts("cleanup");
+       }
+       return 0;
+}

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

Reply via email to