Skip to content

Commit e3e25b5

Browse files
committed
[NewPM] Add option to prevent rerunning function pipeline on functions in CGSCC adaptor
In a CGSCC pass manager, we may visit the same function multiple times due to SCC mutations. In the inliner pipeline, this results in running the function simplification pipeline on a function multiple times even if it hasn't been changed since the last function simplification pipeline run. We use a newly introduced analysis to keep track of whether or not a function has changed since the last time the function simplification pipeline has run on it. If we see this analysis available for a function in a CGSCCToFunctionPassAdaptor, we skip running the function passes on the function. The analysis is queried at the end of the function passes so that it's available after the first time the function simplification pipeline runs on a function. This is a per-adaptor option so it doesn't apply to every adaptor. The goal of this is to improve compile times. However, currently we can't turn this on by default at least for the higher optimization levels since the function simplification pipeline is not robust enough to be idempotent in many cases, resulting in performance regressions if we stop running the function simplification pipeline on a function multiple times. We may be able to turn this on for -O1 in the near future, but turning this on for higher optimization levels would require more investment in the function simplification pipeline. Heavily inspired by D98103. Example compile time improvements with flag turned on: https://llvm-compile-time-tracker.com/compare.php?from=998dc4a5d3491d2ae8cbe742d2e13bc1b0cacc5f&to=5c27c913687d3d5559ef3ab42b5a3d513531d61c&stat=instructions Reviewed By: asbirlea, nikic Differential Revision: https://reviews.llvm.org/D113947
1 parent 2e7f12d commit e3e25b5

File tree

7 files changed

+88
-7
lines changed

7 files changed

+88
-7
lines changed

llvm/include/llvm/Analysis/CGSCCPassManager.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -478,11 +478,13 @@ class CGSCCToFunctionPassAdaptor
478478
using PassConceptT = detail::PassConcept<Function, FunctionAnalysisManager>;
479479

480480
explicit CGSCCToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass,
481-
bool EagerlyInvalidate)
482-
: Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate) {}
481+
bool EagerlyInvalidate, bool NoRerun)
482+
: Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate),
483+
NoRerun(NoRerun) {}
483484

484485
CGSCCToFunctionPassAdaptor(CGSCCToFunctionPassAdaptor &&Arg)
485-
: Pass(std::move(Arg.Pass)), EagerlyInvalidate(Arg.EagerlyInvalidate) {}
486+
: Pass(std::move(Arg.Pass)), EagerlyInvalidate(Arg.EagerlyInvalidate),
487+
NoRerun(Arg.NoRerun) {}
486488

487489
friend void swap(CGSCCToFunctionPassAdaptor &LHS,
488490
CGSCCToFunctionPassAdaptor &RHS) {
@@ -513,14 +515,16 @@ class CGSCCToFunctionPassAdaptor
513515
private:
514516
std::unique_ptr<PassConceptT> Pass;
515517
bool EagerlyInvalidate;
518+
bool NoRerun;
516519
};
517520

518521
/// A function to deduce a function pass type and wrap it in the
519522
/// templated adaptor.
520523
template <typename FunctionPassT>
521524
CGSCCToFunctionPassAdaptor
522525
createCGSCCToFunctionPassAdaptor(FunctionPassT &&Pass,
523-
bool EagerlyInvalidate = false) {
526+
bool EagerlyInvalidate = false,
527+
bool NoRerun = false) {
524528
using PassModelT =
525529
detail::PassModel<Function, FunctionPassT, PreservedAnalyses,
526530
FunctionAnalysisManager>;
@@ -529,9 +533,23 @@ createCGSCCToFunctionPassAdaptor(FunctionPassT &&Pass,
529533
return CGSCCToFunctionPassAdaptor(
530534
std::unique_ptr<CGSCCToFunctionPassAdaptor::PassConceptT>(
531535
new PassModelT(std::forward<FunctionPassT>(Pass))),
532-
EagerlyInvalidate);
536+
EagerlyInvalidate, NoRerun);
533537
}
534538

