Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -2683,6 +2683,14 @@ let Documentation = [DLLExportDocs]; } +def DLLExportStaticLocal : InheritableAttr, TargetSpecificAttr { + // This attribute is used internally only when -fno-dllexport-inlines is + // passed. + 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 +2707,14 @@ }]; } +def DLLImportStaticLocal : InheritableAttr, TargetSpecificAttr { + // This attribute is used internally only when -fno-dllexport-inlines is + // passed. + 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 @@ -217,6 +217,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. @@ -273,7 +276,7 @@ /// Floating point control options class FPOptions { public: - FPOptions() : fp_contract(LangOptions::FPC_Off), + FPOptions() : fp_contract(LangOptions::FPC_Off), fenv_access(LangOptions::FEA_Off) {} // Used for serializing. @@ -319,7 +322,7 @@ unsigned getInt() const { return fp_contract | (fenv_access << 2); } private: - /// Adjust BinaryOperator::FPFeatures to match the total bit-field size + /// Adjust BinaryOperator::FPFeatures to match the total bit-field size /// of these two. unsigned fp_contract : 2; unsigned fenv_access : 1; Index: clang/include/clang/Driver/CC1Options.td =================================================================== --- clang/include/clang/Driver/CC1Options.td +++ clang/include/clang/Driver/CC1Options.td @@ -354,6 +354,7 @@ HelpText<"Prints debug information for the new pass manager">; def fno_debug_pass_manager : Flag<["-"], "fno-debug-pass-manager">, HelpText<"Disables debug printing for the new pass manager">; +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 @@ -9540,6 +9540,13 @@ } else if (D->hasAttr()) { if (L == GVA_DiscardableODR) return GVA_StrongODR; + } else if (D->hasAttr() || + D->hasAttr()) { + // 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. + return GVA_DiscardableODR; } else if (Context.getLangOpts().CUDA && Context.getLangOpts().CUDAIsDevice && D->hasAttr()) { // Device-side functions with __global__ attribute must always be Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5412,6 +5412,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 @@ -2159,6 +2159,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 @@ -11977,14 +11977,41 @@ } if (VD->isStaticLocal()) { - if (FunctionDecl *FD = - dyn_cast_or_null(VD->getParentFunctionOrMethod())) { + FunctionDecl *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) { // Static locals inherit dll attributes from their function. if (Attr *A = getDLLAttr(FD)) { 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); } + // CUDA 8.0 E.3.9.4: Within the body of a __device__ or __global__ // function, only __shared__ variables or variables without any device // memory qualifiers may be declared with static storage class. Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -5708,6 +5708,24 @@ if (!getDLLAttr(Member)) { auto *NewAttr = cast(ClassAttr->clone(getASTContext())); + + // Do not export inline function when -fno-dllexport-inlines is passed. + // But add attribute for later local static var check. + if (!getLangOpts().DllExportInlines && MD && MD->isInlined() && + TSK != TSK_ExplicitInstantiationDefinition) { + if (ClassExported) { + NewAttr = ::new (getASTContext()) + DLLExportStaticLocalAttr(ClassAttr->getRange(), + getASTContext(), + ClassAttr->getSpellingListIndex()); + } else { + NewAttr = ::new (getASTContext()) + DLLImportStaticLocalAttr(ClassAttr->getRange(), + getASTContext(), + ClassAttr->getSpellingListIndex()); + } + } + 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,167 @@ +// 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(); +} + +// 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{}; + +// DEFAULT-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; + +// DEFAULT-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 { + void InclassDefFunc() {} + void OutclassDefFunc(); +}; + +template void TemplateNoExportedClass::OutclassDefFunc() {} + +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateNoExportedClass@VA11@@@@AEAAXXZ" +// DEFAULT-DAG: define weak_odr dso_local dllexport void @"?OutclassDefFunc@?$TemplateNoExportedClass@VA11@@@@AEAAXXZ" +template class __declspec(dllexport) TemplateNoExportedClass; + +// DEFAULT-DAG: define weak_odr dso_local void @"?InclassDefFunc@?$TemplateNoExportedClass@VB22@@@@AEAAXXZ" +// DEFAULT-DAG: define weak_odr dso_local void @"?OutclassDefFunc@?$TemplateNoExportedClass@VB22@@@@AEAAXXZ" +template class TemplateNoExportedClass; + +// NOINLINE-NOT: ?InclassDefFunc@?$TemplateNoExportedClass@VC33@ +// DEFAULT-NOT: ?OutclassDefFunc@?$TemplateNoExportedClass@VC33@ +extern template class TemplateNoExportedClass; + +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(); +}