[cfe-commits] r71198 - /cfe/trunk/lib/CodeGen/CGObjCGNU.cpp

Chris Lattner sabre at nondot.org
Thu May 7 17:11:50 PDT 2009


Author: lattner
Date: Thu May  7 19:11:50 2009
New Revision: 71198

URL: http://llvm.org/viewvc/llvm-project?rev=71198&view=rev
Log:
initial support for ObjC exceptions with the GNU runtime:

"This patch is a first pass at adding support for exceptions for the GNU runtime.  There are a few limitations at present:

- @synchronized() is not yet supported at all.  gcc currently emits calls to runtime library functions that don't exist for this directive.

- Only id @catch statements are currently working.  This is enough for NS_DURING and friends, but I need to spend more time reading the output from gcc -S to work out how it finds the class pointer to make arbitrary class type catch statements work.

- I've tested it with a few common cases[1] and the clang test suite (which doesn't test exceptions for the GNU runtime, but shows I haven't broken anything else), but there are probably a lot of cases I've missed."

Patch by David Chisnall!


Modified:
    cfe/trunk/lib/CodeGen/CGObjCGNU.cpp

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

==============================================================================
--- cfe/trunk/lib/CodeGen/CGObjCGNU.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGObjCGNU.cpp Thu May  7 19:11:50 2009
@@ -17,16 +17,20 @@
 #include "CGObjCRuntime.h"
 #include "CodeGenModule.h"
 #include "CodeGenFunction.h"
+
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/StmtObjC.h"
+
+#include "llvm/Intrinsics.h"
 #include "llvm/Module.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Target/TargetData.h"
+
 #include <map>
 
 
