From b25938303de0976b9f189363d43033e5788e3d36 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 16 Sep 2010 06:16:50 +0000 Subject: [PATCH] Opportunistically use the C++ personality function in ObjC++ translation units that don't catch ObjC types. rdar://problem/8434851 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@114070 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGException.cpp | 106 +++++++++++++++++++++++++++++----- lib/CodeGen/CGException.h | 12 ++-- lib/CodeGen/CodeGenModule.cpp | 2 + lib/CodeGen/CodeGenModule.h | 4 ++ 4 files changed, 105 insertions(+), 19 deletions(-) diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index 1063e28b84..d2df1fba1d 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -14,6 +14,7 @@ #include "clang/AST/StmtCXX.h" #include "llvm/Intrinsics.h" +#include "llvm/IntrinsicInst.h" #include "llvm/Support/CallSite.h" #include "CGObjCRuntime.h" @@ -287,7 +288,7 @@ static llvm::Constant *getTerminateFn(CodeGenFunction &CGF) { } static llvm::Constant *getCatchallRethrowFn(CodeGenFunction &CGF, - const char *Name) { + llvm::StringRef Name) { const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); std::vector Args(1, Int8PtrTy); @@ -357,17 +358,95 @@ const EHPersonality &EHPersonality::get(const LangOptions &L) { return getCPersonality(L); } -static llvm::Constant *getPersonalityFn(CodeGenFunction &CGF, +static llvm::Constant *getPersonalityFn(CodeGenModule &CGM, const EHPersonality &Personality) { - const char *Name = Personality.getPersonalityFnName(); - llvm::Constant *Fn = - CGF.CGM.CreateRuntimeFunction(llvm::FunctionType::get( - llvm::Type::getInt32Ty( - CGF.CGM.getLLVMContext()), - true), - Name); - return llvm::ConstantExpr::getBitCast(Fn, CGF.CGM.PtrToInt8Ty); + CGM.CreateRuntimeFunction(llvm::FunctionType::get( + llvm::Type::getInt32Ty(CGM.getLLVMContext()), + true), + Personality.getPersonalityFnName()); + return Fn; +} + +static llvm::Constant *getOpaquePersonalityFn(CodeGenModule &CGM, + const EHPersonality &Personality) { + llvm::Constant *Fn = getPersonalityFn(CGM, Personality); + return llvm::ConstantExpr::getBitCast(Fn, CGM.PtrToInt8Ty); +} + +/// Check whether a personality function could reasonably be swapped +/// for a C++ personality function. +static bool PersonalityHasOnlyCXXUses(llvm::Constant *Fn) { + for (llvm::Constant::use_iterator + I = Fn->use_begin(), E = Fn->use_end(); I != E; ++I) { + llvm::User *User = *I; + + // Conditionally white-list bitcasts. + if (llvm::ConstantExpr *CE = dyn_cast(User)) { + if (CE->getOpcode() != llvm::Instruction::BitCast) return false; + if (!PersonalityHasOnlyCXXUses(CE)) + return false; + continue; + } + + // Otherwise, it has to be a selector call. + if (!isa(User)) return false; + + llvm::EHSelectorInst *Selector = cast(User); + for (unsigned I = 2, E = Selector->getNumArgOperands(); I != E; ++I) { + // Look for something that would've been returned by the ObjC + // runtime's GetEHType() method. + llvm::GlobalVariable *GV + = dyn_cast(Selector->getArgOperand(I)); + if (!GV) continue; + + // ObjC EH selector entries are always global variables with + // names starting like this. + if (GV->getName().startswith("OBJC_EHTYPE")) + return false; + } + } + + return true; +} + +/// Try to use the C++ personality function in ObjC++. Not doing this +/// can cause some incompatibilities with gcc, which is more +/// aggressive about only using the ObjC++ personality in a function +/// when it really needs it. +void CodeGenModule::SimplifyPersonality() { + // For now, this is really a Darwin-specific operation. + if (Context.Target.getTriple().getOS() != llvm::Triple::Darwin) + return; + + // If we're not in ObjC++ -fexceptions, there's nothing to do. + if (!Features.CPlusPlus || !Features.ObjC1 || !Features.Exceptions) + return; + + const EHPersonality &ObjCXX = EHPersonality::get(Features); + const EHPersonality &CXX = getCXXPersonality(Features); + if (&ObjCXX == &CXX || + ObjCXX.getPersonalityFnName() == CXX.getPersonalityFnName()) + return; + + llvm::Function *Fn = + getModule().getFunction(ObjCXX.getPersonalityFnName()); + + // Nothing to do if it's unused. + if (!Fn || Fn->use_empty()) return; + + // Can't do the optimization if it has non-C++ uses. + if (!PersonalityHasOnlyCXXUses(Fn)) return; + + // Create the C++ personality function and kill off the old + // function. + llvm::Constant *CXXFn = getPersonalityFn(*this, CXX); + + // This can happen if the user is screwing with us. + if (Fn->getType() != CXXFn->getType()) return; + + Fn->replaceAllUsesWith(CXXFn); + Fn->eraseFromParent(); } /// Returns the value to inject into a selector to indicate the @@ -757,7 +836,7 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() { // Build the selector arguments. llvm::SmallVector EHSelector; EHSelector.push_back(Exn); - EHSelector.push_back(getPersonalityFn(*this, Personality)); + EHSelector.push_back(getOpaquePersonalityFn(CGM, Personality)); // Accumulate all the handlers in scope. llvm::DenseMap EHHandlers; @@ -1502,7 +1581,7 @@ llvm::BasicBlock *CodeGenFunction::getTerminateLandingPad() { // Tell the backend what the exception table should be: // nothing but a catch-all. - llvm::Value *Args[3] = { Exn, getPersonalityFn(*this, Personality), + llvm::Value *Args[3] = { Exn, getOpaquePersonalityFn(CGM, Personality), getCatchAllValue(*this) }; Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::eh_selector), Args, Args+3, "eh.selector") @@ -1553,8 +1632,9 @@ CodeGenFunction::UnwindDest CodeGenFunction::getRethrowDest() { // This can always be a call because we necessarily didn't find // anything on the EH stack which needs our help. + llvm::StringRef RethrowName = Personality.getCatchallRethrowFnName(); llvm::Constant *RethrowFn; - if (const char *RethrowName = Personality.getCatchallRethrowFnName()) + if (!RethrowName.empty()) RethrowFn = getCatchallRethrowFn(*this, RethrowName); else RethrowFn = getUnwindResumeOrRethrowFn(); diff --git a/lib/CodeGen/CGException.h b/lib/CodeGen/CGException.h index ce8bd49751..9c81a4e7a8 100644 --- a/lib/CodeGen/CGException.h +++ b/lib/CodeGen/CGException.h @@ -29,15 +29,15 @@ namespace CodeGen { /// The exceptions personality for a function. When class EHPersonality { - const char *PersonalityFn; + llvm::StringRef PersonalityFn; // If this is non-null, this personality requires a non-standard // function for rethrowing an exception after a catchall cleanup. // This function must have prototype void(void*). - const char *CatchallRethrowFn; + llvm::StringRef CatchallRethrowFn; - EHPersonality(const char *PersonalityFn, - const char *CatchallRethrowFn = 0) + EHPersonality(llvm::StringRef PersonalityFn, + llvm::StringRef CatchallRethrowFn = llvm::StringRef()) : PersonalityFn(PersonalityFn), CatchallRethrowFn(CatchallRethrowFn) {} @@ -49,8 +49,8 @@ public: static const EHPersonality GNU_CPlusPlus; static const EHPersonality GNU_CPlusPlus_SJLJ; - const char *getPersonalityFnName() const { return PersonalityFn; } - const char *getCatchallRethrowFnName() const { return CatchallRethrowFn; } + llvm::StringRef getPersonalityFnName() const { return PersonalityFn; } + llvm::StringRef getCatchallRethrowFnName() const { return CatchallRethrowFn; } }; /// A protected scope for zero-cost EH handling. diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 173fbf4ff7..f5ba76c1b9 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -110,6 +110,8 @@ void CodeGenModule::Release() { EmitAnnotations(); EmitLLVMUsed(); + SimplifyPersonality(); + if (getCodeGenOpts().EmitDeclMetadata) EmitDeclMetadata(); } diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index cabff9e1ca..898f9c3668 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -613,6 +613,10 @@ private: /// lazily; this is only relevant for definitions. The given decl /// must be either a function or var decl. bool MayDeferGeneration(const ValueDecl *D); + + /// SimplifyPersonality - Check whether we can use a "simpler", more + /// core exceptions personality function. + void SimplifyPersonality(); }; } // end namespace CodeGen } // end namespace clang