diff --git a/llvm/include/llvm/Analysis/CGSCCPassManager.h b/llvm/include/llvm/Analysis/CGSCCPassManager.h --- a/llvm/include/llvm/Analysis/CGSCCPassManager.h +++ b/llvm/include/llvm/Analysis/CGSCCPassManager.h @@ -487,6 +487,18 @@ std::unique_ptr 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 { +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 diff --git a/llvm/lib/Analysis/CGSCCPassManager.cpp b/llvm/lib/Analysis/CGSCCPassManager.cpp --- a/llvm/lib/Analysis/CGSCCPassManager.cpp +++ b/llvm/lib/Analysis/CGSCCPassManager.cpp @@ -44,6 +44,8 @@ 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; template class AnalysisManager; @@ -547,6 +549,13 @@ 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(F)) + continue; PassInstrumentation PI = FAM.getResult(F); if (!PI.runBeforePass(*Pass, F)) diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -279,6 +279,11 @@ "enable-npm-O3-nontrivial-unswitch", cl::init(true), cl::Hidden, cl::ZeroOrMore, cl::desc("Enable non-trivial loop unswitching for -O3")); +static cl::opt 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 @@ // 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()); + MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(std::move(FSP))); return MIWP; } @@ -1182,6 +1189,9 @@ MPM.addPass(SyntheticCountsPropagation()); MPM.addPass(buildInlinerPipeline(Level, Phase)); + if (DoNotRerunFunctionPasses) + MPM.addPass(createModuleToFunctionPassAdaptor( + InvalidateAnalysisPass())); if (EnableMemProfiler && Phase != ThinOrFullLTOPhase::ThinLTOPreLink) { MPM.addPass(createModuleToFunctionPassAdaptor(MemProfilerPass())); diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -174,6 +174,7 @@ 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 --- /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' -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' -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 --- a/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp +++ b/llvm/unittests/Analysis/CGSCCPassManagerTest.cpp @@ -252,6 +252,7 @@ " 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); });