In the fragile ObjC ABI, save the caught exception to the side if there are

both @catches and a @finally, because the second call to @objc_exception_try_enter
will clobber the exception slot.  Fixes rdar://problem/8440970.

llvm-svn: 115575
This commit is contained in:
John McCall 2010-10-04 23:42:51 +00:00
parent bb5f078ce0
commit 9916e3fa93
2 changed files with 89 additions and 6 deletions

View File

@ -2973,6 +2973,10 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
llvm::Value *CallTryExitVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(),
"_call_try_exit");
// A slot containing the exception to rethrow. Only needed when we
// have both a @catch and a @finally.
llvm::Value *PropagatingExnVar = 0;
// Push a normal cleanup to leave the try scope.
CGF.EHStack.pushCleanup<PerformFragileFinally>(NormalCleanup, &S,
SyncArgSlot,
@ -3044,6 +3048,12 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
llvm::BasicBlock *CatchBlock = 0;
llvm::BasicBlock *CatchHandler = 0;
if (HasFinally) {
// Save the currently-propagating exception before
// objc_exception_try_enter clears the exception slot.
PropagatingExnVar = CGF.CreateTempAlloca(Caught->getType(),
"propagating_exception");
CGF.Builder.CreateStore(Caught, PropagatingExnVar);
// Enter a new exception try block (in case a @catch block
// throws an exception).
CGF.Builder.CreateCall(ObjCTypes.getExceptionTryEnterFn(), ExceptionData)
@ -3178,6 +3188,15 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
// the try's write hazard and here.
//Hazards.emitWriteHazard();
// Extract the new exception and save it to the
// propagating-exception slot.
assert(PropagatingExnVar);
llvm::CallInst *NewCaught =
CGF.Builder.CreateCall(ObjCTypes.getExceptionExtractFn(),
ExceptionData, "caught");
NewCaught->setDoesNotThrow();
CGF.Builder.CreateStore(NewCaught, PropagatingExnVar);
// Don't pop the catch handler; the throw already did.
CGF.Builder.CreateStore(CGF.Builder.getFalse(), CallTryExitVar);
CGF.EmitBranchThroughCleanup(FinallyRethrow);
@ -3198,13 +3217,21 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP();
CGF.EmitBlock(FinallyRethrow.getBlock(), true);
if (CGF.HaveInsertPoint()) {
// Just look in the buffer for the exception to throw.
llvm::CallInst *Caught =
CGF.Builder.CreateCall(ObjCTypes.getExceptionExtractFn(),
ExceptionData);
Caught->setDoesNotThrow();
// If we have a propagating-exception variable, check it.
llvm::Value *PropagatingExn;
if (PropagatingExnVar) {
PropagatingExn = CGF.Builder.CreateLoad(PropagatingExnVar);
CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), Caught)
// Otherwise, just look in the buffer for the exception to throw.
} else {
llvm::CallInst *Caught =
CGF.Builder.CreateCall(ObjCTypes.getExceptionExtractFn(),
ExceptionData);
Caught->setDoesNotThrow();
PropagatingExn = Caught;
}
CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), PropagatingExn)
->setDoesNotThrow();
CGF.Builder.CreateUnreachable();
}

View File

@ -133,3 +133,59 @@ void f3() {
// CHECK-NEXT: ret void
f3_helper(4, &x);
}
// rdar://problem/8440970
void f4() {
extern void f4_help(int);
// CHECK: define void @f4()
// CHECK: [[EXNDATA:%.*]] = alloca [[EXNDATA_T:%.*]], align
// CHECK: call void @objc_exception_try_enter([[EXNDATA_T]]* [[EXNDATA]])
// CHECK: call i32 @_setjmp
@try {
// CHECK: call void @f4_help(i32 0)
f4_help(0);
// The finally cleanup has two threaded entrypoints after optimization:
// finally.no-call-exit: Predecessor is when the catch throws.
// CHECK: call i8* @objc_exception_extract([[EXNDATA_T]]* [[EXNDATA]])
// CHECK-NEXT: call void @f4_help(i32 2)
// CHECK-NEXT: br label
// -> rethrow
// finally.call-exit: Predecessors are the @try and @catch fallthroughs
// as well as the no-match case in the catch mechanism. The i1 is whether
// to rethrow and should be true only in the last case.
// CHECK: phi i1
// CHECK-NEXT: phi i8*
// CHECK-NEXT: call void @objc_exception_try_exit([[EXNDATA_T]]* [[EXNDATA]])
// CHECK-NEXT: call void @f4_help(i32 2)
// CHECK-NEXT: br i1
// -> ret, rethrow
// ret:
// CHECK: ret void
// Catch mechanism:
// CHECK: call i8* @objc_exception_extract([[EXNDATA_T]]* [[EXNDATA]])
// CHECK-NEXT: call void @objc_exception_try_enter([[EXNDATA_T]]* [[EXNDATA]])
// CHECK: call i32 @_setjmp
// -> next, finally.no-call-exit
// CHECK: call i32 @objc_exception_match
// -> finally.call-exit, match
} @catch (NSArray *a) {
// match:
// CHECK: call void @f4_help(i32 1)
// CHECK-NEXT: br label
// -> finally.call-exit
f4_help(1);
} @finally {
f4_help(2);
}
// rethrow:
// CHECK: phi i8*
// CHECK-NEXT: call void @objc_exception_throw(i8*
// CHECK-NEXT: unreachable
}