539+
// A marker to determine if function passes should be run on a function within a
540+
// CGSCCToFunctionPassAdaptor. This is used to prevent running an expensive
541+
// function pass (manager) on a function multiple times if SCC mutations cause a
542+
// function to be visited multiple times and the function is not modified by
543+
// other SCC passes.
544+
class ShouldNotRunFunctionPassesAnalysis
545+
: public AnalysisInfoMixin<ShouldNotRunFunctionPassesAnalysis> {
546+
public:
547+
static AnalysisKey Key;
548+
struct Result {};
549+
550+
Result run(Function &F, FunctionAnalysisManager &FAM) { return Result(); }
551+
};
552+
535553
/// A helper that repeats an SCC pass each time an indirect call is refined to
536554
/// a direct call by that pass.
537555
///

llvm/include/llvm/Transforms/IPO/Inliner.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,27 @@ class ModuleInlinerWrapperPass
132132
/// before run is called, as part of pass pipeline building.
133133
CGSCCPassManager &getPM() { return PM; }
134134

135-
/// Allow adding module-level passes benefiting the contained CGSCC passes.
135+
/// Add a module pass that runs before the CGSCC passes.
136136
template <class T> void addModulePass(T Pass) {
137137
MPM.addPass(std::move(Pass));
138138
}
139139

140+
/// Add a module pass that runs after the CGSCC passes.
141+
template <class T> void addLateModulePass(T Pass) {
142+
AfterCGMPM.addPass(std::move(Pass));
143+
}
144+
140145
void printPipeline(raw_ostream &OS,
141146
function_ref<StringRef(StringRef)> MapClassName2PassName);
142147

143148
private:
144149
const InlineParams Params;
145150
const InliningAdvisorMode Mode;
146151
const unsigned MaxDevirtIterations;
152+
// TODO: Clean this up so we only have one ModulePassManager.
147153
CGSCCPassManager PM;
148154
ModulePassManager MPM;
155+
ModulePassManager AfterCGMPM;
149156
};
150157
} // end namespace llvm
151158

llvm/lib/Analysis/CGSCCPassManager.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ static cl::opt<bool> AbortOnMaxDevirtIterationsReached(
4343
cl::desc("Abort when the max iterations for devirtualization CGSCC repeat "
4444
"pass is reached"));
4545

46+
AnalysisKey ShouldNotRunFunctionPassesAnalysis::Key;
47+
4648
// Explicit instantiations for the core proxy templates.
4749
template class AllAnalysesOn<LazyCallGraph::SCC>;
4850
template class AnalysisManager<LazyCallGraph::SCC, LazyCallGraph &>;
@@ -540,6 +542,9 @@ PreservedAnalyses CGSCCToFunctionPassAdaptor::run(LazyCallGraph::SCC &C,
540542

541543
Function &F = N->getFunction();
542544

545+
if (NoRerun && FAM.getCachedResult<ShouldNotRunFunctionPassesAnalysis>(F))
546+
continue;
547+
543548
PassInstrumentation PI = FAM.getResult<PassInstrumentationAnalysis>(F);
544549
if (!PI.runBeforePass<Function>(*Pass, F))
545550
continue;
@@ -556,6 +561,8 @@ PreservedAnalyses CGSCCToFunctionPassAdaptor::run(LazyCallGraph::SCC &C,
556561
// function's analyses (that's the contract of a function pass), so
557562
// directly handle the function analysis manager's invalidation here.
558563
FAM.invalidate(F, EagerlyInvalidate ? PreservedAnalyses::none() : PassPA);
564+
if (NoRerun)
565+
(void)FAM.getResult<ShouldNotRunFunctionPassesAnalysis>(F);
559566

560567
// Then intersect the preserved set so that invalidation of module
561568
// analyses will eventually occur when the module pass completes.

llvm/lib/Passes/PassBuilderPipelines.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,13 @@ static cl::opt<bool> EnableEagerlyInvalidateAnalyses(
171171
"eagerly-invalidate-analyses", cl::init(true), cl::Hidden,
172172
cl::desc("Eagerly invalidate more analyses in default pipelines"));
173173

174+
static cl::opt<bool> EnableNoRerunSimplificationPipeline(
175+
"enable-no-rerun-simplification-pipeline", cl::init(false), cl::Hidden,
176+
cl::desc(
177+
"Prevent running the simplification pipeline on a function more "
178+
"than once in the case that SCC mutations cause a function to be "
179+
"visited multiple times as long as the function has not been changed"));
180+
174181
PipelineTuningOptions::PipelineTuningOptions() {
175182
LoopInterleaving = true;
176183
LoopVectorization = true;
@@ -736,10 +743,14 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
736743
// CGSCC walk.
737744
MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(
738745
buildFunctionSimplificationPipeline(Level, Phase),
739-
PTO.EagerlyInvalidateAnalyses));
746+
PTO.EagerlyInvalidateAnalyses, EnableNoRerunSimplificationPipeline));
740747

