[cfe-commits] r56827 - in /cfe/trunk/lib/CodeGen: CGObjCMac.cpp CGStmt.cpp CodeGenFunction.h

Daniel Dunbar daniel at zuster.org
Mon Sep 29 18:06:04 PDT 2008


Author: ddunbar
Date: Mon Sep 29 20:06:03 2008
New Revision: 56827

URL: http://llvm.org/viewvc/llvm-project?rev=56827&view=rev
Log:
Add infrastructure for proper @finally support.
 - Provides a basic primitive to jump to an arbitrary basic block,
   through the finally code.

 - Only used for return statements and rethrow currently. Still need
   to handle break, continue and goto.

 - Code still needs to be shuffled around to live elsewhere.

Modified:
    cfe/trunk/lib/CodeGen/CGObjCMac.cpp
    cfe/trunk/lib/CodeGen/CGStmt.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h

Modified: cfe/trunk/lib/CodeGen/CGObjCMac.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGObjCMac.cpp?rev=56827&r1=56826&r2=56827&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CGObjCMac.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGObjCMac.cpp Mon Sep 29 20:06:03 2008
@@ -1406,12 +1406,14 @@
       
       // fell off end, rethrow.
       _rethrow = _caught;
+      ... jump-through-finally to finally_rethrow ...
     } else {
       // exception in catch block
       _rethrow = objc_exception_extract(&d);
-      goto finally_no_exit;
+      ... jump-through-finally_no_exit to finally_rethrow ...
     }
   }
+  ... jump-through-finally to finally_end ...
 
 finally:
   // match either the initial try_enter or the catch try_enter,
@@ -1419,18 +1421,18 @@
   objc_exception_try_exit(&d);
 finally_no_exit:
   ... finally block ....
-  if (_rethrow)
-    objc_exception_throw(_rethrow);
+  ... dispatch to finally destination ...
+
+finally_rethrow:
+  objc_exception_throw(_rethrow);
+
+finally_end:
 }
 
 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.
+uses _rethrow to determine if objc_exception_try_exit should be called
+and if the object should be rethrown. This breaks in the face of
+throwing nil and introduces unnecessary branches.
 
 We specialize this framework for a few particular circumstances:
 
@@ -1450,19 +1452,47 @@
 handled by storing the current exception-handling context in
 ObjCEHStack.
 
+In order to implement proper @finally semantics, we support one basic
+mechanism for jumping through the finally block to an arbitrary
+destination. Constructs which generate exits from a @try or @catch
+block use this mechanism to implement the proper semantics by chaining
+jumps, as necessary.
+
+This mechanism works like the one used for indirect goto: we
+arbitrarily assign an ID to each destination and store the ID for the
+destination in a variable prior to entering the finally block. At the
+end of the finally block we simply create a switch to the proper
+destination.
+
 */
 
 void CGObjCMac::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
