takuto.ikuta updated this revision to Diff 164353.
takuto.ikuta edited the summary of this revision.
takuto.ikuta added a comment.
Herald added a subscriber: dschuff.

Make patch closer to Nico's original implementation, but warns local static 
variable instead of detecting it.
In checkClassLevelDLLAttribute, inline function definition is not fully parsed 
and I cannot make test passed in other way adding export only to inline 
function having local static variables.


https://reviews.llvm.org/D51340

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Basic/LangOptions.def
  clang/include/clang/Basic/LangOptions.h
  clang/include/clang/Driver/CC1Options.td
  clang/include/clang/Driver/CLCompatOptions.td
  clang/lib/Driver/ToolChains/Clang.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/CodeGenCXX/dllexport-no-inline.cpp

Index: clang/test/CodeGenCXX/dllexport-no-inline.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/dllexport-no-inline.cpp
@@ -0,0 +1,131 @@
+// RUN: %clang_cc1 %s -fms-extensions -triple x86_64-windows-msvc       \
+// RUN:     -fno-dllexport-inlines -emit-llvm -O0 -o - -verify -DNOEXPORT_INLINE | \
+// 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 {
+  // DEFAULT-NOT: NoTemplateExportedClass@NoTemplateExportedClass@@
+  NoTemplateExportedClass() = default;
+
+  // NOINLINE-NOT: InclassDefFunc@NoTemplateExportedClass
+  // INLINE-DAG: define weak_odr dso_local dllexport void @"?InclassDefFunc@NoTemplateExportedClass@@AEAAXXZ"
+  void InclassDefFunc() {}
+
+  int f();
+
+  // FIX: make this DEFAULT-DAG.
+  // INLINE-DAG: define weak_odr dso_local dllexport i32 @"?InclassDefFuncWithStaticVariable@NoTemplateExportedClass@@AEAAHXZ"
+  int InclassDefFuncWithStaticVariable() {
+#ifdef NOEXPORT_INLINE
+    // expected-warning@+2 {{static 'static_variable' used in an inline function of exported class isn't exported due to -fno-dllexport-inlines}}
+#endif
+    static int static_variable = f();
+
+    static const int static_const_variable = 1; // expected-no-warning
+    constexpr int static_constexpr_variable = 2; // expected-no-warning
+    static const int static_const_array[] = {1, 2, 3}; // expected-no-warning
+
+    return ++static_variable + static_const_variable + static_constexpr_variable +
+        static_const_array[0];
+  }
+
+  // DEFAULT-NOT: InlineOutclassDefFuncWihtoutDefinition
+  __forceinline void InlineOutclassDefFuncWihtoutDefinition();
+
+  // FIX: Do not export this when -ms-no-dllexport-inline.
+  // DEFAULT-DAG: define weak_odr dso_local dllexport void @"?InlineOutclassDefFunc@NoTemplateExportedClass@@AEAAXXZ"
+  __forceinline void InlineOutclassDefFunc();
+
+  // FIX: Do not export this when -ms-no-dllexport-inline is given and warn for local static var.
+  // DEFAULT-DAG: define weak_odr dso_local dllexport i32 @"?InlineOutclassDefFuncWithStaticVariable@NoTemplateExportedClass@@AEAAHXZ"
+  __forceinline int InlineOutclassDefFuncWithStaticVariable();
+
+  // DEFAULT-DAG: define dso_local dllexport void @"?OutclassDefFunc@NoTemplateExportedClass@@AEAAXXZ"
+  void OutclassDefFunc();
+};
+
+void NoTemplateExportedClass::OutclassDefFunc() {}
+
+__forceinline void NoTemplateExportedClass::InlineOutclassDefFunc() {}
+
+__forceinline int NoTemplateExportedClass::InlineOutclassDefFuncWithStaticVariable() {
+    static int static_variable = 0;
+    return ++static_variable;
+}
+
+template<typename T>
+class __declspec(dllexport) TemplateExportedClass {
+  void InclassDefFunc() {}
+  void OutclassDefFunc();
+
+  T templateValue;
+};
+
+// DEFAULT-NOT: define dso_local dllexport void @"?OutclassDefFunc@NoTemplateExportedClass@@
+template<typename T> void TemplateExportedClass<T>::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<A11>;
+
+// 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<B22>;
+
+
+template<typename T>
+class TemplateNoExportedClass {
+  void InclassDefFunc() {}
+  void OutclassDefFunc();
+};
+
+template<typename T> void TemplateNoExportedClass<T>::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<A11>;
+
+// 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<B22>;
+
+
+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();
+}
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -5485,6 +5485,38 @@
   }
 }
 
