Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2683,6 +2683,17 @@ let Documentation = [DLLExportDocs]; } +def DLLExportStaticLocal : InheritableAttr, TargetSpecificAttr { + // This attribute is used internally only when -fno-dllexport-inlines is + // passed. This attribute is added to inline function of class having + // dllexport attribute. And if the function has static local variables, this + // attribute is used to whether the variables are exported or not. Also if + // function has local static variables, the function is dllexported too. + let Spellings = []; + let Subjects = SubjectList<[Function]>; + let Documentation = [Undocumented]; +} + def DLLImport : InheritableAttr, TargetSpecificAttr { let Spellings = [Declspec<"dllimport">, GCC<"dllimport">]; let Subjects = SubjectList<[Function, Var, CXXRecord, ObjCInterface]>; @@ -2699,6 +2710,16 @@ }]; } +def DLLImportStaticLocal : InheritableAttr, TargetSpecificAttr { + // This attribute is used internally only when -fno-dllexport-inlines is + // passed. This attribute is added to inline function of class having + // dllimport attribute. And if the function has static local variables, this + // attribute is used to whether the variables are imported or not. + let Spellings = []; + let Subjects = SubjectList<[Function]>; + let Documentation = [Undocumented]; +} + def SelectAny : InheritableAttr { let Spellings = [Declspec<"selectany">, GCC<"selectany">]; let Documentation = [SelectAnyDocs]; Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -242,6 +242,9 @@ /// input is a header file (i.e. -x c-header). bool IsHeaderFile = false; + /// If set, dllexported classes dllexport their inline methods. + bool DllExportInlines = true; + LangOptions(); // Define accessors/mutators for language options of enumeration type. Index: clang/include/clang/Driver/CC1Options.td =================================================================== --- clang/include/clang/Driver/CC1Options.td +++ clang/include/clang/Driver/CC1Options.td @@ -360,6 +360,7 @@ def msign_return_address_key_EQ : Joined<["-"], "msign-return-address-key=">, Values<"a_key,b_key">; def mbranch_target_enforce : Flag<["-"], "mbranch-target-enforce">; +def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">; //===----------------------------------------------------------------------===// // Dependency Output Options Index: clang/include/clang/Driver/CLCompatOptions.td =================================================================== --- clang/include/clang/Driver/CLCompatOptions.td +++ clang/include/clang/Driver/CLCompatOptions.td @@ -333,6 +333,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/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -9515,6 +9515,14 @@ if (!MD->isUserProvided()) return GVA_DiscardableODR; + // When -fno-dllexport-inlines is given, let inline member function not + // having static local variable have discardableodr linkage. This is to + // overwrite linkage of explicit template instantiation + // definition/declaration. + if (!Context.getLangOpts().DllExportInlines && + isa(FD) && cast(FD)->isInlined()) + return GVA_DiscardableODR; + GVALinkage External; switch (FD->getTemplateSpecializationKind()) { case TSK_Undeclared: Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5502,6 +5502,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("-fno-dllexport-inlines"); + Arg *MostGeneralArg = Args.getLastArg(options::OPT__SLASH_vmg); Arg *BestCaseArg = Args.getLastArg(options::OPT__SLASH_vmb); if (MostGeneralArg && BestCaseArg) Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -2178,6 +2178,9 @@ } } + if (Args.hasArg(OPT_fno_dllexport_inlines)) + Opts.DllExportInlines = false; + if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) { StringRef Name = A->getValue(); if (Name == "full" || Name == "branch") { Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -11930,6 +11930,14 @@ assert(VD->isStaticLocal()); auto *FD = dyn_cast_or_null(VD->getParentFunctionOrMethod()); + + // Find outermost function when VD is in lambda function. + while (FD && !getDLLAttr(FD) && + !FD->hasAttr() && + !FD->hasAttr()) { + FD = dyn_cast_or_null(FD->getParentFunctionOrMethod()); + } + if (!FD) return; @@ -11938,6 +11946,24 @@ auto *NewAttr = cast(A->clone(getASTContext())); NewAttr->setInherited(true); VD->addAttr(NewAttr); + } else if (Attr *A = FD->getAttr()) { + auto *NewAttr = ::new (getASTContext()) DLLExportAttr(A->getRange(), + getASTContext(), + A->getSpellingListIndex()); + NewAttr->setInherited(true); + VD->addAttr(NewAttr); + + // Export this function to enforce exporting this static variable even + // if it is not used in this compilation unit. + if (!FD->hasAttr()) + FD->addAttr(NewAttr); + + } else if (Attr *A = FD->getAttr()) { + auto *NewAttr = ::new (getASTContext()) DLLImportAttr(A->getRange(), + getASTContext(), + A->getSpellingListIndex()); + NewAttr->setInherited(true); + VD->addAttr(NewAttr); } } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -5706,8 +5706,26 @@ continue; if (!getDLLAttr(Member)) { - auto *NewAttr = - cast(ClassAttr->clone(getASTContext())); + InheritableAttr *NewAttr = nullptr; + + // Do not export/import inline function when -fno-dllexport-inlines is + // passed. But add attribute for later local static var check. + if (!getLangOpts().DllExportInlines && MD && MD->isInlined()) { + if (ClassExported) { + NewAttr = ::new (getASTContext()) + DLLExportStaticLocalAttr(ClassAttr->getRange(), + getASTContext(), + ClassAttr->getSpellingListIndex()); + } else { + NewAttr = ::new (getASTContext()) + DLLImportStaticLocalAttr(ClassAttr->getRange(), + getASTContext(), + ClassAttr->getSpellingListIndex()); + } + } else { + NewAttr = cast(ClassAttr->clone(getASTContext())); + } + NewAttr->setInherited(true); Member->addAttr(NewAttr); Index: clang/test/CodeGenCXX/dllexport-no-dllexport-inlines.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/dllexport-no-dllexport-inlines.cpp @@ -0,0 +1,199 @@ +// RUN: %clang_cc1 %s -fms-extensions -triple x86_64-windows-msvc \ +// RUN: -fno-dllexport-inlines -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() {} + + +// DEFAULT-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(); +} + + +// DEFAULT-DAG: @"?static_x@?2??InclassDefFuncWithStaticLocal@?$TemplateNoExportedClass@VA11@@@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 + +// NOINLINE-DAG: @"?static_x@?2??InclassDefFuncWithStaticLocal@?$TemplateNoExportedClass@VD44@@@@QEAAHXZ@4HA" = available_externally dllimport global i32 0, align 4 + + +// Class member function + +// check for local static variables +// NOINLINE-DAG: @"?static_variable@?1??InclassDefFuncWithStaticVariable@NoTemplateExportedClass@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 + +// INLINE-DAG: @"?static_variable@?1??InclassDefFuncWithStaticVariable@NoTemplateExportedClass@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 + +// NOINLINE-DAG: @"?static_variable@?1??InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ@4HA" = available_externally dllimport global i32 0, align 4 + +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(); + + // DEFAULT-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFuncWithStaticVariable@NoTemplateExportedClass@@QEAAHXZ" + int InclassDefFuncWithStaticVariable() { + static int static_variable = 0; + ++static_variable; + return static_variable; + } + + // DEFAULT-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFunctWithLambdaStaticVariable@NoTemplateExportedClass@@QEAAHXZ" + int InclassDefFunctWithLambdaStaticVariable() { + return ([]() { static int static_x; return ++static_x; })(); + } + + // 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{}; +class C33{}; +class D44{}; + +// INLINE-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateExportedClass@VA11@@@@AEAAXXZ" +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?OutclassDefFunc@?$TemplateExportedClass@VA11@@@@AEAAXXZ" +template class __declspec(dllexport) TemplateExportedClass; + +// INLINE-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateExportedClass@VB22@@@@AEAAXXZ" +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?OutclassDefFunc@?$TemplateExportedClass@VB22@@@@AEAAXXZ +template class TemplateExportedClass; + + +template +class TemplateNoExportedClass { +public: + void InclassDefFunc() {} + int InclassDefFuncWithStaticLocal() { + static int static_x; + return ++static_x; + } + void OutclassDefFunc(); +}; + +template void TemplateNoExportedClass::OutclassDefFunc() {} + +// NOINLINE-NOT: define{{.*}}@"?InclassDefFunc@?$TemplateNoExportedClass@VA11 +// INLINE-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateNoExportedClass@VA11@@@@QEAAXXZ" +// DEFAULT-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFuncWithStaticLocal@?$TemplateNoExportedClass@VA11 +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?OutclassDefFunc@?$TemplateNoExportedClass@VA11@@@@QEAAXXZ" +template class __declspec(dllexport) TemplateNoExportedClass; + +// NOINLINE-NOT: define{{.*}}@"?InclassDefFunc@?$TemplateNoExportedClass@VB22 +// INLINE-DAG: define weak_odr dso_local void @"?InclassDefFunc@?$TemplateNoExportedClass@VB22@@@@QEAAXXZ" +// DEFAULT-DAG: define weak_odr dso_local void @"?OutclassDefFunc@?$TemplateNoExportedClass@VB22@@@@QEAAXXZ" +template class TemplateNoExportedClass; + +// NOINLINE-NOT: declare{{.*}}@"?InclassDefFunc@?$TemplateNoExportedClass@VC33 +// INLINE-DAG: declare dso_local void @"?InclassDefFunc@?$TemplateNoExportedClass@VC33@@@@QEAAXXZ" +// DEFAULT-NOT: ?OutclassDefFunc@?$TemplateNoExportedClass@VC33@ +extern template class TemplateNoExportedClass; + +// NOINLINE-NOT: declare{{.*}}@"?InclassDefFunc@?$TemplateNoExportedClass@VD44 +// INLINE-DAG: declare dllimport void @"?InclassDefFunc@?$TemplateNoExportedClass@VD44@@@@QEAAXXZ" +// NOINLINE-NOT: declare{{.*}}@"?InclassDefFuncWithStaticLocal@?$TemplateNoExportedClass@VD44 +// INLINE-DAG: declare dllimport i32 @"?InclassDefFuncWithStaticLocal@?$TemplateNoExportedClass@VD44 +// DEFAULT-NOT: ?OutclassDefFunc@?$TemplateNoExportedClass@VD44@ +extern template class __declspec(dllimport) TemplateNoExportedClass; + +void ExplicitTemplateDeclarationUser() { + TemplateNoExportedClass c33; + c33.InclassDefFunc(); + + TemplateNoExportedClass d44; + d44.InclassDefFunc(); + d44.InclassDefFuncWithStaticLocal(); +} + +class __declspec(dllimport) ImportedClass { +public: + class ImportedInnerClass { + public: + void OutClassDefFunc(); + }; + + // INLINE-DAG: declare dllimport i32 @"?InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ"(%class.ImportedClass*) #2 + // NOINLINE-NOT: declare{{.*}}"?InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ" + int InClassDefFuncWithStaticVariable() { + static int static_variable = 0; + ++static_variable; + return static_variable; + } + + // NOINLINE-DAG: define linkonce_odr dso_local void @"?InClassDefFunc@ImportedClass@@QEAAXXZ" + // INLINE-DAG: declare dllimport void @"?InClassDefFunc@ImportedClass@@QEAAXXZ" + void InClassDefFunc() { + i.OutClassDefFunc(); + } + ImportedInnerClass i; +}; + +int InClassDefFuncUser() { + // This is necessary for declare statement of ImportedClass::InClassDefFunc(). + ImportedClass c; + c.InClassDefFunc(); + return c.InClassDefFuncWithStaticVariable(); +}