-                            const ObjCAtTryStmt &S)
-{
-  // Allocate exception data.
+                            const ObjCAtTryStmt &S) {
+  // Create various blocks we refer to for handling @finally.
+  llvm::BasicBlock *FinallyBlock = llvm::BasicBlock::Create("finally");
+  llvm::BasicBlock *FinallyNoExit = llvm::BasicBlock::Create("finally.noexit");
+  llvm::BasicBlock *FinallyRethrow = llvm::BasicBlock::Create("finally.throw");
+  llvm::BasicBlock *FinallyEnd = llvm::BasicBlock::Create("finally.end");
+  llvm::Value *DestCode = 
+    CGF.CreateTempAlloca(llvm::Type::Int32Ty, "finally.dst");
+
+  // Generate jump code. Done here so we can directly add things to
+  // the switch instruction.
+  llvm::BasicBlock *FinallyJump = llvm::BasicBlock::Create("finally.jump");
+  llvm::SwitchInst *FinallySwitch = 
+    llvm::SwitchInst::Create(new llvm::LoadInst(DestCode, "", FinallyJump), 
+                             FinallyEnd, 10, FinallyJump);
+
+  // Push an EH context entry, used for handling rethrows and jumps
+  // through finally.
+  CodeGenFunction::ObjCEHEntry EHEntry(FinallyBlock, FinallyNoExit,
+                                       FinallySwitch, DestCode);
+  CGF.ObjCEHStack.push_back(&EHEntry);
+
+  // Allocate memory for the exception data and rethrow pointer.
   llvm::Value *ExceptionData = CGF.CreateTempAlloca(ObjCTypes.ExceptionDataTy,
                                                     "exceptiondata.ptr");
-  
-  // Allocate memory for the rethrow pointer.
-  llvm::Value *RethrowPtr = CGF.CreateTempAlloca(ObjCTypes.ObjectPtrTy);
-  CGF.Builder.CreateStore(llvm::Constant::getNullValue(ObjCTypes.ObjectPtrTy),
-                          RethrowPtr);
+  llvm::Value *RethrowPtr = CGF.CreateTempAlloca(ObjCTypes.ObjectPtrTy, "_rethrow");
   
   // Enter a new try block and call setjmp.
   CGF.Builder.CreateCall(ObjCTypes.ExceptionTryEnterFn, ExceptionData);
@@ -1471,25 +1501,16 @@
   JmpBufPtr = CGF.Builder.CreateStructGEP(JmpBufPtr, 0, "tmp");
   llvm::Value *SetJmpResult = CGF.Builder.CreateCall(ObjCTypes.SetJmpFn,
                                                      JmpBufPtr, "result");
-  
-  
-  llvm::BasicBlock *FinallyBlock = llvm::BasicBlock::Create("finally");
-  llvm::BasicBlock *FinallyNoExit = llvm::BasicBlock::Create("finally.noexit");
-  
+
   llvm::BasicBlock *TryBlock = llvm::BasicBlock::Create("try");
   llvm::BasicBlock *TryHandler = llvm::BasicBlock::Create("try.handler");
-
   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());
-  CGF.Builder.CreateBr(FinallyBlock);
+  CGF.EmitJumpThroughFinally(&EHEntry, FinallyEnd);
   
   // Emit the "exception in @try" block.
   CGF.EmitBlock(TryHandler);
@@ -1499,7 +1520,7 @@
   llvm::Value *Caught = CGF.Builder.CreateCall(ObjCTypes.ExceptionExtractFn,
                                                ExceptionData,
                                                "caught");
-  CGF.ObjCEHStack.back().Exception = Caught;
+  EHEntry.Exception = Caught;
   if (const ObjCAtCatchStmt* CatchStmt = S.getCatchStmts()) {    
     // Enter a new exception try block (in case a @catch block throws
     // an exception).
@@ -1520,7 +1541,7 @@
     // so.
     bool AllMatched = false;
     for (; CatchStmt; CatchStmt = CatchStmt->getNextCatchStmt()) {
-      llvm::BasicBlock *NextCatchBlock = llvm::BasicBlock::Create("nextcatch");
+      llvm::BasicBlock *NextCatchBlock = llvm::BasicBlock::Create("catch");
 
       const DeclStmt *CatchParam = 
         cast_or_null<DeclStmt>(CatchStmt->getCatchParamStmt());
@@ -1549,7 +1570,7 @@
         }
         
         CGF.EmitStmt(CatchStmt->getCatchBody());
-        CGF.Builder.CreateBr(FinallyBlock);
+        CGF.EmitJumpThroughFinally(&EHEntry, FinallyEnd);
         break;
       }
       
@@ -1579,7 +1600,7 @@
       CGF.Builder.CreateStore(Tmp, CGF.GetAddrOfLocalVar(VD));
       
       CGF.EmitStmt(CatchStmt->getCatchBody());
-      CGF.Builder.CreateBr(FinallyBlock);
+      CGF.EmitJumpThroughFinally(&EHEntry, FinallyEnd);
       
       CGF.EmitBlock(NextCatchBlock);
     }
@@ -1588,7 +1609,7 @@
       // None of the handlers caught the exception, so store it to be
       // rethrown at the end of the @finally block.
       CGF.Builder.CreateStore(Caught, RethrowPtr);