741748
MainCGPipeline.addPass(CoroSplitPass(Level != OptimizationLevel::O0));
742749

750+
if (EnableNoRerunSimplificationPipeline)
751+
MIWP.addLateModulePass(createModuleToFunctionPassAdaptor(
752+
InvalidateAnalysisPass<ShouldNotRunFunctionPassesAnalysis>()));
753+
743754
return MIWP;
744755
}
745756

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ FUNCTION_ANALYSIS("regions", RegionInfoAnalysis())
201201
FUNCTION_ANALYSIS("no-op-function", NoOpFunctionAnalysis())
202202
FUNCTION_ANALYSIS("opt-remark-emit", OptimizationRemarkEmitterAnalysis())
203203
FUNCTION_ANALYSIS("scalar-evolution", ScalarEvolutionAnalysis())
204+
FUNCTION_ANALYSIS("should-not-run-function-passes", ShouldNotRunFunctionPassesAnalysis())
204205
FUNCTION_ANALYSIS("stack-safety-local", StackSafetyAnalysis())
205206
FUNCTION_ANALYSIS("targetlibinfo", TargetLibraryAnalysis())
206207
FUNCTION_ANALYSIS("targetir",

llvm/lib/Transforms/IPO/Inliner.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,8 @@ PreservedAnalyses ModuleInlinerWrapperPass::run(Module &M,
11091109
else
11101110
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(
11111111
createDevirtSCCRepeatedPass(std::move(PM), MaxDevirtIterations)));
1112+
1113+
MPM.addPass(std::move(AfterCGMPM));
11121114
MPM.run(M, MAM);
11131115

11141116
// Discard the InlineAdvisor, a subsequent inlining session should construct
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
; RUN: opt < %s -passes='default<O1>' -disable-output -debug-pass-manager=verbose 2>&1 | FileCheck %s --check-prefixes=CHECK,NORMAL
2+
; RUN: opt < %s -passes='default<O2>' -disable-output -debug-pass-manager=verbose 2>&1 | FileCheck %s --check-prefixes=CHECK,NORMAL
3+
; RUN: opt < %s -passes='default<O1>' -disable-output -debug-pass-manager=verbose -enable-no-rerun-simplification-pipeline=1 2>&1 | FileCheck %s --check-prefixes=CHECK,NORERUN
4+
; RUN: opt < %s -passes='default<O2>' -disable-output -debug-pass-manager=verbose -enable-no-rerun-simplification-pipeline=1 2>&1 | FileCheck %s --check-prefixes=CHECK,NORERUN
5+
6+
; BDCE only runs once in the function simplification pipeline and nowhere else so we use that to check for reruns.
7+
8+
; CHECK: PassManager{{.*}}SCC{{.*}} on (f1)
9+
; CHECK: Running pass: BDCEPass on f1
10+
; CHECK: PassManager{{.*}}SCC{{.*}} on (f2, f3)
11+
; CHECK: Running pass: BDCEPass on f2
12+
; CHECK-NOT: BDCEPass
13+
; CHECK: PassManager{{.*}}SCC{{.*}} on (f2)
14+
; NORMAL: Running pass: BDCEPass on f2
15+
; NORERUN-NOT: Running pass: BDCEPass on f2
16+
; CHECK: PassManager{{.*}}SCC{{.*}} on (f3)
17+
; CHECK: Running pass: BDCEPass on f3
18+
19+
define void @f1(void()* %p) alwaysinline {
20+
call void %p()
21+
ret void
22+
}
23+
24+
define void @f2() #0 {
25+
call void @f1(void()* @f2)
26+
call void @f3()
27+
ret void
28+
}
29+
30+
define void @f3() #0 {
31+
call void @f2()
32+
ret void
33+
}
34+
35+
attributes #0 = { nofree noreturn nosync nounwind readnone noinline }

0 commit comments

Comments
 (0)