Index: clang/include/clang/Basic/LangOptions.h =================================================================== --- clang/include/clang/Basic/LangOptions.h +++ clang/include/clang/Basic/LangOptions.h @@ -214,6 +214,10 @@ /// 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/Basic/LangOptions.def =================================================================== --- clang/include/clang/Basic/LangOptions.def +++ clang/include/clang/Basic/LangOptions.def @@ -114,6 +114,7 @@ LANGOPT(GNUKeywords , 1, 1, "GNU keywords") BENIGN_LANGOPT(ImplicitInt, 1, !C99 && !CPlusPlus, "C89 implicit 'int'") LANGOPT(Digraphs , 1, 0, "digraphs") +LANGOPT(DllexportInlines , 1, 1, "If dllexport a class should dllexport inline methods in the Microsoft ABI") BENIGN_LANGOPT(HexFloats , 1, C99, "C99 hexadecimal float constants") LANGOPT(CXXOperatorNames , 1, 0, "C++ operator name keywords") LANGOPT(AppleKext , 1, 0, "Apple kext support") Index: clang/include/clang/Driver/CC1Options.td =================================================================== --- clang/include/clang/Driver/CC1Options.td +++ clang/include/clang/Driver/CC1Options.td @@ -169,6 +169,7 @@ HelpText<"Make assembler warnings fatal">; def mrelax_relocations : Flag<["--"], "mrelax-relocations">, HelpText<"Use relaxable elf relocations">; +def ms_no_dllexport_inline : Flag<["-"], "ms-no-dllexport-inline">; def msave_temp_labels : Flag<["-"], "msave-temp-labels">, HelpText<"Save temporary labels in the symbol table. " "Note this may change .s semantics and shouldn't generally be used " 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/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -5205,6 +5205,12 @@ if (VolatileOptionID == options::OPT__SLASH_volatile_ms) CmdArgs.push_back("-fms-volatile"); + // FIXME: what about the non-cl driver + if (Args.hasFlag(options::OPT__SLASH_Zc_dllexportInlines_, + options::OPT__SLASH_Zc_dllexportInlines, + false)) + CmdArgs.push_back("-ms-no-dllexport-inline"); + 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 @@ -2143,6 +2143,9 @@ } } + if (Args.getLastArg(OPT_ms_no_dllexport_inline)) + 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/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -5512,6 +5512,10 @@ if (!MD) continue; + // XXX: need to skip inlines here + // or not needed because they no longer get dllexport attribs. hmm, best place here + // or checkClassLevelDLLAttribute? + if (Member->getAttr()) { if (MD->isUserProvided()) { // Instantiate non-default class member functions ... @@ -5531,6 +5535,7 @@ // defaulted methods, and the copy and move assignment operators. The // latter are exported even if they are trivial, because the address of // an operator can be taken and should compare equal across libraries. + // XXX not with that flag i guess DiagnosticErrorTrap Trap(S.Diags); S.MarkFunctionReferenced(Class->getLocation(), MD); if (Trap.hasErrorOccurred()) { @@ -5595,6 +5600,31 @@ } } +namespace { +class StaticLocalVarChecker final : public ConstStmtVisitor { +public: + bool VisitDeclStmt(const DeclStmt *E) { + E->dump(); + + for (const Decl* D : E->decls()) { + if (const auto *VD = dyn_cast(D)) { + return VD->isStaticLocal(); + } + } + return false; + } + + bool VisitStmt(const Stmt *S) { + S->dump(); + for (const Stmt *Child : S->children()) { + if (Child && Visit(Child)) + return true; + } + return false; + } +}; +} // namespace + /// Check class-level dllimport/dllexport attribute. void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) { Attr *ClassAttr = getDLLAttr(Class); @@ -5686,6 +5716,22 @@ !Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment()) continue; + bool HasStaticLocalVariable = false; + if (const FunctionDecl *Definition = MD->getDefinition()) { + if (const Stmt *Body = Definition->getBody()) { + StaticLocalVarChecker Checker; + HasStaticLocalVariable = Checker.Visit(Body); + } + } + + // XXX: explicit instantiation declarations give me trouble + if (Context.getTargetInfo().getCXXABI().isMicrosoft() && + !getLangOpts().DllexportInlines && + !HasStaticLocalVariable && + Class->getTemplateSpecializationKind() != TSK_ExplicitInstantiationDeclaration && + Class->getTemplateSpecializationKind() != TSK_ExplicitInstantiationDefinition) + 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. Index: clang/test/CodeGenCXX/dllexport-no-inline.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/dllexport-no-inline.cpp @@ -0,0 +1,104 @@ +// RUN: %clang_cc1 %s -fms-extensions -triple x86_64-windows-msvc \ +// RUN: -ms-no-dllexport-inline -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@@ +__forceinline void __declspec(dllexport) AlwaysInlineFunction() {} + +// Class member function + +class __declspec(dllexport) NoTemplateExportedClass { + // NOINLINE-NOT: InclassDefFunc@NoTemplateExportedClass + // INLINE-DAG: InclassDefFunc@NoTemplateExportedClass + void InclassDefFunc() {} + int InclassDefFuncWithStaticVariable() { + // This should be DEFAULT-DAG ? + // INLINE-DAG: @"?static_variable@?1??InclassDefFuncWithStaticVariable@NoTemplateExportedClass@@AEAAHXZ@4HA" = weak_odr dso_local dllexport global i32 0, comdat, align 4 + static int static_variable = 0; + return ++static_variable; + } + + void OutclassDefFunc(); + + __forceinline void InlineOutclassDefFunc(); +}; + +// DEFAULT-DAG: define dso_local dllexport void @"?OutclassDefFunc@NoTemplateExportedClass@@AEAAXXZ" +void NoTemplateExportedClass::OutclassDefFunc() {} + + +// NOINLINE-NOT: InlineOutclassDefFunc@NoTemplateExportedClass@@ +// INLINE-DAG: InlineOutclassDefFunc@NoTemplateExportedClass@@ +__forceinline void NoTemplateExportedClass::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 void @"?InClassDefFunc@ImportedClass@ + // NOINLINE-NOT: dllimport .* InClassDefFunc@ImportedClass@ + void InClassDefFunc() { + i.OutClassDefFunc(); + } + ImportedInnerClass i; +}; + +void InClassDefFuncUser() { + // This is necessary for declare statement of ImportedClass::InClassDefFunc(). + ImportedClass c; + c.InClassDefFunc(); +}