]> granicus.if.org Git - clang/commitdiff
In the fragile ObjC ABI, save the caught exception to the side if there are
authorJohn McCall <rjmccall@apple.com>
Mon, 4 Oct 2010 23:42:51 +0000 (23:42 +0000)
committerJohn McCall <rjmccall@apple.com>
Mon, 4 Oct 2010 23:42:51 +0000 (23:42 +0000)
both @catches and a @finally, because the second call to @objc_exception_try_enter
will clobber the exception slot.  Fixes rdar://problem/8440970.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@115575 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGObjCMac.cpp
test/CodeGenObjC/exceptions.m

index 766703d9e72f6b8586f5bd6865ccfb4782082e13..c7e4c5ffcf955444b57f4f5bcecd16826f7fb86f 100644 (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);
+
+    // 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();
   }
index b431e3712473b4e29d7029927040347f58de0468..7cc0c1f25045eb82a5c129de24cb8e504a1e9433 100644 (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
+}