vsk created this revision.
vsk added reviewers: chandlerc, t.p.northover, tejohnson, hiraditya.
Herald added a subscriber: mehdi_amini.

In order for the hot/cold splitting pass to graduate out of experimental
status, users need some way to safely enable it.

The current method of passing `-mllvm -hot-cold-split=true` to clang is
not safe, because directly setting a cl::opt cannot interact well with
other CC1 options (e.g. `-O0`, or `-disable-llvm-passes`).

This patch adds -f[no-]split-cold-code CC1 options to clang so that the
splitting pass can be toggled/combined with other options without issue.
This makes it possible to deploy the new pass on a large scale.

I've held off on adding a driver option because the ultimate goal is to
remove these options, and to simply have the pass enabled by default.


https://reviews.llvm.org/D57265

Files:
  clang/include/clang/Basic/CodeGenOptions.def
  clang/include/clang/Driver/CC1Options.td
  clang/lib/CodeGen/BackendUtil.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/test/CodeGen/split-cold-code.c
  llvm/include/llvm/Passes/PassBuilder.h
  llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h
  llvm/lib/Passes/PassBuilder.cpp
  llvm/lib/Transforms/IPO/PassManagerBuilder.cpp

Index: llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
===================================================================
--- llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
+++ llvm/lib/Transforms/IPO/PassManagerBuilder.cpp
@@ -168,6 +168,7 @@
     VerifyInput = false;
     VerifyOutput = false;
     MergeFunctions = false;
+    SplitColdCode = false;
     PrepareForLTO = false;
     EnablePGOInstrGen = false;
     PGOInstrGen = "";
@@ -519,7 +520,7 @@
 
   // Split out cold code before inlining. See comment in the new PM
   // (\ref buildModuleSimplificationPipeline).
-  if (EnableHotColdSplit && DefaultOrPreLinkPipeline)
+  if ((EnableHotColdSplit || SplitColdCode) && DefaultOrPreLinkPipeline)
     MPM.add(createHotColdSplittingPass());
 
   addInstructionCombiningPass(MPM); // Clean up after IPCP & DAE
Index: llvm/lib/Passes/PassBuilder.cpp
===================================================================
--- llvm/lib/Passes/PassBuilder.cpp
+++ llvm/lib/Passes/PassBuilder.cpp
@@ -663,7 +663,7 @@
   // not grow after inlining, and 2) inhibiting inlining of cold code improves
   // code size & compile time. Split after Mem2Reg to make code model estimates
   // more accurate, but before InstCombine to allow it to clean things up.
-  if (EnableHotColdSplit && Phase != ThinLTOPhase::PostLink)
+  if ((EnableHotColdSplit || SplitColdCode) && Phase != ThinLTOPhase::PostLink)
     MPM.addPass(HotColdSplittingPass());
 
   // Create a small function pass pipeline to cleanup after all the global
@@ -2079,3 +2079,7 @@
 
   return Error::success();
 }
+
+void PassBuilder::setEnableHotColdSplitting(bool Enabled) {
+  SplitColdCode = Enabled;
+}
Index: llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h
===================================================================
--- llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h
+++ llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h
@@ -152,6 +152,7 @@
   bool VerifyInput;
   bool VerifyOutput;
   bool MergeFunctions;
+  bool SplitColdCode;
   bool PrepareForLTO;
   bool PrepareForThinLTO;
   bool PerformThinLTO;
Index: llvm/include/llvm/Passes/PassBuilder.h
===================================================================
--- llvm/include/llvm/Passes/PassBuilder.h
+++ llvm/include/llvm/Passes/PassBuilder.h
@@ -577,6 +577,10 @@
     TopLevelPipelineParsingCallbacks.push_back(C);
   }
 
+  /// Enable or disable the hot/cold splitting optimization. By default, it is
+  /// disabled.
+  void setEnableHotColdSplitting(bool Enabled);
+
 private:
   static Optional<std::vector<PipelineElement>>
   parsePipelineText(StringRef Text);
@@ -664,6 +668,8 @@
   // AA callbacks
   SmallVector<std::function<bool(StringRef Name, AAManager &AA)>, 2>
       AAParsingCallbacks;
+  // Tunable passes
+  bool SplitColdCode = false;
 };
 
 /// This utility template takes care of adding require<> and invalidate<>
