takuto.ikuta updated this revision to Diff 167683.
takuto.ikuta added a comment.

Update comment for Sema::ActOnFinishInlineFunctionDef


https://reviews.llvm.org/D51340

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

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<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();
+  };
+
+  // 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();
+}
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<CXXConstructorDecl>(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<CXXDestructorDecl>(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<CXXConstructorDecl>(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<CXXDestructorDecl>(MD)) && MD->isTrivial())
+        if (Context.getTargetInfo().getCXXABI().isMicrosoft() &&
+            !getLangOpts().DllExportInlines &&
+            TSK != TSK_ExplicitInstantiationDeclaration &&
+            TSK != TSK_ExplicitInstantiationDefinition) {
+          // Check local static var later.
           continue;
+        }
       }
     }
 
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<LocalStaticVarChecker, bool> {
+ 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<VarDecl>(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<CXXMethodDecl>(D)) {
+    CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D);
+
+    if (isInlineFunctionDLLExportable(MD)) {
+      CXXRecordDecl *Class = MD->getParent();
+      Attr *ClassAttr = getDLLAttr(Class);
+      LocalStaticVarChecker checker;
+      if (ClassAttr && checker.Visit(D->getBody())) {
+        InheritableAttr *NewAttr = cast<InheritableAttr>(ClassAttr->clone(getASTContext()));
+        NewAttr->setInherited(true);
+        D->addAttr(NewAttr);
+      }
+    }
+  }
   Consumer.HandleInlineFunctionDefinition(D);
 }
 
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
@@ -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/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -5843,6 +5843,10 @@
                             ArrayRef<CXXCtorInitializer*> 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/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
@@ -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/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;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to