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 @@ -351,6 +351,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 @@ -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 @@ -5843,6 +5843,10 @@ ArrayRef MemInits, bool AnyErrors); + /// Check whether inline function can be exported by default. + /// This function does not take care fno-dllexport-inlines option. + bool isInlineFunctionDLLExportable(const CXXMethodDecl *MD); + /// Check class-level dllimport/dllexport attribute. The caller must /// ensure that referenceDLLExportedClassMethods is called some point later /// when all outer classes of Class are complete. Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5287,6 +5287,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 @@ -2153,6 +2153,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 @@ -12586,7 +12586,60 @@ return ActOnStartOfFunctionDef(FnBodyScope, DP, SkipBody); } +namespace { +class LocalStaticVarChecker : public ConstStmtVisitor { + public: + + bool VisitChildren(const Stmt *S) { + for (const Stmt *Child : S->children()) { + if (Child && Visit(Child)) + return true; + } + return false; + } + + bool VisitDeclStmt(const DeclStmt *DS) { + if (VisitChildren(DS)) + return true; + + for (const Decl *D : DS->decls()) { + if (const VarDecl *VD = dyn_cast(D)) { + if (VD->isStaticLocal()) { + return true; + } + } + } + return false; + } + + bool VisitStmt(const Stmt *S) { + return VisitChildren(S); + } +}; +} + void Sema::ActOnFinishInlineFunctionDef(FunctionDecl *D) { + // Non inline functions inherit dll attributes in + // Sema::checkClassLevelDLLAttribute. + // Inline functions need to be checked whether they have static local + // variables that requires them to have function body when + // -fno-dllexport-inlines is given. + + if (!getLangOpts().DllExportInlines && D->isInlined() && !getDLLAttr(D) && + isa(D)) { + CXXMethodDecl *MD = dyn_cast(D); + + if (isInlineFunctionDLLExportable(MD)) { + CXXRecordDecl *Class = MD->getParent(); + Attr *ClassAttr = getDLLAttr(Class); + LocalStaticVarChecker checker; + if (ClassAttr && checker.Visit(D->getBody())) { + InheritableAttr *NewAttr = cast(ClassAttr->clone(getASTContext())); + NewAttr->setInherited(true); + D->addAttr(NewAttr); + } + } + } Consumer.HandleInlineFunctionDefinition(D); } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -5596,6 +5596,32 @@ } } +bool Sema::isInlineFunctionDLLExportable(const CXXMethodDecl *MD) { + assert(MD->isInlined()); + + // MinGW does not import or export inline methods. + if (!Context.getTargetInfo().getCXXABI().isMicrosoft() && + !Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment()) + return false; + + // 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. + auto *Ctor = dyn_cast(MD); + if ((MD->isMoveAssignmentOperator() || + (Ctor && Ctor->isMoveConstructor())) && + !getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015)) + return false; + + // MSVC2015 doesn't export trivial defaulted x-tor but copy assign + // operator is exported anyway. + if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && + (Ctor || isa(MD)) && MD->isTrivial()) + return false; + + return true; +} + void Sema::checkClassLevelCodeSegAttribute(CXXRecordDecl *Class) { // Mark any compiler-generated routines with the implicit code_seg attribute. for (auto *Method : Class->methods()) { @@ -5692,25 +5718,16 @@ continue; if (MD->isInlined()) { - // MinGW does not import or export inline methods. - if (!Context.getTargetInfo().getCXXABI().isMicrosoft() && - !Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment()) + if (!isInlineFunctionDLLExportable(MD)) 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. - auto *Ctor = dyn_cast(MD); - if ((MD->isMoveAssignmentOperator() || - (Ctor && Ctor->isMoveConstructor())) && - !getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015)) - continue; - - // MSVC2015 doesn't export trivial defaulted x-tor but copy assign - // operator is exported anyway. - if (getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) && - (Ctor || isa(MD)) && MD->isTrivial()) + if (Context.getTargetInfo().getCXXABI().isMicrosoft() && + !getLangOpts().DllExportInlines && + TSK != TSK_ExplicitInstantiationDeclaration && + TSK != TSK_ExplicitInstantiationDefinition) { + // Check local static var later. continue; + } } } Index: clang/test/CodeGenCXX/dllexport-no-dllexport-inlines.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/dllexport-no-dllexport-inlines.cpp @@ -0,0 +1,159 @@ +// 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" = linkonce_odr dso_local 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 +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{}; + +// 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(); + }; + + // DEFAULT-DAG: declare dllimport i32 @"?InClassDefFuncWithStaticVariable@ImportedClass@@QEAAHXZ"(%class.ImportedClass*) #2 + 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(); +}