]> granicus.if.org Git - clang/commitdiff
Fix Objective-C++ exceptions (GNU runtime).
authorDavid Chisnall <csdavec@swan.ac.uk>
Sun, 20 Mar 2011 21:35:39 +0000 (21:35 +0000)
committerDavid Chisnall <csdavec@swan.ac.uk>
Sun, 20 Mar 2011 21:35:39 +0000 (21:35 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@127980 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGException.cpp
lib/CodeGen/CGException.h
lib/CodeGen/CGObjCGNU.cpp
lib/CodeGen/CGRTTI.cpp
lib/Sema/SemaDeclCXX.cpp

index 7fdfe700421e718424af2c96db239c9be960ec6a..cc4446fcf0154d192343802383718f51fea3d6e6 100644 (file)
@@ -160,6 +160,7 @@ const EHPersonality EHPersonality::GNU_CPlusPlus("__gxx_personality_v0");
 const EHPersonality EHPersonality::GNU_CPlusPlus_SJLJ("__gxx_personality_sj0");
 const EHPersonality EHPersonality::GNU_ObjC("__gnu_objc_personality_v0",
                                             "objc_exception_throw");
+const EHPersonality EHPersonality::GNU_ObjCXX("__gnustep_objcxx_personality_v0");
 
 static const EHPersonality &getCPersonality(const LangOptions &L) {
   if (L.SjLjExceptions)
@@ -201,7 +202,7 @@ static const EHPersonality &getObjCXXPersonality(const LangOptions &L) {
 
   // The GNU runtime's personality function inherently doesn't support
   // mixed EH.  Use the C++ personality just to avoid returning null.
-  return getCXXPersonality(L);
+  return EHPersonality::GNU_ObjCXX;
 }
 
 const EHPersonality &EHPersonality::get(const LangOptions &L) {
index 1f9b8964dcae03a0915195704d074516ca4a0d74..5a743b51f66f6b7408a752eb0f1d8c95b0d9eabe 100644 (file)
@@ -41,6 +41,7 @@ public:
   static const EHPersonality GNU_C;
   static const EHPersonality GNU_C_SJLJ;
   static const EHPersonality GNU_ObjC;
+  static const EHPersonality GNU_ObjCXX;
   static const EHPersonality NeXT_ObjC;
   static const EHPersonality GNU_CPlusPlus;
   static const EHPersonality GNU_CPlusPlus_SJLJ;
index fa873116623ff618263dc2edbb28a34aa56d6676..8b45ed3f3a458623d55716cedb8e04cd0a6c5cce 100644 (file)
@@ -30,6 +30,7 @@
 #include "llvm/LLVMContext.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/Support/CallSite.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Target/TargetData.h"
 
@@ -141,6 +142,7 @@ private:
     if (V->getType() == Ty) return V;
     return B.CreateBitCast(V, Ty);
   }
+  void EmitObjCXXTryStmt(CodeGen::CodeGenFunction &CGF, const ObjCAtTryStmt &S);
 public:
   CGObjCGNU(CodeGen::CodeGenModule &cgm);
   virtual llvm::Constant *GenerateConstantString(const StringLiteral *);
@@ -416,8 +418,62 @@ llvm::Value *CGObjCGNU::GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl
 }
 
 llvm::Constant *CGObjCGNU::GetEHType(QualType T) {
-  llvm_unreachable("asking for catch type for ObjC type in GNU runtime");
-  return 0;
+  // For Objective-C++, we want to provide the ability to catch both C++ and
+  // Objective-C objects in the same function.
+
+  // There's a particular fixed type info for 'id'.
+  if (T->isObjCIdType() ||
+      T->isObjCQualifiedIdType()) {
+    llvm::Constant *IDEHType =
+      CGM.getModule().getGlobalVariable("__objc_id_type_info");
+    if (!IDEHType)
+      IDEHType =
+        new llvm::GlobalVariable(CGM.getModule(), PtrToInt8Ty,
+                                 false,
+                                 llvm::GlobalValue::ExternalLinkage,
+                                 0, "__objc_id_type_info");
+    return llvm::ConstantExpr::getBitCast(IDEHType, PtrToInt8Ty);
+  }
+
+  const ObjCObjectPointerType *PT =
+    T->getAs<ObjCObjectPointerType>();
+  assert(PT && "Invalid @catch type.");
+  const ObjCInterfaceType *IT = PT->getInterfaceType();
+  assert(IT && "Invalid @catch type.");
+  std::string className = IT->getDecl()->getIdentifier()->getName();
+
+  std::string typeinfoName = "__objc_eh_typeinfo_" + className;
+
+  // Return the existing typeinfo if it exists
+  llvm::Constant *typeinfo = TheModule.getGlobalVariable(typeinfoName);
+  if (typeinfo) return typeinfo;
+
+  // Otherwise create it.
+
+  // vtable for gnustep::libobjc::__objc_class_type_info
+  // It's quite ugly hard-coding this.  Ideally we'd generate it using the host
+  // platform's name mangling.
+  const char *vtableName = "_ZTVN7gnustep7libobjc22__objc_class_type_infoE";
+  llvm::Constant *Vtable = TheModule.getGlobalVariable(vtableName);
+  if (!Vtable) {
+    Vtable = new llvm::GlobalVariable(TheModule, PtrToInt8Ty, true,
+            llvm::GlobalValue::ExternalLinkage, 0, vtableName);
+  }
+  llvm::Constant *Two = llvm::ConstantInt::get(IntTy, 2);
+  Vtable = llvm::ConstantExpr::getGetElementPtr(Vtable, &Two, 1);
+  Vtable = llvm::ConstantExpr::getBitCast(Vtable, PtrToInt8Ty);
+
+  llvm::Constant *typeName =
+    ExportUniqueString(className, "__objc_eh_typename_");
+
+  std::vector<llvm::Constant*> fields;
+  fields.push_back(Vtable);
+  fields.push_back(typeName);
+  llvm::Constant *TI = 
+      MakeGlobal(llvm::StructType::get(VMContext, PtrToInt8Ty, PtrToInt8Ty,
+              NULL), fields, "__objc_eh_typeinfo_" + className,
+          llvm::GlobalValue::LinkOnceODRLinkage);
+  return llvm::ConstantExpr::getBitCast(TI, PtrToInt8Ty);
 }
 
 llvm::Constant *CGObjCGNU::MakeConstantString(const std::string &Str,
@@ -1945,6 +2001,138 @@ namespace {
     llvm::BasicBlock *Block;
     llvm::Value *TypeInfo;
   };
+
+  struct CallObjCEndCatch : EHScopeStack::Cleanup {
+    CallObjCEndCatch(bool MightThrow, llvm::Value *Fn) :
+      MightThrow(MightThrow), Fn(Fn) {}
+    bool MightThrow;
+    llvm::Value *Fn;
+
+    void Emit(CodeGenFunction &CGF, bool IsForEH) {
+      if (!MightThrow) {
+        CGF.Builder.CreateCall(Fn)->setDoesNotThrow();
+        return;
+      }
+
+      CGF.EmitCallOrInvoke(Fn, 0, 0);
+    }
+  };
+}
+
+void CGObjCGNU::EmitObjCXXTryStmt(CodeGen::CodeGenFunction &CGF,
+                                  const ObjCAtTryStmt &S) {
+  std::vector<const llvm::Type*> Args(1, PtrToInt8Ty);
+  llvm::FunctionType *FTy = llvm::FunctionType::get(PtrToInt8Ty, Args, false);
+  const llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext);
+
+  llvm::Constant *beginCatchFn =
+    CGM.CreateRuntimeFunction(FTy, "__cxa_begin_catch");
+
+  FTy = llvm::FunctionType::get(VoidTy, false);
+  llvm::Constant *endCatchFn =
+    CGM.CreateRuntimeFunction(FTy, "__cxa_end_catch");
+  FTy = llvm::FunctionType::get(VoidTy, Args, false);
+  llvm::Constant *exceptionRethrowFn =
+    CGM.CreateRuntimeFunction(FTy, "_Unwind_Resume_or_Rethrow");
+
+  // Jump destination for falling out of catch bodies.
+  CodeGenFunction::JumpDest Cont;
+  if (S.getNumCatchStmts())
+    Cont = CGF.getJumpDestInCurrentScope("eh.cont");
+
+  CodeGenFunction::FinallyInfo FinallyInfo;
+  if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt())
+    FinallyInfo = CGF.EnterFinallyBlock(Finally->getFinallyBody(),
+                                        beginCatchFn,
+                                        endCatchFn,
+                                        exceptionRethrowFn);
+
+  llvm::SmallVector<CatchHandler, 8> Handlers;
+
+  // Enter the catch, if there is one.
+  if (S.getNumCatchStmts()) {
+    for (unsigned I = 0, N = S.getNumCatchStmts(); I != N; ++I) {
+      const ObjCAtCatchStmt *CatchStmt = S.getCatchStmt(I);
+      const VarDecl *CatchDecl = CatchStmt->getCatchParamDecl();
+
+      Handlers.push_back(CatchHandler());
+      CatchHandler &Handler = Handlers.back();
+      Handler.Variable = CatchDecl;
+      Handler.Body = CatchStmt->getCatchBody();
+      Handler.Block = CGF.createBasicBlock("catch");
+
+      // @catch(...) always matches.
+      if (!CatchDecl) {
+        Handler.TypeInfo = 0; // catch-all
+        // Don't consider any other catches.
+        break;
+      }
+
+      Handler.TypeInfo = GetEHType(CatchDecl->getType());
+    }
+
+    EHCatchScope *Catch = CGF.EHStack.pushCatch(Handlers.size());
+    for (unsigned I = 0, E = Handlers.size(); I != E; ++I)
+      Catch->setHandler(I, Handlers[I].TypeInfo, Handlers[I].Block);
+  }
+  
+  // Emit the try body.
+  CGF.EmitStmt(S.getTryBody());
+
+  // Leave the try.
+  if (S.getNumCatchStmts())
+    CGF.EHStack.popCatch();
+
+  // Remember where we were.
+  CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP();
+
+  // Emit the handlers.
+  for (unsigned I = 0, E = Handlers.size(); I != E; ++I) {
+    CatchHandler &Handler = Handlers[I];
+
+    CGF.EmitBlock(Handler.Block);
+    llvm::Value *RawExn = CGF.Builder.CreateLoad(CGF.getExceptionSlot());
+
+    // Enter the catch.
+    llvm::CallInst *Exn =
+      CGF.Builder.CreateCall(beginCatchFn, RawExn,
+                             "exn.adjusted");
+    Exn->setDoesNotThrow();
+
+    // Add a cleanup to leave the catch.
+    bool EndCatchMightThrow = (Handler.Variable == 0);
+    CGF.EHStack.pushCleanup<CallObjCEndCatch>(NormalAndEHCleanup,
+                                              EndCatchMightThrow,
+                                              endCatchFn);
+
+    // Bind the catch parameter if it exists.
+    if (const VarDecl *CatchParam = Handler.Variable) {
+      const llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType());
+      llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType);
+
+      CGF.EmitAutoVarDecl(*CatchParam);
+      CGF.Builder.CreateStore(CastExn, CGF.GetAddrOfLocalVar(CatchParam));
+    }
+
+    CGF.ObjCEHValueStack.push_back(Exn);
+    CGF.EmitStmt(Handler.Body);
+    CGF.ObjCEHValueStack.pop_back();
+
+    // Leave the earlier cleanup.
+    CGF.PopCleanupBlock();
+
+    CGF.EmitBranchThroughCleanup(Cont);
+  }  
+
+  // Go back to the try-statement fallthrough.
+  CGF.Builder.restoreIP(SavedIP);
+
+  // Pop out of the normal cleanup on the finally.
+  if (S.getFinallyStmt())
+    CGF.ExitFinallyBlock(FinallyInfo);
+
+  if (Cont.isValid())
+    CGF.EmitBlock(Cont.getBlock());
 }
 
 void CGObjCGNU::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
@@ -1958,6 +2146,13 @@ void CGObjCGNU::EmitTryStmt(CodeGen::CodeGenFunction &CGF,
   // (or even _Unwind_DeleteException), but probably doesn't
   // interoperate very well with foreign exceptions.
 
+  // In Objective-C++ mode, we actually emit something equivalent to the C++
+  // exception handler.
+  if (CGM.getLangOptions().CPlusPlus) {
+    EmitObjCXXTryStmt(CGF, S);
+    return;
+  }
+
   // Jump destination for falling out of catch bodies.
   CodeGenFunction::JumpDest Cont;
   if (S.getNumCatchStmts())
index 7ec0ee4b5cab63cb37c8b5ba73b3a319c7cc6797..e276d98f12adaa66d0684b41e58cf01457743062 100644 (file)
@@ -16,6 +16,7 @@
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/Type.h"
 #include "clang/Frontend/CodeGenOptions.h"
+#include "CGObjCRuntime.h"
 
 using namespace clang;
 using namespace CodeGen;
@@ -977,6 +978,10 @@ llvm::Constant *CodeGenModule::GetAddrOfRTTIDescriptor(QualType Ty,
     const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(VMContext);
     return llvm::Constant::getNullValue(Int8PtrTy);
   }
+  
+  if (ForEH && Ty->isObjCObjectPointerType() && !Features.NeXTRuntime) {
+    return Runtime->GetEHType(Ty);
+  }
 
   return RTTIBuilder(*this).BuildTypeInfo(Ty);
 }
index 8b7bbb69540580cee3e79b81936963eac75f9cd6..220c22ad0f54294741c9e54ed7ee15b6edd8f299 100644 (file)
@@ -6775,10 +6775,7 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S,
       Diag(Loc, diag::err_objc_object_catch);
       Invalid = true;
     } else if (T->isObjCObjectPointerType()) {
-      if (!getLangOptions().NeXTRuntime) {
-        Diag(Loc, diag::err_objc_pointer_cxx_catch_gnu);
-        Invalid = true;
-      } else if (!getLangOptions().ObjCNonFragileABI) {
+      if (!getLangOptions().ObjCNonFragileABI) {
         Diag(Loc, diag::err_objc_pointer_cxx_catch_fragile);
         Invalid = true;
       }