Add support for implicit rethrows in @catch blocks.

Comment exception-handling code generation strategy.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56763 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp
index 3a54a45..a35e90f 100644
--- a/lib/CodeGen/CGObjCMac.cpp
+++ b/lib/CodeGen/CGObjCMac.cpp
@@ -1383,6 +1383,75 @@
   return ObjCTypes.EnumerationMutationFn;
 }
 
+/* 
+
+Objective-C setjmp-longjmp (sjlj) Exception Handling
+--
+
+The basic framework for a @try-catch-finally is as follows:
+{
+  objc_exception_data d;
+  id _rethrow = null;
+
+  objc_exception_try_enter(&d);
+  if (!setjmp(d.jmp_buf)) {
+    ... try body ... 
+  } else {
+    // exception path
+    id _caught = objc_exception_extract(&d);
+    
+    // enter new try scope for handlers
+    if (!setjmp(d.jmp_buf)) {
+      ... match exception and execute catch blocks ...
+      
+      // fell off end, rethrow.
+      _rethrow = _caught;
+    } else {
+      // exception in catch block
+      _rethrow = objc_exception_extract(&d);
+      goto finally_no_exit;
+    }
+  }
+
+finally:
+  // match either the initial try_enter or the catch try_enter,
+  // depending on the path followed.
+  objc_exception_try_exit(&d);
+finally_no_exit:
+  ... finally block ....
+  if (_rethrow)
+    objc_exception_throw(_rethrow);
+}
+
+This framework differs slightly from the one gcc uses, in that gcc
+uses _rethrow to determine if objc_exception_try_exit should be
+called. This breaks in the face of throwing nil and introduces an
+unnecessary branch. Note that our framework still does not properly
+handle throwing nil, as a nil object will not be rethrown.
+
+FIXME: Determine if _rethrow should be integrated into the other
+architecture for selecting paths out of the finally block.
+
+We specialize this framework for a few particular circumstances:
+
+ - If there are no catch blocks, then we avoid emitting the second
+   exception handling context.
+
+ - If there is a catch-all catch block (i.e. @catch(...) or @catch(id
+   e)) we avoid emitting the code to rethrow an uncaught exception.
+
+ - FIXME: If there is no @finally block we can do a few more
+   simplifications.
+
+Rethrows and Jumps-Through-Finally
+--
+
+Support for implicit rethrows and jumping through the finally block is
+handled by storing the current exception-handling context in
+ObjCEHStack.
+
+*/
+
 void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
                             const ObjCAtTryStmt &S)
 {
@@ -1413,6 +1482,10 @@
   CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNonNull(SetJmpResult, "threw"), 
                            TryHandler, TryBlock);
 
+  // Push an EH context entry for use by rethrow and
+  // jumps-through-finally.
+  CGF.ObjCEHStack.push_back(CodeGenFunction::ObjCEHEntry(FinallyBlock));
+
   // Emit the @try block.
   CGF.EmitBlock(TryBlock);
   CGF.EmitStmt(S.getTryBody());
@@ -1426,6 +1499,7 @@
   llvm::Value *Caught = CGF.Builder.CreateCall(ObjCTypes.ExceptionExtractFn,
                                                ExceptionData,
                                                "caught");
+  CGF.ObjCEHStack.back().Exception = Caught;
   if (const ObjCAtCatchStmt* CatchStmt = S.getCatchStmts()) {    
     // Enter a new exception try block (in case a @catch block throws
     // an exception).
@@ -1497,14 +1571,12 @@
       
       // Emit the @catch block.
       CGF.EmitBlock(MatchedBlock);
-      if (CatchParam) {
-        CGF.EmitStmt(CatchParam);
-        
-        llvm::Value *Tmp = 
-          CGF.Builder.CreateBitCast(Caught, CGF.ConvertType(VD->getType()), 
-                                    "tmp");
-        CGF.Builder.CreateStore(Tmp, CGF.GetAddrOfLocalVar(VD));
-      }
+      CGF.EmitStmt(CatchParam);
+
+      llvm::Value *Tmp = 
+        CGF.Builder.CreateBitCast(Caught, CGF.ConvertType(VD->getType()), 
+                                  "tmp");
+      CGF.Builder.CreateStore(Tmp, CGF.GetAddrOfLocalVar(VD));
       
       CGF.EmitStmt(CatchStmt->getCatchBody());
       CGF.Builder.CreateBr(FinallyBlock);
@@ -1551,6 +1623,8 @@
   CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, Rethrow);
   CGF.Builder.CreateUnreachable();
 
+  CGF.ObjCEHStack.pop_back();
+
   CGF.EmitBlock(FinallyEndBlock);
 }
 
@@ -1564,7 +1638,9 @@
     ExceptionAsObject = 
       CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy, "tmp");
   } else {
-    assert(0 && "FIXME: rethrows not supported!");
+    assert((!CGF.ObjCEHStack.empty() && CGF.ObjCEHStack.back().Exception) && 
+           "Unexpected rethrow outside @catch block.");
+    ExceptionAsObject = CGF.ObjCEHStack.back().Exception;
   }
   
   CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, ExceptionAsObject);