]> granicus.if.org Git - clang/commitdiff
Add /Zc:DllexportInlines option to clang-cl
authorTakuto Ikuta <takuto.ikuta@gmail.com>
Sat, 3 Nov 2018 06:45:00 +0000 (06:45 +0000)
committerTakuto Ikuta <takuto.ikuta@gmail.com>
Sat, 3 Nov 2018 06:45:00 +0000 (06:45 +0000)
Summary:
This CL adds /Zc:DllexportInlines flag to clang-cl.
When Zc:DllexportInlines- is specified, inline class member function is not exported if the function does not have local static variables.

By not exporting inline function, code for those functions are not generated and that reduces both compile time and obj size. Also this flag does not import inline functions from dllimported class if the function does not have local static variables.

On my 24C48T windows10 machine, build performance of chrome target in chromium repository is like below.
These stats are come with 'target_cpu="x86" enable_nacl = false is_component_build=true dcheck_always_on=true` build config and applied
* https://chromium-review.googlesource.com/c/chromium/src/+/1212379
* https://chromium-review.googlesource.com/c/v8/v8/+/1186017

Below stats were taken with this patch applied on https://github.com/llvm-project/llvm-project-20170507/commit/a05115cd4c57ff76b0f529e38118765b58ed7f2e

| config                          | build time | speedup | build dir size |
| with patch, PCH on, debug       | 1h10m0s    | x1.13   | 35.6GB         |
| without patch, PCH on, debug    | 1h19m17s   |         | 49.0GB         |
| with patch, PCH off, debug      | 1h15m45s   | x1.16   | 33.7GB         |
| without patch, PCH off, debug   | 1h28m10s   |         | 52.3GB         |
| with patch, PCH on, release     | 1h13m13s   | x1.22   | 26.2GB         |
| without patch, PCH on, release  | 1h29m57s   |         | 37.5GB         |
| with patch, PCH off, release    | 1h23m38s   | x1.32   | 23.7GB         |
| without patch, PCH off, release | 1h50m50s   |         | 38.7GB         |

This patch reduced obj size and the number of exported symbols largely, that improved link time too.
e.g. link time stats of blink_core.dll become like below
|                              | cold disk cache | warm disk cache |
| with patch, PCH on, debug    | 71s             | 30s             |
| without patch, PCH on, debug | 111s            | 48s             |

This patch's implementation is based on Nico Weber's patch. I modified to support static local variable, added tests and took stats.

Bug: https://bugs.llvm.org/show_bug.cgi?id=33628

Reviewers: hans, thakis, rnk, javed.absar

Reviewed By: hans

Subscribers: kristof.beyls, smeenai, dschuff, probinson, cfe-commits, eraman

Differential Revision: https://reviews.llvm.org/D51340

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@346069 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/Attr.td
include/clang/Basic/LangOptions.def
include/clang/Driver/CC1Options.td
include/clang/Driver/CLCompatOptions.td
lib/Driver/ToolChains/Clang.cpp
lib/Frontend/CompilerInvocation.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
test/CodeGenCXX/dllexport-no-dllexport-inlines.cpp [new file with mode: 0644]
test/Driver/cl-options.c

index 23b05044c70cd1f11eed6121f6eb323a773ace47..2e2550aafbbdd1a9b1f17a98fbe7bbc128df1f95 100644 (file)
@@ -2683,6 +2683,17 @@ def DLLExport : InheritableAttr, TargetSpecificAttr<TargetWindows> {
   let Documentation = [DLLExportDocs];
 }
 
+def DLLExportStaticLocal : InheritableAttr, TargetSpecificAttr<TargetWindows> {
+  // 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<TargetWindows> {
   let Spellings = [Declspec<"dllimport">, GCC<"dllimport">];
   let Subjects = SubjectList<[Function, Var, CXXRecord, ObjCInterface]>;
@@ -2699,6 +2710,16 @@ public:
   }];
 }
 
