Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -459,6 +459,9 @@ /// \brief Whether we are currently parsing base specifiers. unsigned IsParsingBaseSpecifiers : 1; + /// \brief Whether class with dllimport attribute needs local vftable. + unsigned NeedLocalVFTable : 1; + /// \brief The number of base class specifiers in Bases. unsigned NumBases; @@ -705,6 +708,9 @@ return data().IsParsingBaseSpecifiers; } + void setNeedLocalVFTable() { data().NeedLocalVFTable = true; } + bool getNeedLocalVFTable() const { return data().NeedLocalVFTable; } + /// \brief Sets the base classes of this struct or class. void setBases(CXXBaseSpecifier const * const *Bases, unsigned NumBases); Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -71,8 +71,8 @@ ImplicitCopyAssignmentHasConstParam(true), HasDeclaredCopyConstructorWithConstParam(false), HasDeclaredCopyAssignmentWithConstParam(false), IsLambda(false), - IsParsingBaseSpecifiers(false), NumBases(0), NumVBases(0), Bases(), - VBases(), Definition(D), FirstFriend() {} + IsParsingBaseSpecifiers(false), NeedLocalVFTable(false), NumBases(0), + NumVBases(0), Bases(), VBases(), Definition(D), FirstFriend() {} CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const { return Bases.get(Definition->getASTContext().getExternalSource()); Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -5759,6 +5759,11 @@ APValue &Result, EvalInfo &Info) { assert(E->isRValue() && E->getType()->isRecordType() && "can't evaluate expression as a record rvalue"); + // The class will need local vftable because true const cannot use dllimported + // load time const. + if (Info.EvalMode == EvalInfo::EM_ConstantExpression) + if (auto *CD = E->getType()->getAsCXXRecordDecl()) + CD->setNeedLocalVFTable(); return RecordExprEvaluator(Info, This, Result).Visit(E); } Index: lib/AST/MicrosoftMangle.cpp =================================================================== --- lib/AST/MicrosoftMangle.cpp +++ lib/AST/MicrosoftMangle.cpp @@ -2602,7 +2602,7 @@ // is always '6' for vftables. msvc_hashing_ostream MHO(Out); MicrosoftCXXNameMangler Mangler(*this, MHO); - if (Derived->hasAttr()) + if (Derived->hasAttr() && Derived->getNeedLocalVFTable()) Mangler.getStream() << "\01??_S"; else Mangler.getStream() << "\01??_7"; Index: lib/CodeGen/CGVTables.cpp =================================================================== --- lib/CodeGen/CGVTables.cpp +++ lib/CodeGen/CGVTables.cpp @@ -845,8 +845,11 @@ // We always synthesize vtables if they are needed in the MS ABI. MSVC doesn't // emit them even if there is an explicit template instantiation. - if (CGM.getTarget().getCXXABI().isMicrosoft()) - return false; + if (CGM.getTarget().getCXXABI().isMicrosoft()) { + // But try to use dllexported vftable if we don't need local one in some + // context. + return RD->hasAttr() && !RD->getNeedLocalVFTable(); + } // If we have an explicit instantiation declaration (and not a // definition), the vtable is defined elsewhere. Index: lib/CodeGen/CodeGenModule.cpp =================================================================== --- lib/CodeGen/CodeGenModule.cpp +++ lib/CodeGen/CodeGenModule.cpp @@ -1447,12 +1447,19 @@ // Implicit template instantiations may change linkage if they are later // explicitly instantiated, so they should not be emitted eagerly. return false; - if (const auto *VD = dyn_cast(Global)) + if (const auto *VD = dyn_cast(Global)) { if (Context.getInlineVariableDefinitionKind(VD) == ASTContext::InlineVariableDefinitionKind::WeakUnknown) // A definition of an inline constexpr static data member may change // linkage later if it's redeclared outside the class. return false; + // In Microsoft ABI mode we have to defer variable definition to check + // if class needs local vftable. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) + if (const auto *CD = VD->getType()->getAsCXXRecordDecl()) + if (CD->hasAttr()) + return false; + } // If OpenMP is enabled and threadprivates must be generated like TLS, delay // codegen for global variables, because they may be marked as threadprivate. if (LangOpts.OpenMP && LangOpts.OpenMPUseTLS && Index: lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- lib/CodeGen/MicrosoftCXXABI.cpp +++ lib/CodeGen/MicrosoftCXXABI.cpp @@ -1670,9 +1670,13 @@ // // Because of this unique behavior, we maintain this logic here instead of // getVTableLinkage. - llvm::GlobalValue::LinkageTypes VFTableLinkage = - RD->hasAttr() ? llvm::GlobalValue::LinkOnceODRLinkage - : CGM.getVTableLinkage(RD); + llvm::GlobalValue::LinkageTypes VFTableLinkage = CGM.getVTableLinkage(RD); + if (RD->hasAttr()) { + if (RD->getNeedLocalVFTable()) + VFTableLinkage = llvm::GlobalValue::LinkOnceODRLinkage; + else + VFTableLinkage = llvm::GlobalValue::ExternalLinkage; + } bool VFTableComesFromAnotherTU = llvm::GlobalValue::isAvailableExternallyLinkage(VFTableLinkage) || llvm::GlobalValue::isExternalLinkage(VFTableLinkage); @@ -1745,7 +1749,9 @@ if (C) VTable->setComdat(C); - if (RD->hasAttr()) + if (RD->hasAttr() && !RD->getNeedLocalVFTable()) + VFTable->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass); + else if (RD->hasAttr()) VFTable->setDLLStorageClass(llvm::GlobalValue::DLLExportStorageClass); VFTablesMap[ID] = VFTable; Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -4809,6 +4809,17 @@ } } + if (!ClassExported) { + if (Class->getTemplateSpecializationKind() != TSK_Undeclared) + Class->setNeedLocalVFTable(); + if (const CXXDestructorDecl *Destructor = Class->getDestructor()) { + // Classes with dllimport attribute needs local vftable if they have + // virtual d-tor. + if (Destructor->isVirtual()) + Class->setNeedLocalVFTable(); + } + } + if (ClassExported) DelayedDllExportClasses.push_back(Class); } Index: test/CodeGenCXX/dllimport-rtti.cpp =================================================================== --- test/CodeGenCXX/dllimport-rtti.cpp +++ test/CodeGenCXX/dllimport-rtti.cpp @@ -4,8 +4,7 @@ struct __declspec(dllimport) S { virtual void f() {} } s; -// MSVC: [[VF_S:.*]] = private unnamed_addr constant [2 x i8*] -// MSVC-DAG: @"\01??_SS@@6B@" = unnamed_addr alias i8*, getelementptr inbounds ([2 x i8*], [2 x i8*]* [[VF_S]], i32 0, i32 1) +// MSVC-DAG: @"\01??_7S@@6B@" = external dllimport unnamed_addr constant [2 x i8*] // MSVC-DAG: @"\01??_R0?AUS@@@8" = linkonce_odr // MSVC-DAG: @"\01??_R1A@?0A@EA@S@@8" = linkonce_odr // MSVC-DAG: @"\01??_R2S@@8" = linkonce_odr @@ -14,6 +13,16 @@ // GNU-DAG: @_ZTV1S = available_externally dllimport // GNU-DAG: @_ZTI1S = external dllimport +struct __declspec(dllimport) S2 { + virtual void f() {} + virtual ~S2() {} +} s2; +// MSVC-DAG: [[VF_S2:.*]] = private unnamed_addr constant [3 x i8*] [i8* bitcast (%rtti.CompleteObjectLocator* @"\01??_R4S2@@6B@" to i8*) +// MSVC-DAG: @"\01??_SS2@@6B@" = unnamed_addr alias i8*, getelementptr inbounds ([3 x i8*], [3 x i8*]* [[VF_S2]], i32 0, i32 1) +// MSVC-DAG: @"\01??_R0?AUS2@@@8" = linkonce_odr +// MSVC-DAG: @"\01??_R1A@?0A@EA@S2@@8" = linkonce_odr +// MSVC-DAG: @"\01??_R2S2@@8" = linkonce_odr + struct U : S { } u; Index: test/CodeGenCXX/dllimport.cpp =================================================================== --- test/CodeGenCXX/dllimport.cpp +++ test/CodeGenCXX/dllimport.cpp @@ -620,9 +620,31 @@ struct __declspec(dllimport) W { virtual void foo() {} }; USECLASS(W) // vftable: -// MO1-DAG: @"\01??_SW@@6B@" = linkonce_odr unnamed_addr constant [1 x i8*] [i8* bitcast (void (%struct.W*)* @"\01?foo@W@@UAEXXZ" to i8*)] +// MO1-DAG: @"\01??_7W@@6B@" = external dllimport unnamed_addr constant [1 x i8*] // GO1-DAG: @_ZTV1W = available_externally dllimport unnamed_addr constant [3 x i8*] [i8* null, i8* null, i8* bitcast (void (%struct.W*)* @_ZN1W3fooEv to i8*)] +struct __declspec(dllimport) W2 { + virtual ~W2(); + virtual void foo() {} +}; +USECLASS(W2) +// vftable: +// MO1-DAG: @"\01??_SW2@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*] +// GO1-DAG: @_ZTV2W2 = external dllimport unnamed_addr constant [5 x i8*] + +struct __declspec(dllimport) W3 { + virtual void fn() const { + } + constexpr W3() = default; +}; + +W3 w3; +W3& w3r = w3; + +constexpr W3 cw3; +const W3 &cw3r = cw3; +// MO1-DAG: @"\01??_SW3@@6B@" = linkonce_odr unnamed_addr constant [1 x i8*] + struct __declspec(dllimport) KeyFuncClass { constexpr KeyFuncClass() {} virtual void foo();