-      CGF.Builder.CreateBr(FinallyBlock);
+      CGF.EmitJumpThroughFinally(&EHEntry, FinallyRethrow);
     }
     
     // Emit the exception handler for the @catch blocks.
@@ -1596,41 +1617,37 @@
     CGF.Builder.CreateStore(CGF.Builder.CreateCall(ObjCTypes.ExceptionExtractFn,
                                                    ExceptionData), 
                             RethrowPtr);
-    CGF.Builder.CreateBr(FinallyNoExit);
+    CGF.EmitJumpThroughFinally(&EHEntry, FinallyRethrow, false);
   } else {
     CGF.Builder.CreateStore(Caught, RethrowPtr);
-    CGF.Builder.CreateBr(FinallyNoExit);
+    CGF.EmitJumpThroughFinally(&EHEntry, FinallyRethrow, false);    
   }
   
+  // Pop the exception-handling stack entry. It is important to do
+  // this now, because the code in the @finally block is not in this
+  // context.
+  CGF.ObjCEHStack.pop_back();
+
   // Emit the @finally block.
   CGF.EmitBlock(FinallyBlock);
   CGF.Builder.CreateCall(ObjCTypes.ExceptionTryExitFn, ExceptionData);
 
   CGF.EmitBlock(FinallyNoExit);
-
   if (const ObjCAtFinallyStmt* FinallyStmt = S.getFinallyStmt())
     CGF.EmitStmt(FinallyStmt->getFinallyBody());
 
-  llvm::Value *Rethrow = CGF.Builder.CreateLoad(RethrowPtr);
-  
-  llvm::BasicBlock *RethrowBlock = llvm::BasicBlock::Create("rethrow");  
-  llvm::BasicBlock *FinallyEndBlock = llvm::BasicBlock::Create("finally.end");
-
-  // If necessary, rethrow the exception.
-  CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNonNull(Rethrow, "rethrow.test"), 
-                           RethrowBlock, FinallyEndBlock);
-  CGF.EmitBlock(RethrowBlock);
-  CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, Rethrow);
+  CGF.EmitBlock(FinallyJump);
+ 
+  CGF.EmitBlock(FinallyRethrow);
+  CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, 
+                         CGF.Builder.CreateLoad(RethrowPtr));
   CGF.Builder.CreateUnreachable();
-
-  CGF.ObjCEHStack.pop_back();
-
-  CGF.EmitBlock(FinallyEndBlock);
+  
+  CGF.EmitBlock(FinallyEnd);
 }
 
 void CGObjCMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
-                              const ObjCAtThrowStmt &S)
-{
+                              const ObjCAtThrowStmt &S) {
   llvm::Value *ExceptionAsObject;
   
   if (const Expr *ThrowExpr = S.getThrowExpr()) {
@@ -1638,9 +1655,9 @@
     ExceptionAsObject = 
       CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy, "tmp");
   } else {
-    assert((!CGF.ObjCEHStack.empty() && CGF.ObjCEHStack.back().Exception) && 
+    assert((!CGF.ObjCEHStack.empty() && CGF.ObjCEHStack.back()->Exception) && 
            "Unexpected rethrow outside @catch block.");
-    ExceptionAsObject = CGF.ObjCEHStack.back().Exception;
+    ExceptionAsObject = CGF.ObjCEHStack.back()->Exception;
   }
   
   CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn, ExceptionAsObject);
@@ -1648,6 +1665,35 @@
   CGF.EmitBlock(llvm::BasicBlock::Create("bb"));
 }
 