+def DLLImportStaticLocal : InheritableAttr, TargetSpecificAttr<TargetWindows> {
+  // 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 397343df26061ee4b306ef2a8c5ac50f611191bb..a76eab808e54f46ebcc31bc2be1566f8cdb716d6 100644 (file)
@@ -138,6 +138,7 @@ LANGOPT(NoBuiltin         , 1, 0, "disable builtin functions")
 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 2d89adeb1057c044433d081974f7f7d3882d46a8..c5cb424b9614a18aa9e3d7a6739926db17497970 100644 (file)
@@ -363,6 +363,7 @@ def fno_debug_pass_manager : Flag<["-"], "fno-debug-pass-manager">,
 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 3c2bef74ed0a130eee8bcc2bf1bb1faeda1ce97c..b3eee5162e78d0ed984f8312b843d966ef17218a 100644 (file)
@@ -333,6 +333,8 @@ def _SLASH_Yu : CLJoined<"Yu">,
   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 41681e7314e5f9f5d625f2f9b60384ae7d07b10b..83830b51ed517bbde0eee73183bd4addd501c033 100644 (file)
@@ -5504,6 +5504,11 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
   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 8ccaddd4b1b5ac44d969308cc44b277061f0806d..7be183f9701cb62274e06b77b9c09031a2076258 100644 (file)
@@ -2179,6 +2179,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
     }
   }
 
+  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 f8068dd9c82192ec073b3a9e839d34440862c5d0..0965c3f3473570e6ade284292b3e7e11d0a484ed 100644 (file)
@@ -11930,6 +11930,14 @@ void Sema::CheckStaticLocalForDllExport(VarDecl *VD) {
   assert(VD->isStaticLocal());
 
   auto *FD = dyn_cast_or_null<FunctionDecl>(VD->getParentFunctionOrMethod());
+
+  // Find outermost function when VD is in lambda function.
+  while (FD && !getDLLAttr(FD) &&
+         !FD->hasAttr<DLLExportStaticLocalAttr>() &&
+         !FD->hasAttr<DLLImportStaticLocalAttr>()) {
+    FD = dyn_cast_or_null<FunctionDecl>(FD->getParentFunctionOrMethod());
+  }
+
   if (!FD)
     return;
 
@@ -11938,6 +11946,24 @@ void Sema::CheckStaticLocalForDllExport(VarDecl *VD) {
     auto *NewAttr = cast<InheritableAttr>(A->clone(getASTContext()));
     NewAttr->setInherited(true);
     VD->addAttr(NewAttr);
+  } else if (Attr *A = FD->getAttr<DLLExportStaticLocalAttr>()) {
+    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<DLLExportAttr>())
+      FD->addAttr(NewAttr);
+
+  } else if (Attr *A = FD->getAttr<DLLImportStaticLocalAttr>()) {
+    auto *NewAttr = ::new (getASTContext()) DLLImportAttr(A->getRange(),
+                                                          getASTContext(),
+                                                          A->getSpellingListIndex());
+    NewAttr->setInherited(true);
+    VD->addAttr(NewAttr);
   }
 }
 
index af4be999a1af5f1db4fee38a28aaf49cd8dbbd64..6f173b165f5b313cea7012413ccec4319c62ab7c 100644 (file)
@@ -5707,8 +5707,28 @@ void Sema::checkClassLevelDLLAttribute(CXXRecordDecl *Class) {
       continue;
 
     if (!getDLLAttr(Member)) {
-      auto *NewAttr =
-          cast<InheritableAttr>(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<InheritableAttr>(ClassAttr->clone(getASTContext()));
+      }
+
       NewAttr->setInherited(true);
       Member->addAttr(NewAttr);
 
diff --git a/test/CodeGenCXX/dllexport-no-dllexport-inlines.cpp b/test/CodeGenCXX/dllexport-no-dllexport-inlines.cpp
new file mode 100644 (file)
index 0000000..3866731
--- /dev/null
@@ -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  --check-prefix=EXPORTINLINE %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<lambda_1>@?0??InclassDefFunctWithLambdaStaticVariable@ExportedClass@@QEAAHXZ@QEBA?A?<auto>@@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<typename T>
+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<A11>;
+
+// 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> b22;
+
+void TemplateExportedClassUser() {
+  b22.InclassDefFunc();
+  b22.InclassDefFuncWithStaticVariable();
+}
+
+
+template<typename T>
+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<A11>;
+
+// 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<B22>;
+
+void TemplateNoAttributeClassUser() {
+  TemplateNoAttributeClass<B22> b22;
+  b22.InclassDefFunc();
+  b22.InclassDefFuncWithStaticLocal();
+}
+
+struct __declspec(dllimport) ImportedClass {
+  // NOEXPORTINLINE-DAG: define linkonce_odr dso_local void @"?InClassDefFunc@ImportedClass@@QEAAXXZ"
+  // EXPORTINLINE-DAG: define available_externally 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 80f2d7481579497d1dd211b11cbdc3868ab3b723..ca0c72ca60ea847391dd7cc18d4116853cd484b9 100644 (file)
 // 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"