Index: clang/include/clang/Driver/CLCompatOptions.td =================================================================== --- clang/include/clang/Driver/CLCompatOptions.td +++ clang/include/clang/Driver/CLCompatOptions.td @@ -301,6 +301,8 @@ MetaVarName<"">; def _SLASH_Y_ : CLFlag<"Y-">, HelpText<"Disable precompiled headers, overrides /Yc and /Yu">; +def _SLASH_Zc_dllexportInlines : CLFlag<"Zc:dllexportInlines">; +def _SLASH_Zc_dllexportInlines_ : CLFlag<"Zc:dllexportInlines-">; def _SLASH_Fp : CLJoined<"Fp">, HelpText<"Set pch filename (with /Yc and /Yu)">, MetaVarName<"">; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -10638,7 +10638,7 @@ // Emitting members of dllexported classes is delayed until the class // (including field initializers) is fully parsed. - SmallVector DelayedDllExportClasses; + SmallVector DelayedDllExportImportClasses; private: class SavePendingParsedClassStateRAII { @@ -10653,7 +10653,7 @@ assert(S.DelayedDefaultedMemberExceptionSpecs.empty() && "there shouldn't be any pending delayed defaulted member " "exception specs"); - assert(S.DelayedDllExportClasses.empty() && + assert(S.DelayedDllExportImportClasses.empty() && "there shouldn't be any pending delayed DLL export classes"); swapSavedState(); } @@ -10666,7 +10666,7 @@ SavedEquivalentExceptionSpecChecks; decltype(DelayedDefaultedMemberExceptionSpecs) SavedDefaultedMemberExceptionSpecs; - decltype(DelayedDllExportClasses) SavedDllExportClasses; + decltype(DelayedDllExportImportClasses) SavedDllExportImportClasses; void swapSavedState() { SavedOverridingExceptionSpecChecks.swap( @@ -10675,7 +10675,7 @@ S.DelayedEquivalentExceptionSpecChecks); SavedDefaultedMemberExceptionSpecs.swap( S.DelayedDefaultedMemberExceptionSpecs); - SavedDllExportClasses.swap(S.DelayedDllExportClasses); + SavedDllExportImportClasses.swap(S.DelayedDllExportImportClasses); } }; Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5238,6 +5238,11 @@ if (VolatileOptionID == options::OPT__SLASH_volatile_ms) CmdArgs.push_back("-fms-volatile"); + if (Args.hasFlag(options::OPT__SLASH_Zc_dllexportInlines_, + options::OPT__SLASH_Zc_dllexportInlines, + false)) + CmdArgs.push_back("-fvisibility-inlines-hidden"); + Arg *MostGeneralArg = Args.getLastArg(options::OPT__SLASH_vmg); Arg *BestCaseArg = Args.getLastArg(options::OPT__SLASH_vmb); if (MostGeneralArg && BestCaseArg) Index: clang/lib/Sema/Sema.cpp =================================================================== --- clang/lib/Sema/Sema.cpp +++ clang/lib/Sema/Sema.cpp @@ -926,8 +926,8 @@ assert(DelayedEquivalentExceptionSpecChecks.empty()); assert(DelayedDefaultedMemberExceptionSpecs.empty()); - // All dllexport classes should have been processed already. - assert(DelayedDllExportClasses.empty()); + // All dllexport/dllimport classes should have been processed already. + assert(DelayedDllExportImportClasses.empty()); // Remove file scoped decls that turned out to be used. UnusedFileScopedDecls.erase( Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -5485,13 +5485,50 @@ } } +namespace { +class AttrLocalStaticVars : public StmtVisitor { + Sema &SemaRef; + Attr *ClassAttr; + + public: + AttrLocalStaticVars(Sema &S, Attr *A) : SemaRef(S), ClassAttr(A) {} + + bool VisitChildren(Stmt *S) { + for (Stmt *Child : S->children()) { + if (Child && Visit(Child)) + return true; + } + return false; + } + + bool VisitDeclStmt(DeclStmt *DS) { + bool HasStatic = VisitChildren(DS); + for (Decl *D : DS->decls()) { + if (VarDecl *VD = dyn_cast(D)) { + if (VD->isStaticLocal()) { + auto *NewAttr = + cast(ClassAttr->clone(SemaRef.getASTContext())); + NewAttr->setInherited(true); + VD->addAttr(NewAttr); + HasStatic = true; + } + } + } + return HasStatic; + } + + + bool VisitStmt(Stmt *S) { + return VisitChildren(S); + } +}; +} + static void ReferenceDllExportedMembers(Sema &S, CXXRecordDecl *Class) { Attr *ClassAttr = getDLLAttr(Class); if (!ClassAttr) return; - assert(ClassAttr->getKind() == attr::DLLExport); - TemplateSpecializationKind TSK = Class->getTemplateSpecializationKind(); if (TSK == TSK_ExplicitInstantiationDeclaration) @@ -5499,6 +5536,33 @@ // declaration. return; + for (Decl *Member : Class->decls()) { + auto *MD = dyn_cast(Member); + if (!MD) + continue; + + if (MD->isInlined() && S.Context.getTargetInfo().getCXXABI().isMicrosoft() && + S.getLangOpts().InlineVisibilityHidden && + TSK != TSK_ExplicitInstantiationDefinition && + !Member->getAttr() && MD->getBody()) { + // Export or import local static var for inline function. + AttrLocalStaticVars ALSV(S, ClassAttr); + + if (ALSV.Visit(MD->getBody()) && ClassAttr->getKind() == attr::DLLExport) { + // Class is dllexported, export inline function having local static var too. + // This is necessary to have instantiation of local static var. + auto *NewAttr = + cast(ClassAttr->clone(S.getASTContext())); + NewAttr->setInherited(true); + MD->addAttr(NewAttr); + } + } + } + + if (ClassAttr->getKind() == attr::DLLImport) { + return; + } + for (Decl *Member : Class->decls()) { // Defined static variables that are members of an exported base // class must be marked export too. @@ -5686,6 +5750,16 @@ !Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment()) continue; + if (Context.getTargetInfo().getCXXABI().isMicrosoft() && + getLangOpts().InlineVisibilityHidden && + Class->getTemplateSpecializationKind() != TSK_ExplicitInstantiationDeclaration && + Class->getTemplateSpecializationKind() != TSK_ExplicitInstantiationDefinition && + + // Check local static variable later. + MD->willHaveBody()) { + continue; + } + // MSVC versions before 2015 don't export the move assignment operators // and move constructor, so don't attempt to import/export them if // we have a definition. @@ -5734,8 +5808,7 @@ } } - if (ClassExported) - DelayedDllExportClasses.push_back(Class); + DelayedDllExportImportClasses.push_back(Class); } /// Perform propagation of DLL attributes from a derived class to a @@ -11222,11 +11295,11 @@ } void Sema::referenceDLLExportedClassMethods() { - if (!DelayedDllExportClasses.empty()) { + if (!DelayedDllExportImportClasses.empty()) { // Calling ReferenceDllExportedMembers might cause the current function to // be called again, so use a local copy of DelayedDllExportClasses. SmallVector WorkList; - std::swap(DelayedDllExportClasses, WorkList); + std::swap(DelayedDllExportImportClasses, WorkList); for (CXXRecordDecl *Class : WorkList) ReferenceDllExportedMembers(*this, Class); } Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -8554,8 +8554,8 @@ "on Def without dllexport or dllimport"); // We reject explicit instantiations in class scope, so there should - // never be any delayed exported classes to worry about. - assert(S.DelayedDllExportClasses.empty() && + // never be any delayed exported/imported classes to worry about. + assert(S.DelayedDllExportImportClasses.empty() && "delayed exports present at explicit instantiation"); S.checkClassLevelDLLAttribute(Def); Index: clang/test/CodeGenCXX/dllexport-import-fvisibility-inlines-hidden.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/dllexport-import-fvisibility-inlines-hidden.cpp @@ -0,0 +1,162 @@ +// RUN: %clang_cc1 %s -fms-extensions -triple x86_64-windows-msvc \ +// RUN: -fvisibility-inlines-hidden -emit-llvm -O0 -o - | \ +// RUN: FileCheck --check-prefix=DEFAULT --check-prefix=NOINLINE %s + +// RUN: %clang_cc1 %s -fms-extensions -triple x86_64-windows-msvc \ +// RUN: -emit-llvm -O0 -o - | \ +// RUN: FileCheck --check-prefix=DEFAULT --check-prefix=INLINE %s + + +// Function + +// DEFAULT-DAG: define dso_local dllexport void @"?NormalFunction@@YAXXZ"() +void __declspec(dllexport) NormalFunction() {} + + +// NOINLINE-DAG: define weak_odr hidden dllexport void @"?AlwaysInlineFunction@@YAXXZ" +// INLINE-DAG: define weak_odr dso_local dllexport void @"?AlwaysInlineFunction@@YAXXZ" +__forceinline void __declspec(dllexport) AlwaysInlineFunction() {} + +// DEFAULT-DAG: @"?static_variable@?1??AlwaysInlineWithStaticVariableExported@@YAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 +__forceinline int __declspec(dllexport) AlwaysInlineWithStaticVariableExported() { + static int static_variable = 0; + ++static_variable; + return static_variable; +} + +// DEFAULT-DAG: @"?static_variable@?1??AlwaysInlineWithStaticVariableImported@@YAHXZ@4HA" = available_externally dllimport global i32 0, align 4 +__forceinline int __declspec(dllimport) AlwaysInlineWithStaticVariableImported() { + static int static_variable = 0; + ++static_variable; + return static_variable; +} + +int ImportedFunctionUser() { + return AlwaysInlineWithStaticVariableImported(); +} + +// Class member function + +// check for local static variables +// DEFAULT-DAG: @"?static_variable@?1??InclassDefFuncWithStaticVariable@NoTemplateExportedClass@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 +// DEFAULT-DAG: @"?static_variable@?1??InlineOutclassDefFuncWithStaticVariable@NoTemplateExportedClass@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 + +// local static var in ImportedClass +// NOINLINE-DAG: @"?static_variable@?1??InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ@4HA" = available_externally dllimport global i32 0, align 4 +// INLINE-NOT: static_variable@?1??InClassDefFuncWithStaticVariable@ImportedClass@@ + +class __declspec(dllexport) NoTemplateExportedClass { + public: + // DEFAULT-NOT: NoTemplateExportedClass@NoTemplateExportedClass@@ + NoTemplateExportedClass() = default; + + // NOINLINE-NOT: InclassDefFunc@NoTemplateExportedClass + // INLINE-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@NoTemplateExportedClass@@ + void InclassDefFunc() {} + + int f(); + + // INLINE-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFuncWithStaticVariable@NoTemplateExportedClass@@QEAAHXZ" + // NOINLINE-DAG: define weak_odr hidden dllexport i32 @"?InclassDefFuncWithStaticVariable@NoTemplateExportedClass@@QEAAHXZ" + int InclassDefFuncWithStaticVariable() { + static int static_variable = 0; + ++static_variable; + return static_variable; + } + + // DEFAULT-NOT: InlineOutclassDefFuncWihtoutDefinition + __forceinline void InlineOutclassDefFuncWihtoutDefinition(); + + // DEFAULT-NOT: InlineOutclassDefFunc@NoTemplateExportedClass@@ + __forceinline void InlineOutclassDefFunc(); + + // DEFAULT-NOT: InlineOutclassDefFuncWithStaticVariable@NoTemplateExportedClass@@ + __forceinline int InlineOutclassDefFuncWithStaticVariable(); + + // DEFAULT-DAG: define dso_local dllexport void @"?OutclassDefFunc@NoTemplateExportedClass@@QEAAXXZ" + void OutclassDefFunc(); +}; + +void NoTemplateExportedClass::OutclassDefFunc() {} + +__forceinline void NoTemplateExportedClass::InlineOutclassDefFunc() {} + +__forceinline int NoTemplateExportedClass::InlineOutclassDefFuncWithStaticVariable() { + static int static_variable = 0; + return ++static_variable; +} + +void __declspec(dllexport) NoTemplateExportedClassUser() { + NoTemplateExportedClass a; + a.InlineOutclassDefFunc(); +} + +template +class __declspec(dllexport) TemplateExportedClass { + void InclassDefFunc() {} + void OutclassDefFunc(); + + T templateValue; +}; + +// DEFAULT-NOT: define dso_local dllexport void @"?OutclassDefFunc@NoTemplateExportedClass@@ +template void TemplateExportedClass::OutclassDefFunc() {} + +class A11{}; +class B22{}; + +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateExportedClass@VA11@@ +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?OutclassDefFunc@?$TemplateExportedClass@VA11@@ +template class __declspec(dllexport) TemplateExportedClass; + +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateExportedClass@VB22@@ +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?OutclassDefFunc@?$TemplateExportedClass@VB22@@ +template class TemplateExportedClass; + + +template +class TemplateNoExportedClass { + void InclassDefFunc() {} + void OutclassDefFunc(); +}; + +template void TemplateNoExportedClass::OutclassDefFunc() {} + +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateNoExportedClass@VA11@@ +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?OutclassDefFunc@?$TemplateNoExportedClass@VA11@@ +template class __declspec(dllexport) TemplateNoExportedClass; + +// DEFAULT-DAG: define weak_odr dso_local void @"?InclassDefFunc@?$TemplateNoExportedClass@VB22@@ +// DEFAULT-DAG: define weak_odr dso_local void @"?OutclassDefFunc@?$TemplateNoExportedClass@VB22@@ +template class TemplateNoExportedClass; + + +class __declspec(dllimport) ImportedClass { +public: + class ImportedInnerClass { + public: + void OutClassDefFunc(); + }; + + // INLINE-DAG: declare dllimport i32 @"?InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ"(%class.ImportedClass*) + // NOINLINE-NOT dllimport .* InClassDefFuncWithStaticVariable@ImportedClass + int InClassDefFuncWithStaticVariable() { + static int static_variable = 0; + ++static_variable; + return static_variable; + } + + // INLINE-DAG: declare dllimport void @"?InClassDefFunc@ImportedClass@ + // NOINLINE-NOT: dllimport .* InClassDefFunc@ImportedClass@ + void InClassDefFunc() { + i.OutClassDefFunc(); + } + ImportedInnerClass i; +}; + +int InClassDefFuncUser() { + // This is necessary for declare statement of ImportedClass::InClassDefFunc(). + ImportedClass c; + c.InClassDefFunc(); + return c.InClassDefFuncWithStaticVariable(); +} Index: clang/test/CodeGenCXX/hidden-dllimport.cpp =================================================================== --- clang/test/CodeGenCXX/hidden-dllimport.cpp +++ clang/test/CodeGenCXX/hidden-dllimport.cpp @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -fvisibility-inlines-hidden -o - %s | FileCheck %s -// We used to declare this hidden dllimport, which is contradictory. +// We don't declare this hidden dllimport. -// CHECK: declare dllimport void @"?bar@foo@@QEAAXXZ"(%struct.foo*) +// CHECK-NOT: declare dllimport void @"?bar@foo@@QEAAXXZ"(%struct.foo*) struct __attribute__((dllimport)) foo { void bar() {}