[llvm] 97ab068 - [NPM] Do not run function simplification pipeline unnecessarily
Mircea Trofin via llvm-commits
llvm-commits at lists.llvm.org
Thu May 6 12:34:45 PDT 2021
Author: Mircea Trofin
Date: 2021-05-06T12:24:33-07:00
New Revision: 97ab068034161fb35e5c9a7b293bf1e569cf077b
URL: https://github.com/llvm/llvm-project/commit/97ab068034161fb35e5c9a7b293bf1e569cf077b
DIFF: https://github.com/llvm/llvm-project/commit/97ab068034161fb35e5c9a7b293bf1e569cf077b.diff
LOG: [NPM] Do not run function simplification pipeline unnecessarily
The CGSCC pass manager interplay with the FunctionAnalysisManagerCGSCCProxy is 'special' in the sense that the former will rerun the latter if there are changes to a SCC structure; that being said, some of the functions in the SCC may be unchanged. In that case, the function simplification pipeline will be re-run, which impacts compile time[1].
This patch allows the function simplification pipeline be skipped if it was already run and the function was not modified since.
The behavior is currently disabled by default. This is because, currently, the rerunning of the function simplification pipeline on an unchanged function may still result in changes. The patch simplifies investigating and fixing those cases where repeated function pass runs do actually positively impact code quality, while offering an easy workaround for those impacted negatively by compile time regressions, and not impacting mainline scenarios.
[1] A [[ http://llvm-compile-time-tracker.com/compare.php?from=eb37d3546cd0c6e67798496634c45e501f7806f1&to=ac722d1190dc7bbdd17e977ef7ec95e69eefc91e&stat=instructions | compile time tracker ]] run with the option enabled.
Differential Revision: https://reviews.llvm.org/D98103
Added:
llvm/test/Other/new-pass-manager-cgscc-fct-proxy.ll
Modified:
llvm/include/llvm/Analysis/CGSCCPassManager.h
llvm/lib/Analysis/CGSCCPassManager.cpp
llvm/lib/Passes/PassBuilder.cpp
llvm/lib/Passes/PassRegistry.def
llvm/unittests/Analysis/CGSCCPassManagerTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/CGSCCPassManager.h b/llvm/include/llvm/Analysis/CGSCCPassManager.h
index 985424a74054..4a9f7f118950 100644
--- a/llvm/include/llvm/Analysis/CGSCCPassManager.h
+++ b/llvm/include/llvm/Analysis/CGSCCPassManager.h
@@ -487,6 +487,18 @@ class CGSCCToFunctionPassAdaptor
std::unique_ptr<PassConceptT> Pass;
};
+/// A 'signaling' analysis to indicate whether a function has been changed. It
+/// is meant to control the runs of the function pass(es) managed by the
+/// FunctionAnalysisManagerCGSCCProxy.
+class FunctionStatusAnalysis
+ : public AnalysisInfoMixin<FunctionStatusAnalysis> {
+public:
+ static AnalysisKey Key;
+ struct Result {};
+
+ Result run(Function &F, FunctionAnalysisManager &FAM) { return Result(); }
+};
+
/// A function to deduce a function pass type and wrap it in the
/// templated adaptor.
template <typename FunctionPassT>
diff --git a/llvm/lib/Analysis/CGSCCPassManager.cpp b/llvm/lib/Analysis/CGSCCPassManager.cpp
index 99876630e813..8ecf06ff86b6 100644
--- a/llvm/lib/Analysis/CGSCCPassManager.cpp
+++ b/llvm/lib/Analysis/CGSCCPassManager.cpp
@@ -44,6 +44,8 @@ static cl::opt<bool> AbortOnMaxDevirtIterationsReached(
cl::desc("Abort when the max iterations for devirtualization CGSCC repeat "
"pass is reached"));
+AnalysisKey FunctionStatusAnalysis::Key;
+
// Explicit instantiations for the core proxy templates.
template class AllAnalysesOn<LazyCallGraph::SCC>;
template class AnalysisManager<LazyCallGraph::SCC, LazyCallGraph &>;
@@ -547,6 +549,13 @@ PreservedAnalyses CGSCCToFunctionPassAdaptor::run(LazyCallGraph::SCC &C,
continue;
Function &F = N->getFunction();
+ // The expectation here is that FunctionStatusAnalysis was required at the
+ // end of the function passes pipeline managed by this adaptor. Then, if any
+ // CGSCC passes were re-run because CGSCCs changed (or devirtualization),
+ // and none changed F, then FunctionStatusAnalysis would still be cached
+ // here and we don't need to rerun the passes managed by this adaptor.
+ if (FAM.getCachedResult<FunctionStatusAnalysis>(F))
+ continue;
PassInstrumentation PI = FAM.getResult<PassInstrumentationAnalysis>(F);
if (!PI.runBeforePass<Function>(*Pass, F))
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index e0338601e83d..f0963d4292de 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -279,6 +279,11 @@ static cl::opt<bool> EnableO3NonTrivialUnswitching(
"enable-npm-O3-nontrivial-unswitch", cl::init(true), cl::Hidden,
cl::ZeroOrMore, cl::desc("Enable non-trivial loop unswitching for -O3"));
+static cl::opt<bool> DoNotRerunFunctionPasses(
+ "cgscc-npm-no-fp-rerun", cl::init(false),
+ cl::desc("Do not rerun function passes wrapped by the scc pass adapter, if "
+ "they were run already and the function hasn't changed."));
+
PipelineTuningOptions::PipelineTuningOptions() {
LoopInterleaving = true;
LoopVectorization = true;
@@ -1022,8 +1027,10 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
// Lastly, add the core function simplification pipeline nested inside the
// CGSCC walk.
- MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(
- buildFunctionSimplificationPipeline(Level, Phase)));
+ auto FSP = buildFunctionSimplificationPipeline(Level, Phase);
+ if (DoNotRerunFunctionPasses)
+ FSP.addPass(RequireAnalysisPass<FunctionStatusAnalysis, Function>());
+ MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(std::move(FSP)));
return MIWP;
}
@@ -1182,6 +1189,9 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
MPM.addPass(SyntheticCountsPropagation());
MPM.addPass(buildInlinerPipeline(Level, Phase));
+ if (DoNotRerunFunctionPasses)
+ MPM.addPass(createModuleToFunctionPassAdaptor(
+ InvalidateAnalysisPass<FunctionStatusAnalysis>()));
if (EnableMemProfiler && Phase != ThinOrFullLTOPhase::ThinLTOPreLink) {
MPM.addPass(createModuleToFunctionPassAdaptor(MemProfilerPass()));
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 4e9bfd21311f..01c7d2b4588b 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -174,6 +174,7 @@ FUNCTION_ANALYSIS("targetir",
FUNCTION_ANALYSIS("verify", VerifierAnalysis())
FUNCTION_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC))
FUNCTION_ANALYSIS("divergence", DivergenceAnalysis())
+FUNCTION_ANALYSIS("func-status", FunctionStatusAnalysis())
#ifndef FUNCTION_ALIAS_ANALYSIS
#define FUNCTION_ALIAS_ANALYSIS(NAME, CREATE_PASS) \
diff --git a/llvm/test/Other/new-pass-manager-cgscc-fct-proxy.ll b/llvm/test/Other/new-pass-manager-cgscc-fct-proxy.ll
new file mode 100644
index 000000000000..62b954cb754d
--- /dev/null
+++ b/llvm/test/Other/new-pass-manager-cgscc-fct-proxy.ll
@@ -0,0 +1,44 @@
+; RUN: opt %s -disable-verify -disable-output -passes='default<O2>' -debug-pass-manager -cgscc-npm-no-fp-rerun=1 \
+; RUN: 2>&1 | FileCheck %s -check-prefixes=CHECK,NOREPS
+; RUN: opt %s -disable-verify -disable-output -passes='default<O2>' -debug-pass-manager -cgscc-npm-no-fp-rerun=0 \
+; RUN: 2>&1 | FileCheck %s -check-prefixes=CHECK,REPS
+
+; Pre-attribute the functions to avoid the PostOrderFunctionAttrsPass cause
+; changes (and keep the test simple)
+attributes #0 = { nofree noreturn nosync nounwind readnone }
+
+define void @f1(void()* %p) {
+ call void %p()
+ ret void
+}
+
+define void @f2() #0 {
+ call void @f1(void()* @f2)
+ call void @f3()
+ ret void
+}
+
+define void @f3() #0 {
+ call void @f2()
+ ret void
+}
+
+; CHECK: Starting CGSCC pass manager run
+; CHECK-NEXT: Running pass: InlinerPass on (f1)
+; NOREPS: Running analysis: FunctionStatusAnalysis on f1
+; CHECK: Finished CGSCC pass manager run.
+
+; CHECK: Starting CGSCC pass manager run
+; CHECK-NEXT: Running pass: InlinerPass on (f2, f3)
+; NOREPS: Running analysis: FunctionStatusAnalysis on f2
+; CHECK: Finished CGSCC pass manager run.
+
+; CHECK: Starting CGSCC pass manager run
+; CHECK-NEXT: Running pass: InlinerPass on (f2)
+; REPS: Running pass: SROA on f2
+; NOREPS-NOT: Running pass: SROA on f2
+; CHECK: Finished CGSCC pass manager run.
+
+; CHECK: Starting CGSCC pass manager run.
+; CHECK-NEXT: Running pass: InlinerPass on (f3)
+; CHECK: Running pass: SROA on f3
diff --git a/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp b/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp
index ceaeaaf83e5d..4ad449f6fa7b 100644
--- a/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp
+++ b/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp
@@ -252,6 +252,7 @@ class CGSCCPassManagerTest : public ::testing::Test {
" call void @h1()\n"
" ret void\n"
"}\n")) {
+ FAM.registerPass([&] { return FunctionStatusAnalysis(); });
FAM.registerPass([&] { return TargetLibraryAnalysis(); });
MAM.registerPass([&] { return LazyCallGraphAnalysis(); });
MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
More information about the llvm-commits
mailing list