diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index 99105e165f3a..746f68daee22 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -1326,6 +1326,12 @@ namespace { CGF.EHStack.pushCleanup(NormalAndEHCleanup, ForEHVar, EndCatchFn); + // Save the current cleanup destination in case there are + // cleanups in the finally block. + llvm::Value *SavedCleanupDest = + CGF.Builder.CreateLoad(CGF.getNormalCleanupDestSlot(), + "cleanup.dest.saved"); + // Emit the finally block. CGF.EmitStmt(Body); @@ -1349,6 +1355,10 @@ namespace { CGF.Builder.CreateUnreachable(); CGF.EmitBlock(ContBB); + + // Restore the cleanup destination. + CGF.Builder.CreateStore(SavedCleanupDest, + CGF.getNormalCleanupDestSlot()); } // Leave the end-catch cleanup. As an optimization, pretend that diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 3a80330a2fd4..47e303fb1eb6 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -2674,11 +2674,22 @@ namespace { if (isa(S)) { if (const ObjCAtFinallyStmt* FinallyStmt = - cast(S).getFinallyStmt()) + cast(S).getFinallyStmt()) { + // Save the current cleanup destination in case there's + // control flow inside the finally statement. + llvm::Value *CurCleanupDest = + CGF.Builder.CreateLoad(CGF.getNormalCleanupDestSlot()); + CGF.EmitStmt(FinallyStmt->getFinallyBody()); - // Currently, the end of the cleanup must always exist. - CGF.EnsureInsertPoint(); + if (CGF.HaveInsertPoint()) { + CGF.Builder.CreateStore(CurCleanupDest, + CGF.getNormalCleanupDestSlot()); + } else { + // Currently, the end of the cleanup must always exist. + CGF.EnsureInsertPoint(); + } + } } else { // Emit objc_sync_exit(expr); as finally's sole statement for // @synchronized. @@ -3030,8 +3041,8 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF, llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try"); llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler"); llvm::Value *DidCatch = - CGF.Builder.CreateIsNull(SetJmpResult, "did_catch_exception"); - CGF.Builder.CreateCondBr(DidCatch, TryBlock, TryHandler); + CGF.Builder.CreateIsNotNull(SetJmpResult, "did_catch_exception"); + CGF.Builder.CreateCondBr(DidCatch, TryHandler, TryBlock); // Emit the protected block. CGF.EmitBlock(TryBlock); diff --git a/clang/test/CodeGenObjC/exceptions.m b/clang/test/CodeGenObjC/exceptions.m index a494651a42a1..f8292dc7f151 100644 --- a/clang/test/CodeGenObjC/exceptions.m +++ b/clang/test/CodeGenObjC/exceptions.m @@ -82,3 +82,54 @@ int f2() { } return x; } + +// Test that the cleanup destination is saved when entering a finally +// block. rdar://problem/8293901 +// CHECK: define void @f3() +void f3() { + extern void f3_helper(int, int*); + + // CHECK: [[X:%.*]] = alloca i32 + // CHECK: store i32 0, i32* [[X]] + int x = 0; + + // CHECK: call void @objc_exception_try_enter( + // CHECK: call i32 @_setjmp + // CHECK-NEXT: icmp eq + // CHECK-NEXT: br i1 + + @try { + // CHECK: call void @f3_helper(i32 0, i32* [[X]]) + // CHECK: call void @objc_exception_try_exit( + f3_helper(0, &x); + } @finally { + // CHECK: [[DEST1:%.*]] = phi i32 [ 0, {{%.*}} ], [ 3, {{%.*}} ] + // CHECK: call void @objc_exception_try_enter + // CHECK: call i32 @_setjmp + @try { + // CHECK: call void @f3_helper(i32 1, i32* [[X]]) + // CHECK: call void @objc_exception_try_exit( + f3_helper(1, &x); + } @finally { + // CHECK: [[DEST2:%.*]] = phi i32 [ 0, {{%.*}} ], [ 5, {{%.*}} ] + // CHECK: call void @f3_helper(i32 2, i32* [[X]]) + f3_helper(2, &x); + + // This loop is large enough to dissuade the optimizer from just + // duplicating the finally block. + while (x) f3_helper(3, &x); + + // It's okay for this to turn into a test against 0. + // CHECK: icmp eq i32 [[DEST2]], 5 + // CHECK: br i1 + } + + // It's okay for this to turn into a test against 0. + // CHECK: icmp eq i32 [[DEST1]], 3 + // CHECK: br i1 + } + + // CHECK: call void @f3_helper(i32 4, i32* [[X]]) + // CHECK-NEXT: ret void + f3_helper(4, &x); +}