Now with the patch :-(
On 15 September 2014 18:06, Rafael Espíndola <rafael.espind...@gmail.com> wrote: > Hi Reid, > > The attached patch is rebased on top of trunk, implements the rules > described in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62306#c6 and > I think has all your previous suggestions. > > I have sent an email to cxx-abi-dev documenting the above rules, but > got no replies so far. > > Cheers, > Rafael
diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h index a8d1199..cbe08a1 100644 --- a/include/clang/AST/Mangle.h +++ b/include/clang/AST/Mangle.h @@ -156,6 +156,11 @@ public: virtual void mangleItaniumThreadLocalWrapper(const VarDecl *D, raw_ostream &) = 0; + virtual void mangleCXXCtorComdat(const CXXConstructorDecl *D, + raw_ostream &) = 0; + virtual void mangleCXXDtorComdat(const CXXDestructorDecl *D, + raw_ostream &) = 0; + static bool classof(const MangleContext *C) { return C->getKind() == MK_Itanium; } diff --git a/include/clang/Basic/ABI.h b/include/clang/Basic/ABI.h index a3aad4b..bd24679 100644 --- a/include/clang/Basic/ABI.h +++ b/include/clang/Basic/ABI.h @@ -24,14 +24,15 @@ namespace clang { enum CXXCtorType { Ctor_Complete, ///< Complete object ctor Ctor_Base, ///< Base object ctor - Ctor_CompleteAllocating ///< Complete object allocating ctor + Ctor_Comdat ///< The COMDAT used for ctors }; /// \brief C++ destructor types. enum CXXDtorType { Dtor_Deleting, ///< Deleting dtor Dtor_Complete, ///< Complete object dtor - Dtor_Base ///< Base object dtor + Dtor_Base, ///< Base object dtor + Dtor_Comdat ///< The COMDAT used for dtors }; /// \brief A return adjustment. diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 486fab3..bdde133 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -150,6 +150,8 @@ public: void mangleCXXDtor(const CXXDestructorDecl *D, CXXDtorType Type, raw_ostream &) override; + void mangleCXXCtorComdat(const CXXConstructorDecl *D, raw_ostream &) override; + void mangleCXXDtorComdat(const CXXDestructorDecl *D, raw_ostream &) override; void mangleStaticGuardVariable(const VarDecl *D, raw_ostream &) override; void mangleDynamicInitializer(const VarDecl *D, raw_ostream &Out) override; void mangleDynamicAtExitDestructor(const VarDecl *D, @@ -3249,8 +3251,8 @@ void CXXNameMangler::mangleFunctionParam(const ParmVarDecl *parm) { void CXXNameMangler::mangleCXXCtorType(CXXCtorType T) { // <ctor-dtor-name> ::= C1 # complete object constructor // ::= C2 # base object constructor - // ::= C3 # complete object allocating constructor // + // In addition, C5 is a comdat name with C1 and C2 in it. switch (T) { case Ctor_Complete: Out << "C1"; @@ -3258,8 +3260,8 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T) { case Ctor_Base: Out << "C2"; break; - case Ctor_CompleteAllocating: - Out << "C3"; + case Ctor_Comdat: + Out << "C5"; break; } } @@ -3269,6 +3271,7 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) { // ::= D1 # complete object destructor // ::= D2 # base object destructor // + // In addition, D5 is a comdat name with D1, D2 and, if virtual, D0 in it. switch (T) { case Dtor_Deleting: Out << "D0"; @@ -3279,6 +3282,9 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) { case Dtor_Base: Out << "D2"; break; + case Dtor_Comdat: + Out << "D5"; + break; } } @@ -3689,6 +3695,18 @@ void ItaniumMangleContextImpl::mangleCXXDtor(const CXXDestructorDecl *D, Mangler.mangle(D); } +void ItaniumMangleContextImpl::mangleCXXCtorComdat(const CXXConstructorDecl *D, + raw_ostream &Out) { + CXXNameMangler Mangler(*this, Out, D, Ctor_Comdat); + Mangler.mangle(D); +} + +void ItaniumMangleContextImpl::mangleCXXDtorComdat(const CXXDestructorDecl *D, + raw_ostream &Out) { + CXXNameMangler Mangler(*this, Out, D, Dtor_Comdat); + Mangler.mangle(D); +} + void ItaniumMangleContextImpl::mangleThunk(const CXXMethodDecl *MD, const ThunkInfo &Thunk, raw_ostream &Out) { diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 9938c24..5e87fc2 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -198,6 +198,10 @@ void CodeGenModule::createCUDARuntime() { CUDARuntime = CreateNVCUDARuntime(*this); } +void CodeGenModule::addReplacement(StringRef Name, llvm::Constant *C) { + Replacements[Name] = C; +} + void CodeGenModule::applyReplacements() { for (ReplacementsTy::iterator I = Replacements.begin(), E = Replacements.end(); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 6012606..b36d4a6 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -1058,9 +1058,17 @@ public: void setFunctionDefinitionAttributes(const FunctionDecl *D, llvm::Function *F); -private: llvm::GlobalValue *GetGlobalValue(StringRef Ref); + /// Set attributes which are common to any form of a global definition (alias, + /// Objective-C method, function, global variable). + /// + /// NOTE: This should only be called for definitions. + void SetCommonAttributes(const Decl *D, llvm::GlobalValue *GV); + + void addReplacement(StringRef Name, llvm::Constant *C); +private: + llvm::Constant * GetOrCreateLLVMFunction(StringRef MangledName, llvm::Type *Ty, GlobalDecl D, bool ForVTable, bool DontDefer = false, @@ -1070,12 +1078,6 @@ private: llvm::PointerType *PTy, const VarDecl *D); - /// Set attributes which are common to any form of a global definition (alias, - /// Objective-C method, function, global variable). - /// - /// NOTE: This should only be called for definitions. - void SetCommonAttributes(const Decl *D, llvm::GlobalValue *GV); - void setNonAliasAttributes(const Decl *D, llvm::GlobalObject *GO); /// Set function attributes for a function declaration. diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index d422f55..0ca274b 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -80,8 +80,8 @@ inline StructorType getFromCtorType(CXXCtorType T) { return StructorType::Complete; case Ctor_Base: return StructorType::Base; - case Ctor_CompleteAllocating: - llvm_unreachable("invalid enum"); + case Ctor_Comdat: + llvm_unreachable("not expecting a COMDAT"); } llvm_unreachable("not a CXXCtorType"); } @@ -106,6 +106,8 @@ inline StructorType getFromDtorType(CXXDtorType T) { return StructorType::Complete; case Dtor_Base: return StructorType::Base; + case Dtor_Comdat: + llvm_unreachable("not expecting a COMDAT"); } llvm_unreachable("not a CXXDtorType"); } diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 91e9d4d..038d890 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -2999,35 +2999,102 @@ ItaniumCXXABI::RTTIUniquenessKind ItaniumCXXABI::classifyRTTIUniqueness( return RUK_NonUniqueVisible; } -static void emitCXXConstructor(CodeGenModule &CGM, - const CXXConstructorDecl *ctor, - StructorType ctorType) { - if (!ctor->getParent()->getNumVBases() && - (ctorType == StructorType::Complete || ctorType == StructorType::Base)) { - // The complete constructor is equivalent to the base constructor - // for classes with no virtual bases. Try to emit it as an alias. - bool ProducedAlias = !CGM.TryEmitDefinitionAsAlias( - GlobalDecl(ctor, Ctor_Complete), GlobalDecl(ctor, Ctor_Base), true); - if (ctorType == StructorType::Complete && ProducedAlias) - return; +// Find out how to codegen the complete destructor and constructor +namespace { +enum class StructorCodegen { Emit, RAUW, Alias, COMDAT }; +} +static StructorCodegen getCodegenToUse(CodeGenModule &CGM, + const CXXMethodDecl *MD) { + if (!CGM.getCodeGenOpts().CXXCtorDtorAliases) + return StructorCodegen::Emit; + + // The complete and base structors are not equivalent if there are any virtual + // bases, so emit separate functions. + if (MD->getParent()->getNumVBases()) + return StructorCodegen::Emit; + + GlobalDecl AliasDecl; + if (const auto *DD = dyn_cast<CXXDestructorDecl>(MD)) { + AliasDecl = GlobalDecl(DD, Dtor_Complete); + } else { + const auto *CD = cast<CXXConstructorDecl>(MD); + AliasDecl = GlobalDecl(CD, Ctor_Complete); + } + llvm::GlobalValue::LinkageTypes Linkage = CGM.getFunctionLinkage(AliasDecl); + + if (llvm::GlobalValue::isDiscardableIfUnused(Linkage)) + return StructorCodegen::RAUW; + + // FIXME: Should we allow available_externally aliases? + if (!llvm::GlobalAlias::isValidLinkage(Linkage)) + return StructorCodegen::RAUW; + + if (llvm::GlobalValue::isWeakForLinker(Linkage)) + return StructorCodegen::COMDAT; + + return StructorCodegen::Alias; +} + +static void emitConstructorDestructorAlias(CodeGenModule &CGM, + GlobalDecl AliasDecl, + GlobalDecl TargetDecl) { + llvm::GlobalValue::LinkageTypes Linkage = CGM.getFunctionLinkage(AliasDecl); + + StringRef MangledName = CGM.getMangledName(AliasDecl); + llvm::GlobalValue *Entry = CGM.GetGlobalValue(MangledName); + if (Entry && !Entry->isDeclaration()) + return; + + auto *Aliasee = cast<llvm::GlobalValue>(CGM.GetAddrOfGlobal(TargetDecl)); + llvm::PointerType *AliasType = Aliasee->getType(); + + // Create the alias with no name. + auto *Alias = llvm::GlobalAlias::create( + AliasType->getElementType(), 0, Linkage, "", Aliasee, &CGM.getModule()); + + // Switch any previous uses to the alias. + if (Entry) { + assert(Entry->getType() == AliasType && + "declaration exists with different type"); + Alias->takeName(Entry); + Entry->replaceAllUsesWith(Alias); + Entry->eraseFromParent(); + } else { + Alias->setName(MangledName); } - CGM.codegenCXXStructor(ctor, ctorType); -} - -static void emitCXXDestructor(CodeGenModule &CGM, const CXXDestructorDecl *dtor, - StructorType dtorType) { - // The complete destructor is equivalent to the base destructor for - // classes with no virtual bases, so try to emit it as an alias. - if (!dtor->getParent()->getNumVBases() && - (dtorType == StructorType::Complete || dtorType == StructorType::Base)) { - bool ProducedAlias = !CGM.TryEmitDefinitionAsAlias( - GlobalDecl(dtor, Dtor_Complete), GlobalDecl(dtor, Dtor_Base), true); - if (ProducedAlias) { - if (dtorType == StructorType::Complete) - return; - if (dtor->isVirtual()) - CGM.getVTables().EmitThunks(GlobalDecl(dtor, Dtor_Complete)); + // Finally, set up the alias with its proper name and attributes. + CGM.SetCommonAttributes(cast<NamedDecl>(AliasDecl.getDecl()), Alias); +} + +void ItaniumCXXABI::emitCXXStructor(const CXXMethodDecl *MD, + StructorType Type) { + auto *CD = dyn_cast<CXXConstructorDecl>(MD); + const CXXDestructorDecl *DD = CD ? nullptr : cast<CXXDestructorDecl>(MD); + + StructorCodegen CGType = getCodegenToUse(CGM, MD); + + if (Type == StructorType::Complete) { + GlobalDecl CompleteDecl; + GlobalDecl BaseDecl; + if (CD) { + CompleteDecl = GlobalDecl(CD, Ctor_Complete); + BaseDecl = GlobalDecl(CD, Ctor_Base); + } else { + CompleteDecl = GlobalDecl(DD, Dtor_Complete); + BaseDecl = GlobalDecl(DD, Dtor_Base); + } + + if (CGType == StructorCodegen::Alias || CGType == StructorCodegen::COMDAT) { + emitConstructorDestructorAlias(CGM, CompleteDecl, BaseDecl); + return; + } + + if (CGType == StructorCodegen::RAUW) { + StringRef MangledName = CGM.getMangledName(CompleteDecl); + auto *Aliasee = cast<llvm::GlobalValue>(CGM.GetAddrOfGlobal(BaseDecl)); + CGM.addReplacement(MangledName, Aliasee); + return; } } @@ -3035,17 +3102,20 @@ static void emitCXXDestructor(CodeGenModule &CGM, const CXXDestructorDecl *dtor, // base class if there is exactly one non-virtual base class with a // non-trivial destructor, there are no fields with a non-trivial // destructor, and the body of the destructor is trivial. - if (dtorType == StructorType::Base && !CGM.TryEmitBaseDestructorAsAlias(dtor)) + if (DD && Type == StructorType::Base && CGType != StructorCodegen::COMDAT && + !CGM.TryEmitBaseDestructorAsAlias(DD)) return; - CGM.codegenCXXStructor(dtor, dtorType); -} + llvm::Function *Fn = CGM.codegenCXXStructor(MD, Type); -void ItaniumCXXABI::emitCXXStructor(const CXXMethodDecl *MD, - StructorType Type) { - if (auto *CD = dyn_cast<CXXConstructorDecl>(MD)) { - emitCXXConstructor(CGM, CD, Type); - return; + if (CGType == StructorCodegen::COMDAT) { + SmallString<256> Buffer; + llvm::raw_svector_ostream Out(Buffer); + if (DD) + getMangleContext().mangleCXXDtorComdat(DD, Out); + else + getMangleContext().mangleCXXCtorComdat(CD, Out); + llvm::Comdat *C = CGM.getModule().getOrInsertComdat(Out.str()); + Fn->setComdat(C); } - emitCXXDestructor(CGM, cast<CXXDestructorDecl>(MD), Type); } diff --git a/test/CodeGenCXX/ctor-dtor-alias.cpp b/test/CodeGenCXX/ctor-dtor-alias.cpp index 8be5494..10923d1 100644 --- a/test/CodeGenCXX/ctor-dtor-alias.cpp +++ b/test/CodeGenCXX/ctor-dtor-alias.cpp @@ -6,18 +6,23 @@ // RUN: FileCheck --check-prefix=CHECK3 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK4 --input-file=%t %s // RUN: FileCheck --check-prefix=CHECK5 --input-file=%t %s +// RUN: FileCheck --check-prefix=CHECK6 --input-file=%t %s namespace test1 { -// test that we don't produce an alias when the destructor is weak_odr. The -// reason to avoid it that another TU might have no explicit template -// instantiation definition or declaration, causing it to to output only -// one of the destructors as linkonce_odr, producing a different comdat. - -// CHECK1: define weak_odr void @_ZN5test16foobarIvEC2Ev -// CHECK1: define weak_odr void @_ZN5test16foobarIvEC1Ev - -template <typename T> struct foobar { +// Test that we produce the apropriate comdats when creating aliases to +// weak_odr constructors and destructors. + +// CHECK1: @_ZN5test16foobarIvEC1Ev = weak_odr alias void {{.*}} @_ZN5test16foobarIvEC2Ev +// CHECK1: @_ZN5test16foobarIvED1Ev = weak_odr alias void (%"struct.test1::foobar"*)* @_ZN5test16foobarIvED2Ev +// CHECK1: define weak_odr void @_ZN5test16foobarIvEC2Ev({{.*}} comdat $_ZN5test16foobarIvEC5Ev +// CHECK1: define weak_odr void @_ZN5test16foobarIvED2Ev({{.*}} comdat $_ZN5test16foobarIvED5Ev +// CHECK1: define weak_odr void @_ZN5test16foobarIvED0Ev({{.*}} comdat $_ZN5test16foobarIvED5Ev +// CHECK1-NOT: comdat + +template <typename T> +struct foobar { foobar() {} + virtual ~foobar() {} }; template struct foobar<void>; @@ -187,3 +192,38 @@ void fn1() { new C; } + +namespace test10 { +// Test that if a destructor is in a comdat, we don't try to emit is as an +// alias to a base class destructor. +struct bar { + ~bar(); +}; +bar::~bar() { +} +} // closing the namespace causes ~bar to be sent to CodeGen +namespace test10 { +template <typename T> +struct foo : public bar { + ~foo(); +}; +template <typename T> +foo<T>::~foo() {} +template class foo<int>; +// CHECK5: define weak_odr void @_ZN6test103fooIiED2Ev({{.*}} comdat $_ZN6test103fooIiED5Ev +} + +namespace test11 { +// Test that when we don't have to worry about COMDATs we produce an alias +// from complate to base and from base to base class base. +struct bar { + ~bar(); +}; +bar::~bar() {} +struct foo : public bar { + ~foo(); +}; +foo::~foo() {} +// CHECK6: @_ZN6test113fooD2Ev = alias {{.*}} @_ZN6test113barD2Ev +// CHECK6: @_ZN6test113fooD1Ev = alias {{.*}} @_ZN6test113fooD2Ev +} diff --git a/test/CodeGenCXX/destructors.cpp b/test/CodeGenCXX/destructors.cpp index 0bfc8ec..bc9a683 100644 --- a/test/CodeGenCXX/destructors.cpp +++ b/test/CodeGenCXX/destructors.cpp @@ -204,18 +204,14 @@ namespace test3 { // CHECK4: call void @_ZN5test312_GLOBAL__N_11DD0Ev( // CHECK4: ret void - // CHECK4-LABEL: define internal void @_ZThn8_N5test312_GLOBAL__N_11CD1Ev( - // CHECK4: getelementptr inbounds i8* {{.*}}, i64 -8 - // CHECK4: call void @_ZN5test312_GLOBAL__N_11CD2Ev( - // CHECK4: ret void + // CHECK4-LABEL: declare void @_ZN5test31BD2Ev( + // CHECK4-LABEL: declare void @_ZN5test31AD2Ev( // CHECK4-LABEL: define internal void @_ZN5test312_GLOBAL__N_11CD2Ev(%"struct.test3::(anonymous namespace)::C"* %this) unnamed_addr // CHECK4: invoke void @_ZN5test31BD2Ev( // CHECK4: call void @_ZN5test31AD2Ev( // CHECK4: ret void - // CHECK4: declare void @_ZN5test31BD2Ev( - // CHECK4: declare void @_ZN5test31AD2Ev( // CHECK4-LABEL: define internal void @_ZN5test312_GLOBAL__N_11CD0Ev(%"struct.test3::(anonymous namespace)::C"* %this) unnamed_addr // CHECK4: invoke void @_ZN5test312_GLOBAL__N_11CD2Ev( @@ -226,6 +222,11 @@ namespace test3 { // CHECK4: call void @_ZdlPv({{.*}}) [[NUW]] // CHECK4: resume { i8*, i32 } + // CHECK4-LABEL: define internal void @_ZThn8_N5test312_GLOBAL__N_11CD1Ev( + // CHECK4: getelementptr inbounds i8* {{.*}}, i64 -8 + // CHECK4: call void @_ZN5test312_GLOBAL__N_11CD2Ev( + // CHECK4: ret void + // CHECK4-LABEL: define internal void @_ZThn8_N5test312_GLOBAL__N_11CD0Ev( // CHECK4: getelementptr inbounds i8* {{.*}}, i64 -8 // CHECK4: call void @_ZN5test312_GLOBAL__N_11CD0Ev( diff --git a/test/CodeGenCXX/virtual-destructor-calls.cpp b/test/CodeGenCXX/virtual-destructor-calls.cpp index 3e7fa82..f0e3dc5 100644 --- a/test/CodeGenCXX/virtual-destructor-calls.cpp +++ b/test/CodeGenCXX/virtual-destructor-calls.cpp @@ -17,8 +17,8 @@ struct B : A { // CHECK: @_ZN1BD1Ev = alias {{.*}} @_ZN1BD2Ev // (aliases from C) -// CHECK: @_ZN1CD1Ev = alias {{.*}} @_ZN1CD2Ev // CHECK: @_ZN1CD2Ev = alias bitcast {{.*}} @_ZN1BD2Ev +// CHECK: @_ZN1CD1Ev = alias {{.*}} @_ZN1CD2Ev // Base dtor: actually calls A's base dtor. // CHECK-LABEL: define void @_ZN1BD2Ev(%struct.B* %this) unnamed_addr
_______________________________________________ cfe-commits mailing list cfe-commits@cs.uiuc.edu http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits