llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-codegen Author: Hugo Melder (hmelder) <details> <summary>Changes</summary> 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`. --- Full diff: https://github.com/llvm/llvm-project/pull/176779.diff 2 Files Affected: - (modified) clang/lib/CodeGen/CGObjCRuntime.cpp (+27-24) - (added) clang/test/CodeGenObjC/exceptions-seh.m (+65) ``````````diff 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()); 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; +} `````````` </details> https://github.com/llvm/llvm-project/pull/176779 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
