diff options
author | John McCall <rjmccall@apple.com> | 2013-01-25 22:31:03 +0000 |
---|---|---|
committer | John McCall <rjmccall@apple.com> | 2013-01-25 22:31:03 +0000 |
commit | d5617eeafc93209a26b9f88276c88cf997c3a0a7 (patch) | |
tree | 43c9295b24bd0ef8787299fa08681e041ef4697b | |
parent | b8b2c9da87e7d70a1679db026f40548b3192b705 (diff) |
The standard ARM C++ ABI dictates that inline functions are
never key functions. We did not implement that rule for the
iOS ABI, which was driven by what was implemented in gcc-4.2.
However, implement it now for other ARM-based platforms.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@173515 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/AST/ASTContext.h | 23 | ||||
-rw-r--r-- | include/clang/Basic/TargetCXXABI.h | 41 | ||||
-rw-r--r-- | lib/AST/ASTContext.cpp | 17 | ||||
-rw-r--r-- | lib/AST/RecordLayoutBuilder.cpp | 50 | ||||
-rw-r--r-- | lib/CodeGen/CGCXXABI.h | 5 | ||||
-rw-r--r-- | lib/CodeGen/CGRTTI.cpp | 10 | ||||
-rw-r--r-- | lib/CodeGen/CGVTables.cpp | 208 | ||||
-rw-r--r-- | lib/CodeGen/CGVTables.h | 18 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenModule.cpp | 93 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenModule.h | 13 | ||||
-rw-r--r-- | lib/CodeGen/ItaniumCXXABI.cpp | 7 | ||||
-rw-r--r-- | lib/CodeGen/MicrosoftCXXABI.cpp | 7 | ||||
-rw-r--r-- | lib/Sema/Sema.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 26 | ||||
-rw-r--r-- | lib/Sema/SemaDeclCXX.cpp | 2 | ||||
-rw-r--r-- | lib/Serialization/ASTWriterDecl.cpp | 6 | ||||
-rw-r--r-- | test/CodeGenCXX/vtable-available-externally.cpp | 9 | ||||
-rw-r--r-- | test/CodeGenCXX/vtable-key-function-arm.cpp | 307 | ||||
-rw-r--r-- | test/CodeGenCXX/vtable-key-function-ios.cpp | 189 |
19 files changed, 853 insertions, 180 deletions
diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 05f818299b..bf0487198d 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1563,14 +1563,27 @@ public: const ASTRecordLayout & getASTObjCImplementationLayout(const ObjCImplementationDecl *D) const; - /// \brief Get the key function for the given record decl, or NULL if there - /// isn't one. + /// \brief Get our current best idea for the key function of the + /// given record decl, or NULL if there isn't one. /// /// The key function is, according to the Itanium C++ ABI section 5.2.3: + /// ...the first non-pure virtual function that is not inline at the + /// point of class definition. /// - /// ...the first non-pure virtual function that is not inline at the point - /// of class definition. - const CXXMethodDecl *getKeyFunction(const CXXRecordDecl *RD); + /// Other ABIs use the same idea. However, the ARM C++ ABI ignores + /// virtual functions that are defined 'inline', which means that + /// the result of this computation can change. + const CXXMethodDecl *getCurrentKeyFunction(const CXXRecordDecl *RD); + + /// \brief Observe that the given method cannot be a key function. + /// Checks the key-function cache for the method's class and clears it + /// if matches the given declaration. + /// + /// This is used in ABIs where out-of-line definitions marked + /// inline are not considered to be key functions. + /// + /// \param method should be the declaration from the class definition + void setNonKeyFunction(const CXXMethodDecl *method); /// Get the offset of a FieldDecl or IndirectFieldDecl, in bits. uint64_t getFieldOffset(const ValueDecl *FD) const; diff --git a/include/clang/Basic/TargetCXXABI.h b/include/clang/Basic/TargetCXXABI.h index 2a8f5d0df5..1a8e62c16d 100644 --- a/include/clang/Basic/TargetCXXABI.h +++ b/include/clang/Basic/TargetCXXABI.h @@ -142,6 +142,47 @@ public: return isItaniumFamily(); } + /// \brief Can an out-of-line inline function serve as a key function? + /// + /// This flag is only useful in ABIs where type data (for example, + /// v-tables and type_info objects) are emitted only after processing + /// the definition of a special "key" virtual function. (This is safe + /// because the ODR requires that every virtual function be defined + /// somewhere in a program.) This usually permits such data to be + /// emitted in only a single object file, as opposed to redundantly + /// in every object file that requires it. + /// + /// One simple and common definition of "key function" is the first + /// virtual function in the class definition which is not defined there. + /// This rule works very well when that function has a non-inline + /// definition in some non-header file. Unfortunately, when that + /// function is defined inline, this rule requires the type data + /// to be emitted weakly, as if there were no key function. + /// + /// The ARM ABI observes that the ODR provides an additional guarantee: + /// a virtual function is always ODR-used, so if it is defined inline, + /// that definition must appear in every translation unit that defines + /// the class. Therefore, there is no reason to allow such functions + /// to serve as key functions. + /// + /// Because this changes the rules for emitting type data, + /// it can cause type data to be emitted with both weak and strong + /// linkage, which is not allowed on all platforms. Therefore, + /// exploiting this observation requires an ABI break and cannot be + /// done on a generic Itanium platform. + bool canKeyFunctionBeInline() const { + switch (getKind()) { + case GenericARM: + return false; + + case GenericItanium: + case iOS: // old iOS compilers did not follow this rule + case Microsoft: + return true; + } + llvm_unreachable("bad ABI kind"); + } + /// Try to parse an ABI name, returning false on error. bool tryParse(llvm::StringRef name); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index b5d60f589b..0b771745fd 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -7559,13 +7559,16 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { if (FD->hasAttr<ConstructorAttr>() || FD->hasAttr<DestructorAttr>()) return true; - // The key function for a class is required. - if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { - const CXXRecordDecl *RD = MD->getParent(); - if (MD->isOutOfLine() && RD->isDynamicClass()) { - const CXXMethodDecl *KeyFunc = getKeyFunction(RD); - if (KeyFunc && KeyFunc->getCanonicalDecl() == MD->getCanonicalDecl()) - return true; + // The key function for a class is required. This rule only comes + // into play when inline functions can be key functions, though. + if (getTargetInfo().getCXXABI().canKeyFunctionBeInline()) { + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { + const CXXRecordDecl *RD = MD->getParent(); + if (MD->isOutOfLine() && RD->isDynamicClass()) { + const CXXMethodDecl *KeyFunc = getCurrentKeyFunction(RD); + if (KeyFunc && KeyFunc->getCanonicalDecl() == MD->getCanonicalDecl()) + return true; + } } } diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index 08f6d5c54b..72851dfa1e 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -795,8 +795,6 @@ protected: RecordLayoutBuilder(const RecordLayoutBuilder &) LLVM_DELETED_FUNCTION; void operator=(const RecordLayoutBuilder &) LLVM_DELETED_FUNCTION; -public: - static const CXXMethodDecl *ComputeKeyFunction(const CXXRecordDecl *RD); }; } // end anonymous namespace @@ -2347,8 +2345,8 @@ void RecordLayoutBuilder::CheckFieldPadding(uint64_t Offset, << D->getIdentifier(); } -const CXXMethodDecl * -RecordLayoutBuilder::ComputeKeyFunction(const CXXRecordDecl *RD) { +static const CXXMethodDecl *computeKeyFunction(ASTContext &Context, + const CXXRecordDecl *RD) { // If a class isn't polymorphic it doesn't have a key function. if (!RD->isPolymorphic()) return 0; @@ -2366,6 +2364,9 @@ RecordLayoutBuilder::ComputeKeyFunction(const CXXRecordDecl *RD) { TSK == TSK_ExplicitInstantiationDefinition) return 0; + bool allowInlineFunctions = + Context.getTargetInfo().getCXXABI().canKeyFunctionBeInline(); + for (CXXRecordDecl::method_iterator I = RD->method_begin(), E = RD->method_end(); I != E; ++I) { const CXXMethodDecl *MD = *I; @@ -2391,6 +2392,13 @@ RecordLayoutBuilder::ComputeKeyFunction(const CXXRecordDecl *RD) { if (!MD->isUserProvided()) continue; + // In certain ABIs, ignore functions with out-of-line inline definitions. + if (!allowInlineFunctions) { + const FunctionDecl *Def; + if (MD->hasBody(Def) && Def->isInlineSpecified()) + continue; + } + // We found it. return MD; } @@ -2496,15 +2504,37 @@ ASTContext::getASTRecordLayout(const RecordDecl *D) const { return *NewEntry; } -const CXXMethodDecl *ASTContext::getKeyFunction(const CXXRecordDecl *RD) { +const CXXMethodDecl *ASTContext::getCurrentKeyFunction(const CXXRecordDecl *RD) { + assert(RD->getDefinition() && "Cannot get key function for forward decl!"); RD = cast<CXXRecordDecl>(RD->getDefinition()); - assert(RD && "Cannot get key function for forward declarations!"); - const CXXMethodDecl *&Entry = KeyFunctions[RD]; - if (!Entry) - Entry = RecordLayoutBuilder::ComputeKeyFunction(RD); + const CXXMethodDecl *&entry = KeyFunctions[RD]; + if (!entry) { + entry = computeKeyFunction(*this, RD); + } + + return entry; +} + +void ASTContext::setNonKeyFunction(const CXXMethodDecl *method) { + assert(method == method->getFirstDeclaration() && + "not working with method declaration from class definition"); - return Entry; + // Look up the cache entry. Since we're working with the first + // declaration, its parent must be the class definition, which is + // the correct key for the KeyFunctions hash. + llvm::DenseMap<const CXXRecordDecl*, const CXXMethodDecl*>::iterator + i = KeyFunctions.find(method->getParent()); + + // If it's not cached, there's nothing to do. + if (i == KeyFunctions.end()) return; + + // If it is cached, check whether it's the target method, and if so, + // remove it from the cache. + if (i->second == method) { + // FIXME: remember that we did this for module / chained PCH state? + KeyFunctions.erase(i); + } } static uint64_t getFieldOffset(const ASTContext &C, const FieldDecl *FD) { diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index a3e49d8035..60e1020b99 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -294,11 +294,6 @@ public: /// \param addr - a pointer to pass to the destructor function. virtual void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor, llvm::Constant *addr); - - /***************************** Virtual Tables *******************************/ - - /// Generates and emits the virtual tables for a class. - virtual void EmitVTables(const CXXRecordDecl *Class) = 0; }; /// Creates an instance of a C++ ABI class. diff --git a/lib/CodeGen/CGRTTI.cpp b/lib/CodeGen/CGRTTI.cpp index 3d65892b2e..366ce29fc0 100644 --- a/lib/CodeGen/CGRTTI.cpp +++ b/lib/CodeGen/CGRTTI.cpp @@ -251,10 +251,12 @@ static bool IsStandardLibraryRTTIDescriptor(QualType Ty) { /// the given type exists somewhere else, and that we should not emit the type /// information in this translation unit. Assumes that it is not a /// standard-library type. -static bool ShouldUseExternalRTTIDescriptor(CodeGenModule &CGM, QualType Ty) { +static bool ShouldUseExternalRTTIDescriptor(CodeGenModule &CGM, + QualType Ty) { ASTContext &Context = CGM.getContext(); - // If RTTI is disabled, don't consider key functions. + // If RTTI is disabled, assume it might be disabled in the + // translation unit that defines any potential key function, too. if (!Context.getLangOpts().RTTI) return false; if (const RecordType *RecordTy = dyn_cast<RecordType>(Ty)) { @@ -265,7 +267,9 @@ static bool ShouldUseExternalRTTIDescriptor(CodeGenModule &CGM, QualType Ty) { if (!RD->isDynamicClass()) return false; - return !CGM.getVTables().ShouldEmitVTableInThisTU(RD); + // FIXME: this may need to be reconsidered if the key function + // changes. + return CGM.getVTables().isVTableExternal(RD); } return false; diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 64c7939588..6d402197e0 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -31,33 +31,6 @@ using namespace CodeGen; CodeGenVTables::CodeGenVTables(CodeGenModule &CGM) : CGM(CGM), VTContext(CGM.getContext()) { } -bool CodeGenVTables::ShouldEmitVTableInThisTU(const CXXRecordDecl *RD) { - assert(RD->isDynamicClass() && "Non dynamic classes have no VTable."); - - TemplateSpecializationKind TSK = RD->getTemplateSpecializationKind(); - if (TSK == TSK_ExplicitInstantiationDeclaration) - return false; - - const CXXMethodDecl *KeyFunction = CGM.getContext().getKeyFunction(RD); - if (!KeyFunction) - return true; - - // Itanium C++ ABI, 5.2.6 Instantiated Templates: - // An instantiation of a class template requires: - // - In the object where instantiated, the virtual table... - if (TSK == TSK_ImplicitInstantiation || - TSK == TSK_ExplicitInstantiationDefinition) - return true; - - // If we're building with optimization, we always emit VTables since that - // allows for virtual function calls to be devirtualized. - // (We don't want to do this in -fapple-kext mode however). - if (CGM.getCodeGenOpts().OptimizationLevel && !CGM.getLangOpts().AppleKext) - return true; - - return KeyFunction->hasBody(); -} - llvm::Constant *CodeGenModule::GetAddrOfThunk(GlobalDecl GD, const ThunkInfo &Thunk) { const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); @@ -645,9 +618,8 @@ llvm::GlobalVariable *CodeGenVTables::GetAddrOfVTable(const CXXRecordDecl *RD) { if (VTable) return VTable; - // We may need to generate a definition for this vtable. - if (ShouldEmitVTableInThisTU(RD)) - CGM.DeferredVTables.push_back(RD); + // Queue up this v-table for possible deferred emission. + CGM.addDeferredVTable(RD); SmallString<256> OutName; llvm::raw_svector_ostream Out(OutName); @@ -734,13 +706,108 @@ CodeGenVTables::GenerateConstructionVTable(const CXXRecordDecl *RD, return VTable; } +/// Compute the required linkage of the v-table for the given class. +/// +/// Note that we only call this at the end of the translation unit. +llvm::GlobalVariable::LinkageTypes +CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) { + if (RD->getLinkage() != ExternalLinkage) + return llvm::GlobalVariable::InternalLinkage; + + // We're at the end of the translation unit, so the current key + // function is fully correct. + if (const CXXMethodDecl *keyFunction = Context.getCurrentKeyFunction(RD)) { + // If this class has a key function, use that to determine the + // linkage of the vtable. + const FunctionDecl *def = 0; + if (keyFunction->hasBody(def)) + keyFunction = cast<CXXMethodDecl>(def); + + switch (keyFunction->getTemplateSpecializationKind()) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + // When compiling with optimizations turned on, we emit all vtables, + // even if the key function is not defined in the current translation + // unit. If this is the case, use available_externally linkage. + if (!def && CodeGenOpts.OptimizationLevel) + return llvm::GlobalVariable::AvailableExternallyLinkage; + + if (keyFunction->isInlined()) + return !Context.getLangOpts().AppleKext ? + llvm::GlobalVariable::LinkOnceODRLinkage : + llvm::Function::InternalLinkage; + + return llvm::GlobalVariable::ExternalLinkage; + + case TSK_ImplicitInstantiation: + return !Context.getLangOpts().AppleKext ? + llvm::GlobalVariable::LinkOnceODRLinkage : + llvm::Function::InternalLinkage; + + case TSK_ExplicitInstantiationDefinition: + return !Context.getLangOpts().AppleKext ? + llvm::GlobalVariable::WeakODRLinkage : + llvm::Function::InternalLinkage; + + case TSK_ExplicitInstantiationDeclaration: + // FIXME: Use available_externally linkage. However, this currently + // breaks LLVM's build due to undefined symbols. + // return llvm::GlobalVariable::AvailableExternallyLinkage; + return !Context.getLangOpts().AppleKext ? + llvm::GlobalVariable::LinkOnceODRLinkage : + llvm::Function::InternalLinkage; + } + } + + // -fapple-kext mode does not support weak linkage, so we must use + // internal linkage. + if (Context.getLangOpts().AppleKext) + return llvm::Function::InternalLinkage; + + switch (RD->getTemplateSpecializationKind()) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + case TSK_ImplicitInstantiation: + return llvm::GlobalVariable::LinkOnceODRLinkage; + + case TSK_ExplicitInstantiationDeclaration: + // FIXME: Use available_externally linkage. However, this currently + // breaks LLVM's build due to undefined symbols. + // return llvm::GlobalVariable::AvailableExternallyLinkage; + return llvm::GlobalVariable::LinkOnceODRLinkage; + + case TSK_ExplicitInstantiationDefinition: + return llvm::GlobalVariable::WeakODRLinkage; + } + + llvm_unreachable("Invalid TemplateSpecializationKind!"); +} + +/// This is a callback from Sema to tell us that it believes that a +/// particular v-table is required to be emitted in this translation +/// unit. +/// +/// The reason we don't simply trust this callback is because Sema +/// will happily report that something is used even when it's used +/// only in code that we don't actually have to emit. +/// +/// \param isRequired - if true, the v-table is mandatory, e.g. +/// because the translation unit defines the key function +void CodeGenModule::EmitVTable(CXXRecordDecl *theClass, bool isRequired) { + if (!isRequired) return; + + VTables.GenerateClassData(theClass); +} + void -CodeGenVTables::GenerateClassData(llvm::GlobalVariable::LinkageTypes Linkage, - const CXXRecordDecl *RD) { +CodeGenVTables::GenerateClassData(const CXXRecordDecl *RD) { + // First off, check whether we've already emitted the v-table and + // associated stuff. llvm::GlobalVariable *VTable = GetAddrOfVTable(RD); if (VTable->hasInitializer()) return; + llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD); EmitVTableDefinition(VTable, Linkage, RD); if (RD->getNumVBases()) { @@ -760,3 +827,80 @@ CodeGenVTables::GenerateClassData(llvm::GlobalVariable::LinkageTypes Linkage, DC->getParent()->isTranslationUnit()) CGM.EmitFundamentalRTTIDescriptors(); } + +/// At this point in the translation unit, does it appear that can we +/// rely on the vtable being defined elsewhere in the program? +/// +/// The response is really only definitive when called at the end of +/// the translation unit. +/// +/// The only semantic restriction here is that the object file should +/// not contain a v-table definition when that v-table is defined +/// strongly elsewhere. Otherwise, we'd just like to avoid emitting +/// v-tables when unnecessary. +bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) { + assert(RD->isDynamicClass() && "Non dynamic classes have no VTable."); + + // If we have an explicit instantiation declaration (and not a + // definition), the v-table is defined elsewhere. + TemplateSpecializationKind TSK = RD->getTemplateSpecializationKind(); + if (TSK == TSK_ExplicitInstantiationDeclaration) + return true; + + // Otherwise, if the class is an instantiated template, the + // v-table must be defined here. + if (TSK == TSK_ImplicitInstantiation || + TSK == TSK_ExplicitInstantiationDefinition) + return false; + + // Otherwise, if the class doesn't have a key function (possibly + // anymore), the v-table must be defined here. + const CXXMethodDecl *keyFunction = CGM.getContext().getCurrentKeyFunction(RD); + if (!keyFunction) + return false; + + // Otherwise, if we don't have a definition of the key function, the + // v-table must be defined somewhere else. + return !keyFunction->hasBody(); +} + +/// Given that we're currently at the end of the translation unit, and +/// we've emitted a reference to the v-table for this class, should +/// we define that v-table? +static bool shouldEmitVTableAtEndOfTranslationUnit(CodeGenModule &CGM, + const CXXRecordDecl *RD) { + // If we're building with optimization, we always emit v-tables + // since that allows for virtual function calls to be devirtualized. + // If the v-table is defined strongly elsewhere, this definition + // will be emitted available_externally. + // + // However, we don't want to do this in -fapple-kext mode, because + // kext mode does not permit devirtualization. + if (CGM.getCodeGenOpts().OptimizationLevel && !CGM.getLangOpts().AppleKext) + return true; + + return !CGM.getVTables().isVTableExternal(RD); +} + +/// Given that at some point we emitted a reference to one or more +/// v-tables, and that we are now at the end of the translation unit, +/// decide whether we should emit them. +void CodeGenModule::EmitDeferredVTables() { +#ifndef NDEBUG + // Remember the size of DeferredVTables, because we're going to assume + // that this entire operation doesn't modify it. + size_t savedSize = DeferredVTables.size(); +#endif + + typedef std::vector<const CXXRecordDecl *>::const_iterator const_iterator; + for (const_iterator i = DeferredVTables.begin(), + e = DeferredVTables.end(); i != e; ++i) { + const CXXRecordDecl *RD = *i; + if (shouldEmitVTableAtEndOfTranslationUnit(*this, RD)) + VTables.GenerateClassData(RD); + } + + assert(savedSize == DeferredVTables.size() && + "deferred extra v-tables during v-table emission?"); + DeferredVTables.clear(); +} diff --git a/lib/CodeGen/CGVTables.h b/lib/CodeGen/CGVTables.h index 5f3d814c0a..bd3bdb1358 100644 --- a/lib/CodeGen/CGVTables.h +++ b/lib/CodeGen/CGVTables.h @@ -77,10 +77,6 @@ public: VTableContext &getVTableContext() { return VTContext; } - /// \brief True if the VTable of this record must be emitted in the - /// translation unit. - bool ShouldEmitVTableInThisTU(const CXXRecordDecl *RD); - /// needsVTTParameter - Return whether the given global decl needs a VTT /// parameter, which it does if it's a base constructor or destructor with /// virtual bases. @@ -127,13 +123,13 @@ public: /// EmitThunks - Emit the associated thunks for the given global decl. void EmitThunks(GlobalDecl GD); - /// GenerateClassData - Generate all the class data required to be generated - /// upon definition of a KeyFunction. This includes the vtable, the - /// rtti data structure and the VTT. - /// - /// \param Linkage - The desired linkage of the vtable, the RTTI and the VTT. - void GenerateClassData(llvm::GlobalVariable::LinkageTypes Linkage, - const CXXRecordDecl *RD); + /// GenerateClassData - Generate all the class data required to be + /// generated upon definition of a KeyFunction. This includes the + /// vtable, the RTTI data structure (if RTTI is enabled) and the VTT + /// (if the class has virtual bases). + void GenerateClassData(const CXXRecordDecl *RD); + + bool isVTableExternal(const CXXRecordDecl *RD); }; } // end namespace CodeGen diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 2da9dba5b7..9d7854975e 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -371,7 +371,9 @@ void CodeGenModule::setTypeVisibility(llvm::GlobalValue *GV, // that don't have the key function's definition. But ignore // this if we're emitting RTTI under -fno-rtti. if (!(TVK != TVK_ForRTTI) || LangOpts.RTTI) { - if (Context.getKeyFunction(RD)) + // FIXME: what should we do if we "lose" the key function during + // the emission of the file? + if (Context.getCurrentKeyFunction(RD)) return; } @@ -836,14 +838,19 @@ void CodeGenModule::EmitDeferred() { // previously unused static decl may become used during the generation of code // for a static function, iterate until no changes are made. - while (!DeferredDeclsToEmit.empty() || !DeferredVTables.empty()) { + while (true) { if (!DeferredVTables.empty()) { - const CXXRecordDecl *RD = DeferredVTables.back(); - DeferredVTables.pop_back(); - getCXXABI().EmitVTables(RD); - continue; + EmitDeferredVTables(); + + // Emitting a v-table doesn't directly cause more v-tables to + // become deferred, although it can cause functions to be + // emitted that then need those v-tables. + assert(DeferredVTables.empty()); } + // Stop if we're out of both deferred v-tables and deferred declarations. + if (DeferredDeclsToEmit.empty()) break; + GlobalDecl D = DeferredDeclsToEmit.back(); DeferredDeclsToEmit.pop_back(); @@ -1526,80 +1533,6 @@ void CodeGenModule::EmitTentativeDefinition(const VarDecl *D) { EmitGlobalVarDefinition(D); } -void CodeGenModule::EmitVTable(CXXRecordDecl *Class, bool DefinitionRequired) { - if (DefinitionRequired) - getCXXABI().EmitVTables(Class); -} - -llvm::GlobalVariable::LinkageTypes -CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) { - if (RD->getLinkage() != ExternalLinkage) - return llvm::GlobalVariable::InternalLinkage; - - if (const CXXMethodDecl *KeyFunction - = RD->getASTContext().getKeyFunction(RD)) { - // If this class has a key function, use that to determine the linkage of - // the vtable. - const FunctionDecl *Def = 0; - if (KeyFunction->hasBody(Def)) - KeyFunction = cast<CXXMethodDecl>(Def); - - switch (KeyFunction->getTemplateSpecializationKind()) { - case TSK_Undeclared: - case TSK_ExplicitSpecialization: - // When compiling with optimizations turned on, we emit all vtables, - // even if the key function is not defined in the current translation - // unit. If this is the case, use available_externally linkage. - if (!Def && CodeGenOpts.OptimizationLevel) - return llvm::GlobalVariable::AvailableExternallyLinkage; - - if (KeyFunction->isInlined()) - return !Context.getLangOpts().AppleKext ? - llvm::GlobalVariable::LinkOnceODRLinkage : - llvm::Function::InternalLinkage; - - return llvm::GlobalVariable::ExternalLinkage; - - case TSK_ImplicitInstantiation: - return !Context.getLangOpts().AppleKext ? - llvm::GlobalVariable::LinkOnceODRLinkage : - llvm::Function::InternalLinkage; - - case TSK_ExplicitInstantiationDefinition: - return !Context.getLangOpts().AppleKext ? - llvm::GlobalVariable::WeakODRLinkage : - llvm::Function::InternalLinkage; - - case TSK_ExplicitInstantiationDeclaration: - // FIXME: Use available_externally linkage. However, this currently - // breaks LLVM's build due to undefined symbols. - // return llvm::GlobalVariable::AvailableExternallyLinkage; - return !Context.getLangOpts().AppleKext ? - llvm::GlobalVariable::LinkOnceODRLinkage : - llvm::Function::InternalLinkage; - } - } - - if (Context.getLangOpts().AppleKext) - return llvm::Function::InternalLinkage; - - switch (RD->getTemplateSpecializationKind()) { - case TSK_Undeclared: - case TSK_ExplicitSpecialization: - case TSK_ImplicitInstantiation: - // FIXME: Use available_externally linkage. However, this currently - // breaks LLVM's build due to undefined symbols. - // return llvm::GlobalVariable::AvailableExternallyLinkage; - case TSK_ExplicitInstantiationDeclaration: - return llvm::GlobalVariable::LinkOnceODRLinkage; - - case TSK_ExplicitInstantiationDefinition: - return llvm::GlobalVariable::WeakODRLinkage; - } - - llvm_unreachable("Invalid TemplateSpecializationKind!"); -} - CharUnits CodeGenModule::GetTargetTypeStoreSize(llvm::Type *Ty) const { return Context.toCharUnitsFromBits( TheDataLayout.getTypeStoreSizeInBits(Ty)); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 0f011205a7..75c5e93634 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -258,6 +258,9 @@ class CodeGenModule : public CodeGenTypeCache { /// is done. std::vector<GlobalDecl> DeferredDeclsToEmit; + /// DeferredVTables - A queue of (optional) vtables to consider emitting. + std::vector<const CXXRecordDecl*> DeferredVTables; + /// LLVMUsed - List of global values which are required to be /// present in the object file; bitcast to i8*. This is used for /// forcing visibility of symbols which may otherwise be optimized @@ -865,8 +868,6 @@ public: GetLLVMLinkageVarDefinition(const VarDecl *D, llvm::GlobalVariable *GV); - std::vector<const CXXRecordDecl*> DeferredVTables; - /// Emit all the global annotations. void EmitGlobalAnnotations(); @@ -900,6 +901,10 @@ public: const SanitizerOptions &getSanOpts() const { return SanOpts; } + void addDeferredVTable(const CXXRecordDecl *RD) { + DeferredVTables.push_back(RD); + } + private: llvm::GlobalValue *GetGlobalValue(StringRef Ref); @@ -1002,6 +1007,10 @@ private: /// was deferred. void EmitDeferred(); + /// EmitDeferredVTables - Emit any vtables which we deferred and + /// still have a use for. + void EmitDeferredVTables(); + /// EmitLLVMUsed - Emit the llvm.used metadata used to force /// references to global which may otherwise be optimized out. void EmitLLVMUsed(); diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index b761055043..9fce392352 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -129,8 +129,6 @@ public: llvm::GlobalVariable *DeclPtr, bool PerformInit); void registerGlobalDtor(CodeGenFunction &CGF, llvm::Constant *dtor, llvm::Constant *addr); - - void EmitVTables(const CXXRecordDecl *Class); }; class ARMCXXABI : public ItaniumCXXABI { @@ -1180,8 +1178,3 @@ void ItaniumCXXABI::registerGlobalDtor(CodeGenFunction &CGF, CGF.registerGlobalDtorWithAtExit(dtor, addr); } - -/// Generate and emit virtual tables for the given class. -void ItaniumCXXABI::EmitVTables(const CXXRecordDecl *Class) { - CGM.getVTables().GenerateClassData(CGM.getVTableLinkage(Class), Class); -} diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 8e4929294d..477720bd25 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -60,9 +60,6 @@ public: llvm::GlobalVariable *DeclPtr, bool PerformInit); - void EmitVTables(const CXXRecordDecl *Class); - - // ==== Notes on array cookies ========= // // MSVC seems to only use cookies when the class has a destructor; a @@ -206,10 +203,6 @@ void MicrosoftCXXABI::EmitGuardedInit(CodeGenFunction &CGF, const VarDecl &D, CGF.EmitCXXGlobalVarDeclInit(D, DeclPtr, PerformInit); } -void MicrosoftCXXABI::EmitVTables(const CXXRecordDecl *Class) { - CGM.getVTables().GenerateClassData(CGM.getVTableLinkage(Class), Class); -} - CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) { return new MicrosoftCXXABI(CGM); } diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 19da55cd25..c03d41eb04 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -543,7 +543,7 @@ void Sema::ActOnEndOfTranslationUnit() { I != E; ++I) { assert(!(*I)->isDependentType() && "Should not see dependent types here!"); - if (const CXXMethodDecl *KeyFunction = Context.getKeyFunction(*I)) { + if (const CXXMethodDecl *KeyFunction = Context.getCurrentKeyFunction(*I)) { const FunctionDecl *Definition = 0; if (KeyFunction->hasBody(Definition)) MarkVTableUsed(Definition->getLocation(), *I, true); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9efcb52d14..d584ed8571 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -6362,9 +6362,31 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, } } else { - if (isa<CXXMethodDecl>(NewFD)) // Set access for out-of-line definitions - NewFD->setAccess(OldDecl->getAccess()); + // This needs to happen first so that 'inline' propagates. NewFD->setPreviousDeclaration(cast<FunctionDecl>(OldDecl)); + + if (isa<CXXMethodDecl>(NewFD)) { + // A valid redeclaration of a C++ method must be out-of-line, + // but (unfortunately) it's not necessarily a definition + // because of templates, which means that the previous + // declaration is not necessarily from the class definition. + + // For just setting the access, that doesn't matter. + CXXMethodDecl *oldMethod = cast<CXXMethodDecl>(OldDecl); + NewFD->setAccess(oldMethod->getAccess()); + + // Update the key-function state if necessary for this ABI. + if (NewFD->isInlined() && + !Context.getTargetInfo().getCXXABI().canKeyFunctionBeInline()) { + // setNonKeyFunction needs to work with the original + // declaration from the class definition, and isVirtual() is + // just faster in that case, so map back to that now. + oldMethod = cast<CXXMethodDecl>(oldMethod->getFirstDeclaration()); + if (oldMethod->isVirtual()) { + Context.setNonKeyFunction(oldMethod); + } + } + } } } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 515fd91a6b..448d083aaf 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -11217,7 +11217,7 @@ bool Sema::DefineUsedVTables() { // If this class has a key function, but that key function is // defined in another translation unit, we don't need to emit the // vtable even though we're using it. - const CXXMethodDecl *KeyFunction = Context.getKeyFunction(Class); + const CXXMethodDecl *KeyFunction = Context.getCurrentKeyFunction(Class); if (KeyFunction && !KeyFunction->hasBody()) { switch (KeyFunction->getTemplateSpecializationKind()) { case TSK_Undeclared: diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index dca93f7fda..cd4eb310f0 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -942,10 +942,10 @@ void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) { Record.push_back(CXXRecNotTemplate); } - // Store the key function to avoid deserializing every method so we can - // compute it. + // Store (what we currently believe to be) the key function to avoid + // deserializing every method so we can compute it. if (D->IsCompleteDefinition) - Writer.AddDeclRef(Context.getKeyFunction(D), Record); + Writer.AddDeclRef(Context.getCurrentKeyFunction(D), Record); Code = serialization::DECL_CXX_RECORD; } diff --git a/test/CodeGenCXX/vtable-available-externally.cpp b/test/CodeGenCXX/vtable-available-externally.cpp index 23baac968c..693b36abe5 100644 --- a/test/CodeGenCXX/vtable-available-externally.cpp +++ b/test/CodeGenCXX/vtable-available-externally.cpp @@ -6,13 +6,14 @@ #include <typeinfo> -// Test1::A's key function (f) is not defined in this translation unit, but in -// order to devirtualize calls, we emit the class related data with +// Test1::A's key function (f) is not defined in this translation +// unit, but in order to devirtualize calls, we emit the v-table with // available_externally linkage. +// +// There's no real reason to do this to the RTTI, though. // CHECK-TEST1: @_ZTVN5Test11AE = available_externally -// CHECK-TEST1: @_ZTSN5Test11AE = available_externally -// CHECK-TEST1: @_ZTIN5Test11AE = available_externally +// CHECK-TEST1: @_ZTIN5Test11AE = external constant i8* namespace Test1 { struct A { diff --git a/test/CodeGenCXX/vtable-key-function-arm.cpp b/test/CodeGenCXX/vtable-key-function-arm.cpp new file mode 100644 index 0000000000..08efe8a141 --- /dev/null +++ b/test/CodeGenCXX/vtable-key-function-arm.cpp @@ -0,0 +1,307 @@ +// RUN: %clang_cc1 %s -triple=armv7-unknown-unknown -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple=armv7-unknown-unknown -emit-llvm -o - | FileCheck -check-prefix=CHECK-LATE %s + +// The 'a' variants ask for the v-table first. +// The 'b' variants ask for the v-table second. +// The 'c' variants ask for the v-table third. +// We do a separate CHECK-LATE pass because the RTTI defintion gets +// changed after the fact, which causes reordering of the globals. + +// These are not separated into namespaces because the way that Sema +// currently reports namespaces to IR-generation (i.e., en masse for +// the entire namespace at once) subverts the ordering that we're +// trying to test. + +namespace std { class type_info; } +extern void use(const std::type_info &rtti); + +/*** Test0a ******************************************************************/ + +struct Test0a { + Test0a(); + virtual inline void foo(); + virtual void bar(); +}; + +// V-table should be defined externally. +Test0a::Test0a() { use(typeid(Test0a)); } +// CHECK: @_ZTV6Test0a = external unnamed_addr constant +// CHECK: @_ZTI6Test0a = external constant + +// This is still not a key function. +void Test0a::foo() {} + +/*** Test0b ******************************************************************/ + +struct Test0b { + Test0b(); + virtual inline void foo(); + virtual void bar(); +}; + +// This is still not a key function. +void Test0b::foo() {} + +// V-table should be defined externally. +Test0b::Test0b() { use(typeid(Test0b)); } +// CHECK: @_ZTV6Test0b = external unnamed_addr constant +// CHECK: @_ZTI6Test0b = external constant + +/*** Test1a ******************************************************************/ + +struct Test1a { + Test1a(); + virtual void foo(); + virtual void bar(); +}; + +// V-table should be defined externally. +Test1a::Test1a() { use(typeid(Test1a)); } +// CHECK: @_ZTV6Test1a = external unnamed_addr constant +// CHECK: @_ZTI6Test1a = external constant + +// 'bar' becomes the key function when 'foo' is defined inline. +inline void Test1a::foo() {} + +/*** Test1b ******************************************************************/ + +struct Test1b { + Test1b(); + virtual void foo(); + virtual void bar(); +}; + +// 'bar' becomes the key function when 'foo' is defined inline. +inline void Test1b::foo() {} + +// V-table should be defined externally. +Test1b::Test1b() { use(typeid(Test1b)); } +// CHECK: @_ZTV6Test1b = external unnamed_addr constant +// CHECK: @_ZTI6Test1b = external constant + +/*** Test2a ******************************************************************/ + +struct Test2a { + Test2a(); + virtual void foo(); + virtual void bar(); +}; + +// V-table should be defined with strong linkage. +Test2a::Test2a() { use(typeid(Test2a)); } +// CHECK: @_ZTV6Test2a = unnamed_addr constant +// CHECK-LATE: @_ZTS6Test2a = constant +// CHECK-LATE: @_ZTI6Test2a = unnamed_addr constant + +// 'bar' becomes the key function when 'foo' is defined inline. +void Test2a::bar() {} +inline void Test2a::foo() {} + +/*** Test2b ******************************************************************/ + +struct Test2b { + Test2b(); + virtual void foo(); + virtual void bar(); +}; + +// 'bar' becomes the key function when 'foo' is defined inline. +void Test2b::bar() {} + +// V-table should be defined with strong linkage. +Test2b::Test2b() { use(typeid(Test2b)); } +// CHECK: @_ZTV6Test2b = unnamed_addr constant +// CHECK-LATE: @_ZTS6Test2b = constant +// CHECK-LATE: @_ZTI6Test2b = unnamed_addr constant + +inline void Test2b::foo() {} + +/*** Test2c ******************************************************************/ + +struct Test2c { + Test2c(); + virtual void foo(); + virtual void bar(); +}; + +// 'bar' becomes the key function when 'foo' is defined inline. +void Test2c::bar() {} +inline void Test2c::foo() {} + +// V-table should be defined with strong linkage. +Test2c::Test2c() { use(typeid(Test2c)); } +// CHECK: @_ZTV6Test2c = unnamed_addr constant +// CHECK: @_ZTS6Test2c = constant +// CHECK: @_ZTI6Test2c = unnamed_addr constant + +/*** Test3a ******************************************************************/ + +struct Test3a { + Test3a(); + virtual void foo(); + virtual void bar(); +}; + +// V-table should be defined with weak linkage. +Test3a::Test3a() { use(typeid(Test3a)); } +// CHECK: @_ZTV6Test3a = linkonce_odr unnamed_addr constant +// CHECK-LATE: @_ZTS6Test3a = linkonce_odr constant +// CHECK-LATE: @_ZTI6Test3a = linkonce_odr unnamed_addr constant + +// There ceases to be a key function after these declarations. +inline void Test3a::bar() {} +inline void Test3a::foo() {} + +/*** Test3b ******************************************************************/ + +struct Test3b { + Test3b(); + virtual void foo(); + virtual void bar(); +}; + +// There ceases to be a key function after these declarations. +inline void Test3b::bar() {} + +// V-table should be defined with weak linkage. +Test3b::Test3b() { use(typeid(Test3b)); } +// CHECK: @_ZTV6Test3b = linkonce_odr unnamed_addr constant +// CHECK-LATE: @_ZTS6Test3b = linkonce_odr constant +// CHECK-LATE: @_ZTI6Test3b = linkonce_odr unnamed_addr constant + +inline void Test3b::foo() {} + +/*** Test3c ******************************************************************/ + +struct Test3c { + Test3c(); + virtual void foo(); + virtual void bar(); +}; + +// There ceases to be a key function after these declarations. +inline void Test3c::bar() {} +inline void Test3c::foo() {} + +// V-table should be defined with weak linkage. +Test3c::Test3c() { use(typeid(Test3c)); } +// CHECK: @_ZTV6Test3c = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test3c = linkonce_odr constant +// CHECK: @_ZTI6Test3c = linkonce_odr unnamed_addr constant + +/*** Test4a ******************************************************************/ + +template <class T> struct Test4a { + Test4a(); + virtual void foo(); + virtual void bar(); +}; + +// V-table should be defined with weak linkage. +template <> Test4a<int>::Test4a() { use(typeid(Test4a)); } +// CHECK: @_ZTV6Test4aIiE = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test4aIiE = linkonce_odr constant +// CHECK: @_ZTI6Test4aIiE = linkonce_odr unnamed_addr constant + +// There ceases to be a key function after these declarations. +template <> inline void Test4a<int>::bar() {} +template <> inline void Test4a<int>::foo() {} + +/*** Test4b ******************************************************************/ + +template <class T> struct Test4b { + Test4b(); + virtual void foo(); + virtual void bar(); +}; + +// There ceases to be a key function after these declarations. +template <> inline void Test4b<int>::bar() {} + +// V-table should be defined with weak linkage. +template <> Test4b<int>::Test4b() { use(typeid(Test4b)); } +// CHECK: @_ZTV6Test4bIiE = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test4bIiE = linkonce_odr constant +// CHECK: @_ZTI6Test4bIiE = linkonce_odr unnamed_addr constant + +template <> inline void Test4b<int>::foo() {} + +/*** Test4c ******************************************************************/ + +template <class T> struct Test4c { + Test4c(); + virtual void foo(); + virtual void bar(); +}; + +// There ceases to be a key function after these declarations. +template <> inline void Test4c<int>::bar() {} +template <> inline void Test4c<int>::foo() {} + +// V-table should be defined with weak linkage. +template <> Test4c<int>::Test4c() { use(typeid(Test4c)); } +// CHECK: @_ZTV6Test4cIiE = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test4cIiE = linkonce_odr constant +// CHECK: @_ZTI6Test4cIiE = linkonce_odr unnamed_addr constant + +/*** Test5a ******************************************************************/ + +template <class T> struct Test5a { + Test5a(); + virtual void foo(); + virtual void bar(); +}; + +template <> inline void Test5a<int>::bar(); +template <> inline void Test5a<int>::foo(); + +// V-table should be defined with weak linkage. +template <> Test5a<int>::Test5a() { use(typeid(Test5a)); } +// CHECK: @_ZTV6Test5aIiE = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test5aIiE = linkonce_odr constant +// CHECK: @_ZTI6Test5aIiE = linkonce_odr unnamed_addr constant + +// There ceases to be a key function after these declarations. +template <> inline void Test5a<int>::bar() {} +template <> inline void Test5a<int>::foo() {} + +/*** Test5b ******************************************************************/ + +template <class T> struct Test5b { + Test5b(); + virtual void foo(); + virtual void bar(); +}; + +// There ceases to be a key function after these declarations. +template <> inline void Test5a<int>::bar(); +template <> inline void Test5b<int>::bar() {} + +// V-table should be defined with weak linkage. +template <> Test5b<int>::Test5b() { use(typeid(Test5b)); } +// CHECK: @_ZTV6Test5bIiE = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test5bIiE = linkonce_odr constant +// CHECK: @_ZTI6Test5bIiE = linkonce_odr unnamed_addr constant + +template <> inline void Test5a<int>::foo(); +template <> inline void Test5b<int>::foo() {} + +/*** Test5c ******************************************************************/ + +template <class T> struct Test5c { + Test5c(); + virtual void foo(); + virtual void bar(); +}; + +// There ceases to be a key function after these declarations. +template <> inline void Test5a<int>::bar(); +template <> inline void Test5a<int>::foo(); +template <> inline void Test5c<int>::bar() {} +template <> inline void Test5c<int>::foo() {} + +// V-table should be defined with weak linkage. +template <> Test5c<int>::Test5c() { use(typeid(Test5c)); } +// CHECK: @_ZTV6Test5cIiE = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test5cIiE = linkonce_odr constant +// CHECK: @_ZTI6Test5cIiE = linkonce_odr unnamed_addr constant diff --git a/test/CodeGenCXX/vtable-key-function-ios.cpp b/test/CodeGenCXX/vtable-key-function-ios.cpp new file mode 100644 index 0000000000..bcd3e889d2 --- /dev/null +++ b/test/CodeGenCXX/vtable-key-function-ios.cpp @@ -0,0 +1,189 @@ +// RUN: %clang_cc1 %s -triple=armv7-apple-darwin -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 %s -triple=armv7-apple-darwin -emit-llvm -o - | FileCheck -check-prefix=CHECK-LATE %s + +// The 'a' variants ask for the v-table first. +// The 'b' variants ask for the v-table second. +// The 'c' variants ask for the v-table third. +// We do a separate CHECK-LATE pass because the RTTI defintion gets +// changed after the fact, which causes reordering of the globals. + +// These are not separated into namespaces because the way that Sema +// currently reports namespaces to IR-generation (i.e., en masse for +// the entire namespace at once) subverts the ordering that we're +// trying to test. + +namespace std { class type_info; } +extern void use(const std::type_info &rtti); + +/*** Test0a ******************************************************************/ + +struct Test0a { + Test0a(); + virtual inline void foo(); + virtual void bar(); +}; + +// V-table should be defined externally. +Test0a::Test0a() { use(typeid(Test0a)); } +// CHECK: @_ZTV6Test0a = external unnamed_addr constant +// CHECK: @_ZTI6Test0a = external constant + +// This is not a key function. +void Test0a::foo() {} + +/*** Test0b ******************************************************************/ + +struct Test0b { + Test0b(); + virtual inline void foo(); + virtual void bar(); +}; + +// This is not a key function. +void Test0b::foo() {} + +// V-table should be defined externally. +Test0b::Test0b() { use(typeid(Test0b)); } +// CHECK: @_ZTV6Test0b = external unnamed_addr constant +// CHECK: @_ZTI6Test0b = external constant + +/*** Test1a ******************************************************************/ + +struct Test1a { + Test1a(); + virtual void foo(); + virtual void bar(); +}; + +// V-table needs to be defined weakly. +Test1a::Test1a() { use(typeid(Test1a)); } +// CHECK: @_ZTV6Test1a = linkonce_odr unnamed_addr constant +// CHECK-LATE: @_ZTS6Test1a = linkonce_odr constant +// CHECK-LATE: @_ZTI6Test1a = linkonce_odr unnamed_addr constant + +// This defines the key function. +inline void Test1a::foo() {} + +/*** Test1b ******************************************************************/ + +struct Test1b { + Test1b(); + virtual void foo(); + virtual void bar(); +}; + +// This defines the key function. +inline void Test1b::foo() {} + +// V-table should be defined weakly.. +Test1b::Test1b() { use(typeid(Test1b)); } +// CHECK: @_ZTV6Test1b = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test1b = linkonce_odr constant +// CHECK: @_ZTI6Test1b = linkonce_odr unnamed_addr constant + +/*** Test2a ******************************************************************/ + +struct Test2a { + Test2a(); + virtual void foo(); + virtual void bar(); +}; + +// V-table should be defined with weak linkage. +Test2a::Test2a() { use(typeid(Test2a)); } +// CHECK: @_ZTV6Test2a = linkonce_odr unnamed_addr constant +// CHECK-LATE: @_ZTS6Test2a = linkonce_odr constant +// CHECK-LATE: @_ZTI6Test2a = linkonce_odr unnamed_addr constant + +void Test2a::bar() {} +inline void Test2a::foo() {} + +/*** Test2b ******************************************************************/ + +struct Test2b { + Test2b(); + virtual void foo(); + virtual void bar(); +}; + +void Test2b::bar() {} + +// V-table should be defined with weak linkage. +Test2b::Test2b() { use(typeid(Test2b)); } +// CHECK: @_ZTV6Test2b = linkonce_odr unnamed_addr constant +// CHECK-LATE: @_ZTS6Test2b = linkonce_odr constant +// CHECK-LATE: @_ZTI6Test2b = linkonce_odr unnamed_addr constant + +inline void Test2b::foo() {} + +/*** Test2c ******************************************************************/ + +struct Test2c { + Test2c(); + virtual void foo(); + virtual void bar(); +}; + +void Test2c::bar() {} +inline void Test2c::foo() {} + +// V-table should be defined with weak linkage. +Test2c::Test2c() { use(typeid(Test2c)); } +// CHECK: @_ZTV6Test2c = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test2c = linkonce_odr constant +// CHECK: @_ZTI6Test2c = linkonce_odr unnamed_addr constant + +/*** Test3a ******************************************************************/ + +struct Test3a { + Test3a(); + virtual void foo(); + virtual void bar(); +}; + +// V-table should be defined with weak linkage. +Test3a::Test3a() { use(typeid(Test3a)); } +// CHECK: @_ZTV6Test3a = linkonce_odr unnamed_addr constant +// CHECK-LATE: @_ZTS6Test3a = linkonce_odr constant +// CHECK-LATE: @_ZTI6Test3a = linkonce_odr unnamed_addr constant + +// This defines the key function. +inline void Test3a::bar() {} +inline void Test3a::foo() {} + +/*** Test3b ******************************************************************/ + +struct Test3b { + Test3b(); + virtual void foo(); + virtual void bar(); +}; + +inline void Test3b::bar() {} + +// V-table should be defined with weak linkage. +Test3b::Test3b() { use(typeid(Test3b)); } +// CHECK: @_ZTV6Test3b = linkonce_odr unnamed_addr constant +// CHECK-LATE: @_ZTS6Test3b = linkonce_odr constant +// CHECK-LATE: @_ZTI6Test3b = linkonce_odr unnamed_addr constant + +// This defines the key function. +inline void Test3b::foo() {} + +/*** Test3c ******************************************************************/ + +struct Test3c { + Test3c(); + virtual void foo(); + virtual void bar(); +}; + +// This defines the key function. +inline void Test3c::bar() {} +inline void Test3c::foo() {} + +// V-table should be defined with weak linkage. +Test3c::Test3c() { use(typeid(Test3c)); } +// CHECK: @_ZTV6Test3c = linkonce_odr unnamed_addr constant +// CHECK: @_ZTS6Test3c = linkonce_odr constant +// CHECK: @_ZTI6Test3c = linkonce_odr unnamed_addr constant |