@@ -1109,12 +1113,235 @@
 
 void CGObjCGNU::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
                                           const Stmt &S) {
-  CGF.ErrorUnsupported(&S, "@try/@synchronized statement");
+  // Pointer to the personality function
+  llvm::Constant *Personality =
+    CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::Int32Ty,
+          std::vector<const llvm::Type*>(), true),
+        "__gnu_objc_personality_v0");
+  Personality = llvm::ConstantExpr::getBitCast(Personality, PtrTy);
+  std::vector<const llvm::Type*> Params;
+  Params.push_back(PtrTy);
+  llvm::Value *RethrowFn =
+    CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::VoidTy,
+          Params, false), "_Unwind_Resume_or_Rethrow");
+
+  bool isTry = isa<ObjCAtTryStmt>(S);
+  llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try");
+  llvm::BasicBlock *PrevLandingPad = CGF.getInvokeDest();
+  llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler");
+  llvm::BasicBlock *FinallyBlock = CGF.createBasicBlock("finally");
+  llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw");
+  llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end");
+
+  // GNU runtime does not currently support @synchronized()
+  if (!isTry) {
+    CGF.ErrorUnsupported(&S, "@synchronized statement");
+  }
+
+  // Push an EH context entry, used for handling rethrows and jumps
+  // through finally.
+  CGF.PushCleanupBlock(FinallyBlock);
+
+  // Emit the statements in the @try {} block
+  CGF.setInvokeDest(TryHandler);
+
+  CGF.EmitBlock(TryBlock);
+  CGF.EmitStmt(isTry ? cast<ObjCAtTryStmt>(S).getTryBody() 
+                     : cast<ObjCAtSynchronizedStmt>(S).getSynchBody());
+
+  // Jump to @finally if there is no exception
+  CGF.EmitBranchThroughCleanup(FinallyEnd);
+
+  // Emit the handlers
+  CGF.EmitBlock(TryHandler);
+
+  llvm::Value *llvm_eh_exception = 
+    CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_exception);
+  llvm::Value *llvm_eh_selector_i64 = 
+    CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_selector_i32);
+  llvm::Value *llvm_eh_typeid_for_i64 = 
+    CGF.CGM.getIntrinsic(llvm::Intrinsic::eh_typeid_for_i32);
+
+  // Exception object
+  llvm::Value *Exc = CGF.Builder.CreateCall(llvm_eh_exception, "exc");
+  llvm::Value *RethrowPtr = CGF.CreateTempAlloca(Exc->getType(), "_rethrow");
+
+  llvm::SmallVector<llvm::Value*, 8> ESelArgs;
+  llvm::SmallVector<std::pair<const ParmVarDecl*, const Stmt*>, 8> Handlers;
+
+  ESelArgs.push_back(Exc);
+  ESelArgs.push_back(Personality);
+
+  bool HasCatchAll = false;
+  // Only @try blocks are allowed @catch blocks, but both can have @finally
+  if (isTry) {
+    if (const ObjCAtCatchStmt* CatchStmt =
+      cast<ObjCAtTryStmt>(S).getCatchStmts())  {
+
+      for (; CatchStmt; CatchStmt = CatchStmt->getNextCatchStmt()) {
+        const ParmVarDecl *CatchDecl = CatchStmt->getCatchParamDecl();
+        Handlers.push_back(std::make_pair(CatchDecl, CatchStmt->getCatchBody()));
+
+        // @catch() and @catch(id) both catch any ObjC exception
+        if (!CatchDecl || CGF.getContext().isObjCIdType(CatchDecl->getType())
+            || CatchDecl->getType()->isObjCQualifiedIdType()) {
+          // Use i8* null here to signal this is a catch all, not a cleanup.
+          ESelArgs.push_back(NULLPtr);
+          HasCatchAll = true;
+          // No further catches after this one will ever by reached
+          break;
+        } 
+
+        // All other types should be Objective-C interface pointer types.
+        const PointerType *PT = CatchDecl->getType()->getAsPointerType();
+        assert(PT && "Invalid @catch type.");
+        const ObjCInterfaceType *IT = 
+          PT->getPointeeType()->getAsObjCInterfaceType();
+        assert(IT && "Invalid @catch type.");
+        CGF.ErrorUnsupported(&S, "@catch block with non-id argument  statement");
+        // FIXME: This should be the Class for the corresponding interface
+        llvm::Value *EHType = NULLPtr;
+        ESelArgs.push_back(EHType);
+      }
+    }
+  }
+
+  // We use a cleanup unless there was already a catch all.
+  if (!HasCatchAll) {
+    ESelArgs.push_back(llvm::ConstantInt::get(llvm::Type::Int32Ty, 0));
+    Handlers.push_back(std::make_pair((const ParmVarDecl*) 0, (const Stmt*) 0));
+  }
+
+  // Find which handler was matched.
+  llvm::Value *ESelector = CGF.Builder.CreateCall(llvm_eh_selector_i64,
+      ESelArgs.begin(), ESelArgs.end(), "selector");
+
+  for (unsigned i = 0, e = Handlers.size(); i != e; ++i) {
+    const ParmVarDecl *CatchParam = Handlers[i].first;
+    const Stmt *CatchBody = Handlers[i].second;
+
+    llvm::BasicBlock *Next = 0;
+
+    // The last handler always matches.
+    if (i + 1 != e) {
+      assert(CatchParam && "Only last handler can be a catch all.");
+
+      // Test whether this block matches the type for the selector and branch
+      // to Match if it does, or to the next BB if it doesn't.
+      llvm::BasicBlock *Match = CGF.createBasicBlock("match");
+      Next = CGF.createBasicBlock("catch.next");
+      llvm::Value *Id = CGF.Builder.CreateCall(llvm_eh_typeid_for_i64,
+          CGF.Builder.CreateBitCast(ESelArgs[i+2], PtrTy));
+      CGF.Builder.CreateCondBr(CGF.Builder.CreateICmpEQ(ESelector, Id), Match,
+          Next);
+
+      CGF.EmitBlock(Match);
+    }
+    
+    if (CatchBody) {
+      //FIXME: Do we need to reset the invoke destination here?  Exceptions in
+      //@catch {} blocks should unwind higher up.
+      
+      //FIXME: This may not be right.  The exception object is returned in
+      //__builtin_eh_return_data_regno(0), but I am not sure how to get at
+      //this in LLVM
+      llvm::Value *ExcObject = CGF.Builder.CreateBitCast(Exc,
+          CGF.ConvertType(CatchParam->getType()));
+      
+      // Bind the catch parameter if it exists.
+      if (CatchParam) {
+        // CatchParam is a ParmVarDecl because of the grammar
+        // construction used to handle this, but for codegen purposes
+        // we treat this as a local decl.
+        CGF.EmitLocalBlockVarDecl(*CatchParam);
+        CGF.Builder.CreateStore(ExcObject, CGF.GetAddrOfLocalVar(CatchParam));
+      }
+
+      CGF.ObjCEHValueStack.push_back(ExcObject);
+      CGF.EmitStmt(CatchBody);
+      CGF.ObjCEHValueStack.pop_back();
+
+      CGF.EmitBranchThroughCleanup(FinallyEnd);
+
+      if (Next)
+        CGF.EmitBlock(Next);
+    } else {
+      assert(!Next && "catchup should be last handler.");
+
+      CGF.Builder.CreateStore(Exc, RethrowPtr);
+      CGF.EmitBranchThroughCleanup(FinallyRethrow);
+    }
+  }
+  CodeGenFunction::CleanupBlockInfo Info = CGF.PopCleanupBlock();
+
+  CGF.setInvokeDest(PrevLandingPad);
+
+  CGF.EmitBlock(FinallyBlock);
+
+  if (isTry) {
+    if (const ObjCAtFinallyStmt* FinallyStmt = 
+        cast<ObjCAtTryStmt>(S).getFinallyStmt())
+      CGF.EmitStmt(FinallyStmt->getFinallyBody());
+  } else {
+    // TODO: Emit 'objc_sync_exit(expr)' as finally's sole statement for
+    // @synchronized.
+  }
+
+  if (Info.SwitchBlock)
+    CGF.EmitBlock(Info.SwitchBlock);
+  if (Info.EndBlock)
+    CGF.EmitBlock(Info.EndBlock);
+
+  // Branch around the rethrow code.
+  CGF.EmitBranch(FinallyEnd);
+
+  CGF.EmitBlock(FinallyRethrow);
+  CGF.Builder.CreateCall(RethrowFn, CGF.Builder.CreateLoad(RethrowPtr));
+  CGF.Builder.CreateUnreachable();
+  
+  CGF.EmitBlock(FinallyEnd);
+
 }
 
 void CGObjCGNU::EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
                               const ObjCAtThrowStmt &S) {
-  CGF.ErrorUnsupported(&S, "@throw statement");
+  llvm::Value *ExceptionAsObject;
+
+  std::vector<const llvm::Type*> Args(1, IdTy);
+  llvm::FunctionType *FTy =
+    llvm::FunctionType::get(llvm::Type::VoidTy, Args, false);
+  llvm::Value *ThrowFn = 
+    CGM.CreateRuntimeFunction(FTy, "objc_exception_throw");
+  
+  if (const Expr *ThrowExpr = S.getThrowExpr()) {
+    llvm::Value *Exception = CGF.EmitScalarExpr(ThrowExpr);
+    ExceptionAsObject = 
+      CGF.Builder.CreateBitCast(Exception, IdTy, "tmp");
+  } else {
+    assert((!CGF.ObjCEHValueStack.empty() && CGF.ObjCEHValueStack.back()) && 
+           "Unexpected rethrow outside @catch block.");
+    ExceptionAsObject = CGF.ObjCEHValueStack.back();
+  }
+  
+  // Note: This may have to be an invoke, if we want to support constructs like:
+  // @try {
+  //  @throw(obj);
+  // }
+  // @catch(id) ...
+  //
+  // This is effectively turning @throw into an incredibly-expensive goto, but
+  // it may happen as a result of inlining followed by missed optimizations, or
+  // as a result of stupidity.
+  llvm::BasicBlock *UnwindBB = CGF.getInvokeDest();
+  if (!UnwindBB) {
+    CGF.Builder.CreateCall(ThrowFn, ExceptionAsObject);
+    CGF.Builder.CreateUnreachable();
+  } else {
+    CGF.Builder.CreateInvoke(ThrowFn, UnwindBB, UnwindBB, &ExceptionAsObject,
+        &ExceptionAsObject+1);
+  }
+  // Clear the insertion point to indicate we are in unreachable code.
+  CGF.Builder.ClearInsertionPoint();
 }
 
 llvm::Value * CGObjCGNU::EmitObjCWeakRead(CodeGen::CodeGenFunction &CGF,





More information about the cfe-commits mailing list