+namespace {
+
+// StaticLocalVarChecker is used to warn if static local variables are used in
+// inline functions of exported class with fno-dllexport-inlines
+class StaticLocalVarChecker final : public ConstStmtVisitor<StaticLocalVarChecker> {
+ private:
+  Sema &SemaRef;
+
+ public:
+  StaticLocalVarChecker(Sema &S) : SemaRef(S) {}
+  void VisitDeclStmt(const DeclStmt *E) {
+    for (const Decl *D : E->decls()) {
+      if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
+        if (VD->isStaticLocal() && !VD->checkInitIsICE()) {
+          SemaRef.Diag(VD->getLocation(),
+                       diag::warn_static_local_in_inline_member_of_exported_class)
+              << VD;
+        }
+      }
+    }
+  }
+
+  void VisitStmt(const Stmt *S) {
+    for (const Stmt *Child : S->children()) {
+      if (Child) {
+        Visit(Child);
+      }
+    }
+  }
+};
+} // namespace
+
 static void ReferenceDllExportedMembers(Sema &S, CXXRecordDecl *Class) {
   Attr *ClassAttr = getDLLAttr(Class);
   if (!ClassAttr)
@@ -5512,6 +5544,26 @@
     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 (MD->isInlined() && !getDLLAttr(MD)) {
+      const Stmt *Body = nullptr;
+      StaticLocalVarChecker Checker(S);
+
+      if (const FunctionDecl *Definition = MD->getDefinition()) {
+        Body = Definition->getBody();
+
+        if (S.Context.getTargetInfo().getCXXABI().isMicrosoft() &&
+            !S.getLangOpts().DllexportInlines &&
+            Class->getTemplateSpecializationKind() != TSK_ExplicitInstantiationDeclaration &&
+            Class->getTemplateSpecializationKind() != TSK_ExplicitInstantiationDefinition &&
+            Body) {
+          Checker.Visit(Body);
+        }
+      }
+    }
+
     if (Member->getAttr<DLLExportAttr>()) {
       if (MD->isUserProvided()) {
         // Instantiate non-default class member functions ...
@@ -5686,6 +5738,17 @@
             !Context.getTargetInfo().getTriple().isWindowsItaniumEnvironment())
           continue;
 
+        if (Context.getTargetInfo().getCXXABI().isMicrosoft() &&
+            !getLangOpts().DllexportInlines &&
+            Class->getTemplateSpecializationKind() != TSK_ExplicitInstantiationDeclaration &&
+            Class->getTemplateSpecializationKind() != TSK_ExplicitInstantiationDefinition &&
+
+            // To check static local var later.
+            MD->isDefined()) {
+          // Warn later if MD has static local variable.
+          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/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/Driver/ToolChains/Clang.cpp
===================================================================
--- clang/lib/Driver/ToolChains/Clang.cpp
+++ clang/lib/Driver/ToolChains/Clang.cpp
@@ -5238,6 +5238,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/include/clang/Driver/CLCompatOptions.td
===================================================================
--- clang/include/clang/Driver/CLCompatOptions.td
+++ clang/include/clang/Driver/CLCompatOptions.td
@@ -301,6 +301,8 @@
   MetaVarName<"<filename>">;
 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<"<filename>">;
 
Index: clang/include/clang/Driver/CC1Options.td
===================================================================
--- clang/include/clang/Driver/CC1Options.td
+++ clang/include/clang/Driver/CC1Options.td
@@ -692,6 +692,8 @@
   HelpText<"Don't use a const qualified type for string literals in C and ObjC">;
 def fno_bitfield_type_align : Flag<["-"], "fno-bitfield-type-align">,
   HelpText<"Ignore bit-field types when aligning structures">;
+def fno_dllexport_inlines : Flag<["-"], "fno-dllexport-inlines">,
+  HelpText<"Don't export inline functions">;
 def ffake_address_space_map : Flag<["-"], "ffake-address-space-map">,
   HelpText<"Use a fake address space map; OpenCL testing purposes only">;
 def faddress_space_map_mangling_EQ : Joined<["-"], "faddress-space-map-mangling=">, MetaVarName<"<yes|no|target>">,
Index: clang/include/clang/Basic/LangOptions.h
===================================================================
--- clang/include/clang/Basic/LangOptions.h
+++ clang/include/clang/Basic/LangOptions.h
@@ -214,6 +214,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.
@@ -270,7 +273,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.
@@ -316,7 +319,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/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/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4684,6 +4684,10 @@
 def warn_static_local_in_extern_inline : Warning<
   "non-constant static local variable in inline function may be different "
   "in different files">, InGroup<StaticLocalInInline>;
+def warn_static_local_in_inline_member_of_exported_class : Warning<
+  "static %0 used in an inline function of "
+  "exported class isn't exported due to -fno-dllexport-inlines">,
+  InGroup<StaticInInline>;
 def note_convert_inline_to_static : Note<
   "use 'static' to give inline function %0 internal linkage">;
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to