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,31 @@ 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 { + /// The analysis result is cached explicitly at the end of the passes + /// managed by FunctionAnalysisManagerCGSCCProxy. + /// The CGSCC proxy will invalidate analyses when the CG is invalidated, but + /// that doesn't necessarily mean a function was invalidated since the last + /// time the function passes were run. This analysis is explicitly + /// invalidated in updateCGAndAnalysisManagerForPass when a SCC pass signals + /// a function change. So this analysis result is only invalidated when + /// precisely the function is invalidated. + bool invalidate(Function &F, const PreservedAnalyses &PA, + FunctionAnalysisManager::Invalidator &Inv) { + return false; + } + }; + + 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 @@ -35,6 +35,11 @@ using namespace llvm; +static LazyCallGraph::SCC &updateCGAndAnalysisManagerForPass( + LazyCallGraph &G, LazyCallGraph::SCC &InitialC, LazyCallGraph::Node &N, + CGSCCAnalysisManager &AM, CGSCCUpdateResult &UR, + FunctionAnalysisManager &FAM, bool FunctionPass, bool InvalidateSA = true); + // Explicit template instantiations and specialization definitions for core // template typedefs. namespace llvm { @@ -44,6 +49,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 +554,15 @@ 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. + // The assumption here is that CGSCC passes are well-behaved, in that, upon + // changing a function, they call updateCGAndAnalysisManagerForCGSCCPass. + if (FAM.getCachedResult(F)) + continue; PassInstrumentation PI = FAM.getResult(F); if (!PI.runBeforePass(*Pass, F)) @@ -574,8 +590,8 @@ // a smaller, more refined SCC. auto PAC = PA.getChecker(); if (!PAC.preserved() && !PAC.preservedSet>()) { - CurrentC = &updateCGAndAnalysisManagerForFunctionPass(CG, *CurrentC, *N, - AM, UR, FAM); + CurrentC = &updateCGAndAnalysisManagerForPass(CG, *CurrentC, *N, AM, UR, + FAM, true, false); assert(CG.lookupSCC(*N) == CurrentC && "Current SCC not updated to the SCC containing the current node!"); } @@ -892,7 +908,11 @@ static LazyCallGraph::SCC &updateCGAndAnalysisManagerForPass( LazyCallGraph &G, LazyCallGraph::SCC &InitialC, LazyCallGraph::Node &N, CGSCCAnalysisManager &AM, CGSCCUpdateResult &UR, - FunctionAnalysisManager &FAM, bool FunctionPass) { + FunctionAnalysisManager &FAM, bool FunctionPass, bool InvalidateSA) { + + if (InvalidateSA) + FAM.invalidate(N.getFunction()); + using Node = LazyCallGraph::Node; using Edge = LazyCallGraph::Edge; using SCC = LazyCallGraph::SCC; 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 @@ -278,6 +278,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; @@ -1016,8 +1021,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; } 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 @@ -172,6 +172,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,39 @@ +; 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 + +define void @f1(void()* %p) { + call void %p() + ret void +} + +define void @f2() { + call void @f1(void()* @f2) + call void @f3() + ret void +} + +define void @f3() { + 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. +; CHCEK-NEXT: Running pass: InlinerPass on (f3) \ No newline at end of file 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); });