Fix a bug in @finally emission in both the fragile and non-fragile EH schemes

where we weren't accounting for the possibility that a @finally block might
have internal cleanups and therefore might write to the cleanup destination slot.

Fixes <rdar://problem/8293901>.

llvm-svn: 110760
This commit is contained in:
John McCall 2010-08-11 00:16:14 +00:00
parent ccab1dddd1
commit cebe0ca75e
3 changed files with 77 additions and 5 deletions

View File

@ -1326,6 +1326,12 @@ namespace {
CGF.EHStack.pushCleanup<CallEndCatchForFinally>(NormalAndEHCleanup, CGF.EHStack.pushCleanup<CallEndCatchForFinally>(NormalAndEHCleanup,
ForEHVar, EndCatchFn); 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. // Emit the finally block.
CGF.EmitStmt(Body); CGF.EmitStmt(Body);
@ -1349,6 +1355,10 @@ namespace {
CGF.Builder.CreateUnreachable(); CGF.Builder.CreateUnreachable();
CGF.EmitBlock(ContBB); CGF.EmitBlock(ContBB);
// Restore the cleanup destination.
CGF.Builder.CreateStore(SavedCleanupDest,
CGF.getNormalCleanupDestSlot());
} }
// Leave the end-catch cleanup. As an optimization, pretend that // Leave the end-catch cleanup. As an optimization, pretend that

View File

@ -2674,11 +2674,22 @@ namespace {
if (isa<ObjCAtTryStmt>(S)) { if (isa<ObjCAtTryStmt>(S)) {
if (const ObjCAtFinallyStmt* FinallyStmt = if (const ObjCAtFinallyStmt* FinallyStmt =
cast<ObjCAtTryStmt>(S).getFinallyStmt()) cast<ObjCAtTryStmt>(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()); CGF.EmitStmt(FinallyStmt->getFinallyBody());
// Currently, the end of the cleanup must always exist. if (CGF.HaveInsertPoint()) {
CGF.EnsureInsertPoint(); CGF.Builder.CreateStore(CurCleanupDest,
CGF.getNormalCleanupDestSlot());
} else {
// Currently, the end of the cleanup must always exist.
CGF.EnsureInsertPoint();
}
}
} else { } else {
// Emit objc_sync_exit(expr); as finally's sole statement for // Emit objc_sync_exit(expr); as finally's sole statement for
// @synchronized. // @synchronized.
@ -3030,8 +3041,8 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try"); llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try");
llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler"); llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler");
llvm::Value *DidCatch = llvm::Value *DidCatch =
CGF.Builder.CreateIsNull(SetJmpResult, "did_catch_exception"); CGF.Builder.CreateIsNotNull(SetJmpResult, "did_catch_exception");
CGF.Builder.CreateCondBr(DidCatch, TryBlock, TryHandler); CGF.Builder.CreateCondBr(DidCatch, TryHandler, TryBlock);
// Emit the protected block. // Emit the protected block.
CGF.EmitBlock(TryBlock); CGF.EmitBlock(TryBlock);

View File

@ -82,3 +82,54 @@ int f2() {
} }
return x; 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);
}