forked from OSchip/llvm-project
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:
parent
bb5f078ce0
commit
9916e3fa93
|
@ -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.
|
||||
// If we have a propagating-exception variable, check it.
|
||||
llvm::Value *PropagatingExn;
|
||||
if (PropagatingExnVar) {
|
||||
PropagatingExn = CGF.Builder.CreateLoad(PropagatingExnVar);
|
||||
|
||||
// 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(), Caught)
|
||||
CGF.Builder.CreateCall(ObjCTypes.getExceptionThrowFn(), PropagatingExn)
|
||||
->setDoesNotThrow();
|
||||
CGF.Builder.CreateUnreachable();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue