There are situations when clang knows that the C1 and C2 constructors or the D2 and D2 destructors are identical. We already optimize some of these cases, but cannot optimize it when the GlobalValue is weak_odr.
The problem with weak_odr is that an old TU seeing the same code will have a C1 and a C2 comdat with the corresponding symbols. We cannot suddenly start putting the C2 symbol in the C1 comdat as we cannot guarantee that the linker will not pick a .o with only C1 in it. The solution implemented by GCC is to expand the ABI to have a comdat whose name uses a C5/D5 suffix and always has both symbols. That is what this patch implements. We could also use C5/D5 for linkonce_odr, but it is probably better to change one at a time. Cheers, Rafael
diff --git a/include/clang/AST/Mangle.h b/include/clang/AST/Mangle.h index a8d1199..9423582 100644 --- a/include/clang/AST/Mangle.h +++ b/include/clang/AST/Mangle.h @@ -111,6 +111,8 @@ public: raw_ostream &) = 0; virtual void mangleCXXDtor(const CXXDestructorDecl *D, CXXDtorType Type, raw_ostream &) = 0; + virtual void mangleCXXCtorComdat(const CXXConstructorDecl *D, raw_ostream &); + virtual void mangleCXXDtorComdat(const CXXDestructorDecl *D, raw_ostream &); virtual void mangleStringLiteral(const StringLiteral *SL, raw_ostream &) = 0; void mangleGlobalBlock(const BlockDecl *BD, diff --git a/include/clang/Basic/ABI.h b/include/clang/Basic/ABI.h index a3aad4b..fcb6b21 100644 --- a/include/clang/Basic/ABI.h +++ b/include/clang/Basic/ABI.h @@ -27,6 +27,8 @@ enum CXXCtorType { Ctor_CompleteAllocating ///< Complete object allocating ctor }; +enum CXXCtorComdatType { Ctor_Comdat = Ctor_CompleteAllocating + 1 }; + /// \brief C++ destructor types. enum CXXDtorType { Dtor_Deleting, ///< Deleting dtor @@ -34,6 +36,8 @@ enum CXXDtorType { Dtor_Base ///< Base object dtor }; +enum CXXDtorComdatType { Dtor_Comdat = Dtor_Base + 1 }; + /// \brief A return adjustment. struct ReturnAdjustment { /// \brief The non-virtual adjustment from the derived object to its diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 486fab3..b8befbe 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -149,7 +149,8 @@ public: raw_ostream &) override; 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, @@ -261,13 +262,13 @@ public: !isa<CXXConstructorDecl>(D))); } CXXNameMangler(ItaniumMangleContextImpl &C, raw_ostream &Out_, - const CXXConstructorDecl *D, CXXCtorType Type) - : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), - SeqID(0) { } + const CXXConstructorDecl *D, unsigned Type) + : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), + SeqID(0) {} CXXNameMangler(ItaniumMangleContextImpl &C, raw_ostream &Out_, - const CXXDestructorDecl *D, CXXDtorType Type) - : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), - SeqID(0) { } + const CXXDestructorDecl *D, unsigned Type) + : Context(C), Out(Out_), Structor(getStructor(D)), StructorType(Type), + SeqID(0) {} #if MANGLE_CHECKER ~CXXNameMangler() { @@ -374,8 +375,8 @@ private: DeclarationName name, unsigned knownArity); void mangleExpression(const Expr *E, unsigned Arity = UnknownArity); - void mangleCXXCtorType(CXXCtorType T); - void mangleCXXDtorType(CXXDtorType T); + void mangleCXXCtorType(unsigned T); + void mangleCXXDtorType(unsigned T); void mangleTemplateArgs(const ASTTemplateArgumentListInfo &TemplateArgs); void mangleTemplateArgs(const TemplateArgument *TemplateArgs, @@ -1188,7 +1189,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, if (ND == Structor) // If the named decl is the C++ constructor we're mangling, use the type // we were given. - mangleCXXCtorType(static_cast<CXXCtorType>(StructorType)); + mangleCXXCtorType(StructorType); else // Otherwise, use the complete constructor name. This is relevant if a // class with a constructor is declared within a constructor. @@ -1199,7 +1200,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, if (ND == Structor) // If the named decl is the C++ destructor we're mangling, use the type we // were given. - mangleCXXDtorType(static_cast<CXXDtorType>(StructorType)); + mangleCXXDtorType(StructorType); else // Otherwise, use the complete destructor name. This is relevant if a // class with a destructor is declared within a destructor. @@ -3246,12 +3247,15 @@ void CXXNameMangler::mangleFunctionParam(const ParmVarDecl *parm) { Out << '_'; } -void CXXNameMangler::mangleCXXCtorType(CXXCtorType T) { +void CXXNameMangler::mangleCXXCtorType(unsigned 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) { + default: + llvm_unreachable("unsupported type"); case Ctor_Complete: Out << "C1"; break; @@ -3261,15 +3265,21 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T) { case Ctor_CompleteAllocating: Out << "C3"; break; + case Ctor_Comdat: + Out << "C5"; + break; } } -void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) { +void CXXNameMangler::mangleCXXDtorType(unsigned T) { // <ctor-dtor-name> ::= D0 # deleting destructor // ::= D1 # complete object destructor // ::= D2 # base object destructor // + // In addition, C5 is a comdat name with C1 and C2 in it. switch (T) { + default: + llvm_unreachable("unsupported type"); case Dtor_Deleting: Out << "D0"; break; @@ -3279,6 +3289,9 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) { case Dtor_Base: Out << "D2"; break; + case Dtor_Comdat: + Out << "D5"; + break; } } @@ -3689,6 +3702,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/AST/Mangle.cpp b/lib/AST/Mangle.cpp index fdc00e3..bbc72a4 100644 --- a/lib/AST/Mangle.cpp +++ b/lib/AST/Mangle.cpp @@ -90,6 +90,16 @@ static StdOrFastCC getStdOrFastCallMangling(const ASTContext &Context, } } +void MangleContext::mangleCXXCtorComdat(const CXXConstructorDecl *D, + raw_ostream &Out) { + llvm_unreachable("Should not be used by this mangling"); +} + +void MangleContext::mangleCXXDtorComdat(const CXXDestructorDecl *D, + raw_ostream &Out) { + llvm_unreachable("Should not be used by this mangling"); +} + bool MangleContext::shouldMangleDeclName(const NamedDecl *D) { const ASTContext &ASTContext = getASTContext(); diff --git a/lib/CodeGen/CGCXX.cpp b/lib/CodeGen/CGCXX.cpp index 545c5ef..6cf7c56 100644 --- a/lib/CodeGen/CGCXX.cpp +++ b/lib/CodeGen/CGCXX.cpp @@ -196,33 +196,136 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl, return false; } +// Find out how to codegen the complete destructor and constructor +namespace { +enum class CtorDtorCodeGen { Emit, RAUW, Alias, COMDAT }; +} +static CtorDtorCodeGen getCodegenToUse(CodeGenModule &CGM, + const CXXMethodDecl *MD) { + if (!CGM.getCodeGenOpts().CXXCtorDtorAliases) + return CtorDtorCodeGen::Emit; + + // The complete constructor is equivalent to the base constructor + // for classes with no virtual bases. + if (MD->getParent()->getNumVBases()) + return CtorDtorCodeGen::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); + + // FIXME: With COMDATs it is probably better to use alias for linkonce_odr. + if (llvm::GlobalValue::isDiscardableIfUnused(Linkage)) + return CtorDtorCodeGen::RAUW; + + // FIXME: Should we allow available_externally aliases? + if (!llvm::GlobalAlias::isValidLinkage(Linkage)) + return CtorDtorCodeGen::RAUW; + + if (llvm::GlobalValue::isWeakForLinker(Linkage)) + return CtorDtorCodeGen::COMDAT; + + return CtorDtorCodeGen::Alias; +} + +void CodeGenModule::emitConstructorDestructorAlias(GlobalDecl AliasDecl, + GlobalDecl TargetDecl) { + llvm::GlobalValue::LinkageTypes Linkage = getFunctionLinkage(AliasDecl); + + StringRef MangledName = getMangledName(AliasDecl); + llvm::GlobalValue *Entry = GetGlobalValue(MangledName); + if (Entry && !Entry->isDeclaration()) + return; + + auto *Aliasee = cast<llvm::GlobalValue>(GetAddrOfGlobal(TargetDecl)); + llvm::PointerType *AliasType = Aliasee->getType(); + + // Create the alias with no name. + auto *Alias = llvm::GlobalAlias::create(AliasType->getElementType(), 0, + Linkage, "", Aliasee, &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); + } + + // Finally, set up the alias with its proper name and attributes. + SetCommonAttributes(cast<NamedDecl>(AliasDecl.getDecl()), Alias); +} + +llvm::Function * +CodeGenModule::codegenCXXConstructor(const CXXConstructorDecl *CD, + CXXCtorType CtorType) { + const CGFunctionInfo &FnInfo = + getTypes().arrangeCXXConstructorDeclaration(CD, CtorType); + auto *Fn = cast<llvm::Function>( + GetAddrOfCXXConstructor(CD, CtorType, &FnInfo, true)); + if (!Fn->isDeclaration()) + return Fn; + GlobalDecl GD(CD, CtorType); + setFunctionLinkage(GD, Fn); + CodeGenFunction(*this).GenerateCode(GD, Fn, FnInfo); + setFunctionDefinitionAttributes(CD, Fn); + SetLLVMFunctionAttributesForDefinition(CD, Fn); + return Fn; +} + void CodeGenModule::EmitCXXConstructor(const CXXConstructorDecl *ctor, CXXCtorType ctorType) { + // If there are no constructor variants, always emit the complete destructor. if (!getTarget().getCXXABI().hasConstructorVariants()) { - // If there are no constructor variants, always emit the complete destructor. - ctorType = Ctor_Complete; - } else if (!ctor->getParent()->getNumVBases() && - (ctorType == Ctor_Complete || ctorType == Ctor_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 = - !TryEmitDefinitionAsAlias(GlobalDecl(ctor, Ctor_Complete), - GlobalDecl(ctor, Ctor_Base), true); - if (ctorType == Ctor_Complete && ProducedAlias) - return; + codegenCXXConstructor(ctor, Ctor_Complete); + return; } - const CGFunctionInfo &fnInfo = - getTypes().arrangeCXXConstructorDeclaration(ctor, ctorType); + assert(ctorType == Ctor_Complete || ctorType == Ctor_Base); - auto *fn = cast<llvm::Function>( - GetAddrOfCXXConstructor(ctor, ctorType, &fnInfo, true)); - setFunctionLinkage(GlobalDecl(ctor, ctorType), fn); + CtorDtorCodeGen CompleteCG = getCodegenToUse(*this, ctor); + if (CompleteCG == CtorDtorCodeGen::COMDAT) { + // Make sure both are emitted. + if (ctorType == Ctor_Complete) + GetAddrOfCXXConstructor(ctor, Ctor_Base); + else + GetAddrOfCXXConstructor(ctor, Ctor_Complete); + } + + if (ctorType == Ctor_Complete) { + GlobalDecl CompleteDecl(ctor, Ctor_Complete); + GlobalDecl BaseDecl(ctor, Ctor_Base); + + if (CompleteCG == CtorDtorCodeGen::Alias || + CompleteCG == CtorDtorCodeGen::COMDAT) { + emitConstructorDestructorAlias(CompleteDecl, BaseDecl); + return; + } + if (CompleteCG == CtorDtorCodeGen::RAUW) { + StringRef MangledName = getMangledName(CompleteDecl); + auto *Aliasee = cast<llvm::GlobalValue>(GetAddrOfGlobal(BaseDecl)); + Replacements[MangledName] = Aliasee; + return; + } + } - CodeGenFunction(*this).GenerateCode(GlobalDecl(ctor, ctorType), fn, fnInfo); + llvm::Function *Fn = codegenCXXConstructor(ctor, ctorType); - setFunctionDefinitionAttributes(ctor, fn); - SetLLVMFunctionAttributesForDefinition(ctor, fn); + if (CompleteCG == CtorDtorCodeGen::COMDAT) { + SmallString<256> Buffer; + llvm::raw_svector_ostream Out(Buffer); + getCXXABI().getMangleContext().mangleCXXCtorComdat(ctor, Out); + llvm::Comdat *C = TheModule.getOrInsertComdat(Out.str()); + Fn->setComdat(C); + } } llvm::GlobalValue * @@ -245,20 +348,53 @@ CodeGenModule::GetAddrOfCXXConstructor(const CXXConstructorDecl *ctor, DontDefer)); } +llvm::Function *CodeGenModule::codegenCXXDestructor(const CXXDestructorDecl *DD, + CXXDtorType DtorType) { + const CGFunctionInfo &FnInfo = getTypes().arrangeCXXDestructor(DD, DtorType); + auto *Fn = cast<llvm::Function>( + GetAddrOfCXXDestructor(DD, DtorType, &FnInfo, nullptr, true)); + if (!Fn->isDeclaration()) + return Fn; + GlobalDecl GD(DD, DtorType); + setFunctionLinkage(GD, Fn); + CodeGenFunction(*this).GenerateCode(GD, Fn, FnInfo); + setFunctionDefinitionAttributes(DD, Fn); + SetLLVMFunctionAttributesForDefinition(DD, Fn); + return Fn; +} + void CodeGenModule::EmitCXXDestructor(const CXXDestructorDecl *dtor, CXXDtorType 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 == Dtor_Complete || dtorType == Dtor_Base)) { - bool ProducedAlias = - !TryEmitDefinitionAsAlias(GlobalDecl(dtor, Dtor_Complete), - GlobalDecl(dtor, Dtor_Base), true); - if (ProducedAlias) { - if (dtorType == Dtor_Complete) - return; - if (dtor->isVirtual()) - getVTables().EmitThunks(GlobalDecl(dtor, Dtor_Complete)); + if (dtorType == Dtor_Deleting) { + codegenCXXDestructor(dtor, dtorType); + return; + } + + assert(dtorType == Dtor_Complete || dtorType == Dtor_Base); + + CtorDtorCodeGen CompleteCG = getCodegenToUse(*this, dtor); + if (CompleteCG == CtorDtorCodeGen::COMDAT) { + // Make sure both are emitted. + if (dtorType == Dtor_Complete) + GetAddrOfCXXDestructor(dtor, Dtor_Base); + else + GetAddrOfCXXDestructor(dtor, Dtor_Complete); + } + + if (dtorType == Dtor_Complete) { + GlobalDecl CompleteDecl(dtor, Dtor_Complete); + GlobalDecl BaseDecl(dtor, Dtor_Base); + + if (CompleteCG == CtorDtorCodeGen::Alias || + CompleteCG == CtorDtorCodeGen::COMDAT) { + emitConstructorDestructorAlias(CompleteDecl, BaseDecl); + return; + } + if (CompleteCG == CtorDtorCodeGen::RAUW) { + StringRef MangledName = getMangledName(CompleteDecl); + auto *Aliasee = cast<llvm::GlobalValue>(GetAddrOfGlobal(BaseDecl)); + Replacements[MangledName] = Aliasee; + return; } } @@ -269,17 +405,15 @@ void CodeGenModule::EmitCXXDestructor(const CXXDestructorDecl *dtor, if (dtorType == Dtor_Base && !TryEmitBaseDestructorAsAlias(dtor)) return; - const CGFunctionInfo &fnInfo = - getTypes().arrangeCXXDestructor(dtor, dtorType); - - auto *fn = cast<llvm::Function>( - GetAddrOfCXXDestructor(dtor, dtorType, &fnInfo, nullptr, true)); - setFunctionLinkage(GlobalDecl(dtor, dtorType), fn); + llvm::Function *Fn = codegenCXXDestructor(dtor, dtorType); - CodeGenFunction(*this).GenerateCode(GlobalDecl(dtor, dtorType), fn, fnInfo); - - setFunctionDefinitionAttributes(dtor, fn); - SetLLVMFunctionAttributesForDefinition(dtor, fn); + if (CompleteCG == CtorDtorCodeGen::COMDAT) { + SmallString<256> Buffer; + llvm::raw_svector_ostream Out(Buffer); + getCXXABI().getMangleContext().mangleCXXDtorComdat(dtor, Out); + llvm::Comdat *C = TheModule.getOrInsertComdat(Out.str()); + Fn->setComdat(C); + } } llvm::GlobalValue * diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index aaadbd4..a66388a 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -1066,7 +1066,7 @@ void CodeGenModule::EmitDeferred() { // Stop if we're out of both deferred v-tables and deferred declarations. if (DeferredDeclsToEmit.empty()) break; - DeferredGlobal &G = DeferredDeclsToEmit.back(); + const DeferredGlobal &G = DeferredDeclsToEmit.back(); GlobalDecl D = G.GD; llvm::GlobalValue *GV = G.GV; DeferredDeclsToEmit.pop_back(); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 985e536..0ab65f3 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -320,7 +320,7 @@ class CodeGenModule : public CodeGenTypeCache { /// referenced. These get code generated when the module is done. struct DeferredGlobal { DeferredGlobal(llvm::GlobalValue *GV, GlobalDecl GD) : GV(GV), GD(GD) {} - llvm::AssertingVH<llvm::GlobalValue> GV; + llvm::TrackingVH<llvm::GlobalValue> GV; GlobalDecl GD; }; std::vector<DeferredGlobal> DeferredDeclsToEmit; @@ -1094,15 +1094,21 @@ private: bool InEveryTU); bool TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D); + void emitConstructorDestructorAlias(GlobalDecl Alias, GlobalDecl Target); + void EmitNamespace(const NamespaceDecl *D); void EmitLinkageSpec(const LinkageSpecDecl *D); void CompleteDIClassType(const CXXMethodDecl* D); /// Emit a single constructor with the given type from a C++ constructor Decl. void EmitCXXConstructor(const CXXConstructorDecl *D, CXXCtorType Type); + llvm::Function *codegenCXXConstructor(const CXXConstructorDecl *CD, + CXXCtorType CtorType); /// Emit a single destructor with the given type from a C++ destructor Decl. void EmitCXXDestructor(const CXXDestructorDecl *D, CXXDtorType Type); + llvm::Function *codegenCXXDestructor(const CXXDestructorDecl *DD, + CXXDtorType DtorType); /// \brief Emit the function that initializes C++ thread_local variables. void EmitCXXThreadLocalInitFunc(); diff --git a/test/CodeGenCXX/ctor-dtor-alias.cpp b/test/CodeGenCXX/ctor-dtor-alias.cpp index 8be5494..76d2d60 100644 --- a/test/CodeGenCXX/ctor-dtor-alias.cpp +++ b/test/CodeGenCXX/ctor-dtor-alias.cpp @@ -8,16 +8,20 @@ // RUN: FileCheck --check-prefix=CHECK5 --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( +// CHECK1-NOT: comdat + +template <typename T> +struct foobar { foobar() {} + virtual ~foobar() {} }; template struct foobar<void>; 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