From: David Chisnall Date: Sun, 20 Mar 2011 21:35:39 +0000 (+0000) Subject: Fix Objective-C++ exceptions (GNU runtime). X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=80558d2db10a96eada4d8fb79949b3263b44652b;p=clang Fix Objective-C++ exceptions (GNU runtime). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@127980 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index 7fdfe70042..cc4446fcf0 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -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) { diff --git a/lib/CodeGen/CGException.h b/lib/CodeGen/CGException.h index 1f9b8964dc..5a743b51f6 100644 --- a/lib/CodeGen/CGException.h +++ b/lib/CodeGen/CGException.h @@ -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; diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index fa87311662..8b45ed3f3a 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -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(); + 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 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 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 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(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()) diff --git a/lib/CodeGen/CGRTTI.cpp b/lib/CodeGen/CGRTTI.cpp index 7ec0ee4b5c..e276d98f12 100644 --- a/lib/CodeGen/CGRTTI.cpp +++ b/lib/CodeGen/CGRTTI.cpp @@ -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); } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 8b7bbb6954..220c22ad0f 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -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; }