Index: clang/test/CodeGen/split-cold-code.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/split-cold-code.c
@@ -0,0 +1,81 @@
+// === Old PM ===
+// No splitting at -O0.
+// RUN: %clang_cc1 -O0 -fsplit-cold-code -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s
+//
+// No splitting at -Oz.
+// RUN: %clang_cc1 -Oz -fsplit-cold-code -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s
+//
+// No splitting by default, even at -O3.
+// RUN: %clang_cc1 -O3 -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s
+//
+// No splitting when it's explicitly disabled.
+// RUN: %clang_cc1 -O3 -fno-split-cold-code -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s
+//
+// No splitting when LLVM passes are disabled.
+// RUN: %clang_cc1 -O3 -fsplit-cold-code -disable-llvm-passes -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-NO-SPLIT %s
+//
+// Split at -O1.
+// RUN: %clang_cc1 -O1 -fsplit-cold-code -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s
+//
+// Split at -Os.
+// RUN: %clang_cc1 -Os -fsplit-cold-code -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s
+//
+// Split at -O2.
+// RUN: %clang_cc1 -O2 -fsplit-cold-code -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s
+//
+// Split at -O3.
+// RUN: %clang_cc1 -O3 -fsplit-cold-code -mllvm -debug-pass=Structure \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=OLDPM-SPLIT %s
+
+// OLDPM-NO-SPLIT-NOT: Hot Cold Split
+
+// OLDPM-SPLIT: Hot Cold Split
+
+// === New PM (ditto) ===
+// No splitting at -O0.
+// RUN: %clang_cc1 -O0 -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s
+//
+// No splitting at -Oz.
+// RUN: %clang_cc1 -Oz -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s
+//
+// No splitting by default, even at -O3.
+// RUN: %clang_cc1 -O3 -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s
+//
+// No splitting when it's explicitly disabled.
+// RUN: %clang_cc1 -O3 -fno-split-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s
+//
+// No splitting when LLVM passes are disabled.
+// RUN: %clang_cc1 -O3 -fsplit-cold-code -disable-llvm-passes -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-NO-SPLIT %s
+//
+// Split at -O1.
+// RUN: %clang_cc1 -O1 -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s
+//
+// Split at -Os.
+// RUN: %clang_cc1 -Os -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s
+//
+// Split at -O2.
+// RUN: %clang_cc1 -O2 -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s
+//
+// Split at -O3.
+// RUN: %clang_cc1 -O3 -fsplit-cold-code -fexperimental-new-pass-manager -fdebug-pass-manager \
+// RUN:   -emit-llvm -o /dev/null %s 2>&1 | FileCheck --check-prefix=NEWPM-SPLIT %s
+
+// NEWPM-NO-SPLIT-NOT: HotColdSplit
+
+// NEWPM-SPLIT: HotColdSplit
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -1321,6 +1321,13 @@
 
   Opts.DefaultFunctionAttrs = Args.getAllArgValues(OPT_default_function_attr);
 
+  // -f[no-]split-cold-code
+  // This may only be enabled when optimizing, and when small code size
+  // increases are tolerable.
+  Opts.SplitColdCode =
+      (Opts.OptimizationLevel > 0) && (Opts.OptimizeSize != 2) &&
+      Args.hasFlag(OPT_fsplit_cold_code, OPT_fno_split_cold_code, false);
+
   return Success;
 }
 
Index: clang/lib/CodeGen/BackendUtil.cpp
===================================================================
--- clang/lib/CodeGen/BackendUtil.cpp
+++ clang/lib/CodeGen/BackendUtil.cpp
@@ -554,6 +554,7 @@
 
   PMBuilder.DisableUnrollLoops = !CodeGenOpts.UnrollLoops;
   PMBuilder.MergeFunctions = CodeGenOpts.MergeFunctions;
+  PMBuilder.SplitColdCode = CodeGenOpts.SplitColdCode;
   PMBuilder.PrepareForThinLTO = CodeGenOpts.PrepareForThinLTO;
   PMBuilder.PrepareForLTO = CodeGenOpts.PrepareForLTO;
   PMBuilder.RerollLoops = CodeGenOpts.RerollLoops;
@@ -1012,6 +1013,9 @@
       // configure the pipeline.
       PassBuilder::OptimizationLevel Level = mapToLevel(CodeGenOpts);
 
+      // -fhot-cold-splitting
+      PB.setEnableHotColdSplitting(CodeGenOpts.SplitColdCode);
+
       // Register callbacks to schedule sanitizer passes at the appropriate part of
       // the pipeline.
       // FIXME: either handle asan/the remaining sanitizers or error out
Index: clang/include/clang/Driver/CC1Options.td
===================================================================
--- clang/include/clang/Driver/CC1Options.td
+++ clang/include/clang/Driver/CC1Options.td
@@ -227,6 +227,10 @@
   HelpText<"Dump the layouts of all vtables that will be emitted in a translation unit">;
 def fmerge_functions : Flag<["-"], "fmerge-functions">,
   HelpText<"Permit merging of identical functions when optimizing.">;
+def fsplit_cold_code : Flag<["-"], "fsplit-cold-code">,
+  HelpText<"Permit splitting of cold code when optimizing (off by default).">;
+def fno_split_cold_code : Flag<["-"], "fno-split-cold-code">,
+  HelpText<"Disable splitting of cold code when optimizing.">;
 def femit_coverage_notes : Flag<["-"], "femit-coverage-notes">,
   HelpText<"Emit a gcov coverage notes file when compiling.">;
 def femit_coverage_data: Flag<["-"], "femit-coverage-data">,
Index: clang/include/clang/Basic/CodeGenOptions.def
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.def
+++ clang/include/clang/Basic/CodeGenOptions.def
@@ -124,6 +124,7 @@
                                               ///< linker.
 CODEGENOPT(MergeAllConstants , 1, 1) ///< Merge identical constants.
 CODEGENOPT(MergeFunctions    , 1, 0) ///< Set when -fmerge-functions is enabled.
+CODEGENOPT(SplitColdCode     , 1, 0) ///< Set when -fsplit-cold-code is enabled.
 CODEGENOPT(MSVolatile        , 1, 0) ///< Set when /volatile:ms is enabled.
 CODEGENOPT(NoCommon          , 1, 0) ///< Set when -fno-common or C++ is enabled.
 CODEGENOPT(NoDwarfDirectoryAsm , 1, 0) ///< Set when -fno-dwarf-directory-asm is
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to