+void CodeGenFunction::EmitJumpThroughFinally(ObjCEHEntry *E,
+                                             llvm::BasicBlock *Dst,
+                                             bool ExecuteTryExit) {
+  llvm::BasicBlock *Src = Builder.GetInsertBlock();
+    
+  if (isDummyBlock(Src))
+    return;
+  
+  // Find the destination code for this block. We always use 0 for the
+  // fallthrough block (default destination).
+  llvm::SwitchInst *SI = E->FinallySwitch;
+  llvm::ConstantInt *ID;
+  if (Dst == SI->getDefaultDest()) {
+    ID = llvm::ConstantInt::get(llvm::Type::Int32Ty, 0);
+  } else {
+    ID = SI->findCaseDest(Dst);
+    if (!ID) {
+      // No code found, get a new unique one by just using the number
+      // of switch successors.
+      ID = llvm::ConstantInt::get(llvm::Type::Int32Ty, SI->getNumSuccessors());
+      SI->addCase(ID, Dst);
+    }
+  }
+
+  // Set the destination code and branch.
+  Builder.CreateStore(ID, E->DestCode);
+  Builder.CreateBr(ExecuteTryExit ? E->FinallyBlock : E->FinallyNoExit);
+}
+
 /* *** Private Interface *** */
 
 /// EmitImageInfo - Emit the image info marker used to encode some module

Modified: cfe/trunk/lib/CodeGen/CGStmt.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGStmt.cpp?rev=56827&r1=56826&r2=56827&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CGStmt.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGStmt.cpp Mon Sep 29 20:06:03 2008
@@ -437,6 +437,15 @@
     EmitAggExpr(RV, ReturnValue, false);
   }
 
+  if (!ObjCEHStack.empty()) {
+    for (ObjCEHStackType::reverse_iterator i = ObjCEHStack.rbegin(), 
+           e = ObjCEHStack.rend(); i != e; ++i) {
+      llvm::BasicBlock *ReturnPad = llvm::BasicBlock::Create("return.pad");
+      EmitJumpThroughFinally(*i, ReturnPad);
+      EmitBlock(ReturnPad);
+    }
+  } 
+
   Builder.CreateBr(ReturnBlock);
   
   // Emit a block after the branch so that dead code after a return has some

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=56827&r1=56826&r2=56827&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Sep 29 20:06:03 2008
@@ -31,6 +31,7 @@
 namespace llvm {
   class BasicBlock;
   class Module;
+  class SwitchInst;
 }
 
 namespace clang {
@@ -88,13 +89,42 @@
   // inside @catch blocks and which @finally block exits from an EH
   // scope should be chained through.
   struct ObjCEHEntry {
-    ObjCEHEntry(llvm::BasicBlock *fb)
-      : Exception(0), FinallyBlock(fb) {}
-
-    llvm::Value *Exception;
-    llvm::BasicBlock *FinallyBlock;
+    ObjCEHEntry(llvm::BasicBlock *fb, llvm::BasicBlock *fne, 
+                llvm::SwitchInst *fs, llvm::Value *dc)
+      : FinallyBlock(fb), FinallyNoExit(fne), FinallySwitch(fs), 
+        DestCode(dc), Exception(0) {}
+
+    /// Entry point to the finally block.
+    llvm::BasicBlock *FinallyBlock; 
+
+    /// Entry point to the finally block which skips execution of the
+    /// try_exit runtime function.
+    llvm::BasicBlock *FinallyNoExit; 
+
+    /// Switch instruction which runs at the end of the finally block
+    /// to forward jumps through the finally block.
+    llvm::SwitchInst *FinallySwitch; 
+
+    /// Variable holding the code for the destination of a jump
+    /// through the @finally block.
+    llvm::Value *DestCode;
+
+    /// The exception object being handled, during IR generation for a
+    /// @catch block.
+    llvm::Value *Exception; 
   };
-  llvm::SmallVector<ObjCEHEntry, 8> ObjCEHStack;
+
+  typedef llvm::SmallVector<ObjCEHEntry*, 8> ObjCEHStackType;
+  ObjCEHStackType ObjCEHStack;
+
+  /// EmitJumpThroughFinally - Emit a branch from the current insert
+  /// point through the finally handling code for \arg Entry and then
+  /// on to \arg Dest.
+  ///
+  /// \param ExecuteTryExit - When true, the try_exit runtime function
+  /// should be called prior to executing the finally code.
+  void EmitJumpThroughFinally(ObjCEHEntry *Entry, llvm::BasicBlock *Dest,
+                              bool ExecuteTryExit=true);
   
 private:
   /// LabelIDs - Track arbitrary ids assigned to labels for use in





More information about the cfe-commits mailing list