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.def =================================================================== --- clang/include/clang/Basic/LangOptions.def +++ clang/include/clang/Basic/LangOptions.def @@ -138,6 +138,7 @@ LANGOPT(NoMathBuiltin , 1, 0, "disable math builtin functions") LANGOPT(GNUAsm , 1, 1, "GNU-style inline assembly") LANGOPT(CoroutinesTS , 1, 0, "C++ coroutines TS") +LANGOPT(DllExportInlines , 1, 1, "dllexported classes dllexport inline methods") LANGOPT(RelaxedTemplateTemplateArgs, 1, 0, "C++17 relaxed matching of template template arguments") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") 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/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,28 @@ 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() && + TSK != TSK_ExplicitInstantiationDeclaration && + TSK != TSK_ExplicitInstantiationDefinition) { + 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,133 @@ +// RUN: %clang_cc1 %s -fms-extensions -triple x86_64-windows-msvc \ +// RUN: -disable-llvm-passes \ +// RUN: -fno-dllexport-inlines -emit-llvm -O1 -o - | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=NOEXPORTINLINE %s + +// RUN: %clang_cc1 %s -fms-extensions -triple x86_64-windows-msvc \ +// RUN: -disable-llvm-passes \ +// RUN: -emit-llvm -O1 -o - | \ +// RUN: FileCheck --check-prefix=CHECK %s + + +struct __declspec(dllexport) ExportedClass { + + // NOEXPORTINLINE-DAG: define linkonce_odr dso_local void @"?InclassDefFunc@ExportedClass@@ + // EXPORTINLINE-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@ExportedClass@@ + void InclassDefFunc() {} + + // CHECK-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFuncWithStaticVariable@ExportedClass@@QEAAHXZ" + int InclassDefFuncWithStaticVariable() { + // CHECK-DAG: @"?static_variable@?1??InclassDefFuncWithStaticVariable@ExportedClass@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 + static int static_variable = 0; + ++static_variable; + return static_variable; + } + + // CHECK-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFunctWithLambdaStaticVariable@ExportedClass@@QEAAHXZ" + int InclassDefFunctWithLambdaStaticVariable() { + // CHECK-DAG: @"?static_x@?2???R@?0??InclassDefFunctWithLambdaStaticVariable@ExportedClass@@QEAAHXZ@QEBA?A?@@XZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 + return ([]() { static int static_x; return ++static_x; })(); + } + + // NOEXPORTINLINE-DAG: define linkonce_odr dso_local void @"?InlineOutclassDefFunc@ExportedClass@@QEAAXXZ + // EXPORTINLINE-DAG: define weak_odr dso_local dllexport void @"?InlineOutclassDefFunc@ExportedClass@@QEAAXXZ + inline void InlineOutclassDefFunc(); + + // CHECK-DAG: define weak_odr dso_local dllexport i32 @"?InlineOutclassDefFuncWithStaticVariable@ExportedClass@@QEAAHXZ" + inline int InlineOutclassDefFuncWithStaticVariable(); + + // CHECK-DAG: define dso_local dllexport void @"?OutoflineDefFunc@ExportedClass@@QEAAXXZ" + void OutoflineDefFunc(); +}; + +void ExportedClass::OutoflineDefFunc() {} + +inline void ExportedClass::InlineOutclassDefFunc() {} + +inline int ExportedClass::InlineOutclassDefFuncWithStaticVariable() { + static int static_variable = 0; + return ++static_variable; +} + +void ExportedClassUser() { + ExportedClass a; + a.InclassDefFunc(); + a.InlineOutclassDefFunc(); +} + +template +struct __declspec(dllexport) TemplateExportedClass { + void InclassDefFunc() {} + + int InclassDefFuncWithStaticVariable() { + static int static_x = 0; + return ++static_x; + } +}; + +class A11{}; +class B22{}; + +// CHECK-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateExportedClass@VA11@@@@QEAAXXZ" +// CHECK-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFuncWithStaticVariable@?$TemplateExportedClass@VA11@@@@QEAAHXZ" +// CHECK-DAG: @"?static_x@?2??InclassDefFuncWithStaticVariable@?$TemplateExportedClass@VA11@@@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 +template class TemplateExportedClass; + +// NOEXPORTINLINE-DAG: define linkonce_odr dso_local void @"?InclassDefFunc@?$TemplateExportedClass@VB22@@@@QEAAXXZ" +// EXPORTINLINE-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateExportedClass@VB22@@@@QEAAXXZ +// CHECK-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFuncWithStaticVariable@?$TemplateExportedClass@VB22@@@@QEAAHXZ" +// CHECK-DAG: @"?static_x@?2??InclassDefFuncWithStaticVariable@?$TemplateExportedClass@VB22@@@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 +TemplateExportedClass b22; + +void TemplateExportedClassUser() { + b22.InclassDefFunc(); + b22.InclassDefFuncWithStaticVariable(); +} + + +template +struct TemplateNoAttributeClass { + void InclassDefFunc() {} + int InclassDefFuncWithStaticLocal() { + static int static_x; + return ++static_x; + } +}; + +// CHECK-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@?$TemplateNoAttributeClass@VA11@@@@QEAAXXZ" +// CHECK-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFuncWithStaticLocal@?$TemplateNoAttributeClass@VA11 +// CHECK-DAG: @"?static_x@?2??InclassDefFuncWithStaticLocal@?$TemplateNoAttributeClass@VA11@@@@QEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 +template class __declspec(dllexport) TemplateNoAttributeClass; + +// CHECK-DAG: define available_externally dllimport void @"?InclassDefFunc@?$TemplateNoAttributeClass@VB22@@@@QEAAXXZ" +// CHECK-DAG: define available_externally dllimport i32 @"?InclassDefFuncWithStaticLocal@?$TemplateNoAttributeClass@VB22@@@@QEAAHXZ" +// CHECK-DAG: @"?static_x@?2??InclassDefFuncWithStaticLocal@?$TemplateNoAttributeClass@VB22@@@@QEAAHXZ@4HA" = available_externally dllimport global i32 0, align 4 +extern template class __declspec(dllimport) TemplateNoAttributeClass; + +void TemplateNoAttributeClassUser() { + TemplateNoAttributeClass b22; + b22.InclassDefFunc(); + b22.InclassDefFuncWithStaticLocal(); +} + +struct __declspec(dllimport) ImportedClass { + // NOEXPORTINLINE-DAG: define linkonce_odr dso_local void @"?InClassDefFunc@ImportedClass@@QEAAXXZ" + // EXPORTINLINE-DAG: declare dllimport void @"?InClassDefFunc@ImportedClass@@QEAAXXZ" + void InClassDefFunc() {} + + // EXPORTINLINE-DAG: define available_externally dllimport i32 @"?InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ" + // NOEXPORTINLINE-DAG: define linkonce_odr dso_local i32 @"?InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ" + int InClassDefFuncWithStaticVariable() { + // CHECK-DAG: @"?static_variable@?1??InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ@4HA" = available_externally dllimport global i32 0, align 4 + static int static_variable = 0; + ++static_variable; + return static_variable; + } +}; + +int InClassDefFuncUser() { + // This is necessary for declare statement of ImportedClass::InClassDefFunc(). + ImportedClass c; + c.InClassDefFunc(); + return c.InClassDefFuncWithStaticVariable(); +} Index: clang/test/Driver/cl-options.c =================================================================== --- clang/test/Driver/cl-options.c +++ clang/test/Driver/cl-options.c @@ -490,6 +490,11 @@ // RUN: %clang_cl /Zc:threadSafeInit /c -### -- %s 2>&1 | FileCheck -check-prefix=ThreadSafeStatics %s // ThreadSafeStatics-NOT: "-fno-threadsafe-statics" +// RUN: %clang_cl /Zc:dllexportInlines- /c -### -- %s 2>&1 | FileCheck -check-prefix=NoDllExportInlines %s +// NoDllExportInlines: "-fno-dllexport-inlines" +// RUN: %clang_cl /Zc:dllexportInlines /c -### -- %s 2>&1 | FileCheck -check-prefix=DllExportInlines %s +// DllExportInlines-NOT: "-fno-dllexport-inlines" + // RUN: %clang_cl /Zi /c -### -- %s 2>&1 | FileCheck -check-prefix=Zi %s // Zi: "-gcodeview" // Zi: "-debug-info-kind=limited"