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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits