Skip to content

Commit

Permalink
[NewPM] Make eager analysis invalidation per-adaptor
Browse files Browse the repository at this point in the history
Follow-up change to D111575.
We don't need eager invalidation on every adaptor. Most notably,
adaptors running passes that use very few analyses, or passes that
purely invalidate specific analyses.

Also allow testing of this via a pipeline string
"function<eager-inv>()".

The compile time/memory impact of this is very comparable to D111575.
https://llvm-compile-time-tracker.com/compare.php?from=9a2eec512a29df45c90c2fcb741e9d5c693b1383&to=b9f20bcdea138060967d95a98eab87ce725b22bb&stat=instructions

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D113196
  • Loading branch information
aeubanks committed Nov 5, 2021
1 parent 41860e6 commit 7175886
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 50 deletions.
19 changes: 13 additions & 6 deletions llvm/include/llvm/Analysis/CGSCCPassManager.h
Expand Up @@ -477,11 +477,12 @@ class CGSCCToFunctionPassAdaptor
public:
using PassConceptT = detail::PassConcept<Function, FunctionAnalysisManager>;

explicit CGSCCToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass)
: Pass(std::move(Pass)) {}
explicit CGSCCToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass,
bool EagerlyInvalidate)
: Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate) {}

CGSCCToFunctionPassAdaptor(CGSCCToFunctionPassAdaptor &&Arg)
: Pass(std::move(Arg.Pass)) {}
: Pass(std::move(Arg.Pass)), EagerlyInvalidate(Arg.EagerlyInvalidate) {}

friend void swap(CGSCCToFunctionPassAdaptor &LHS,
CGSCCToFunctionPassAdaptor &RHS) {
Expand All @@ -499,7 +500,10 @@ class CGSCCToFunctionPassAdaptor

void printPipeline(raw_ostream &OS,
function_ref<StringRef(StringRef)> MapClassName2PassName) {
OS << "function(";
OS << "function";
if (EagerlyInvalidate)
OS << "<eager-inv>";
OS << "(";
Pass->printPipeline(OS, MapClassName2PassName);
OS << ")";
}
Expand All @@ -508,21 +512,24 @@ class CGSCCToFunctionPassAdaptor

private:
std::unique_ptr<PassConceptT> Pass;
bool EagerlyInvalidate;
};

/// A function to deduce a function pass type and wrap it in the
/// templated adaptor.
template <typename FunctionPassT>
CGSCCToFunctionPassAdaptor
createCGSCCToFunctionPassAdaptor(FunctionPassT &&Pass) {
createCGSCCToFunctionPassAdaptor(FunctionPassT &&Pass,
bool EagerlyInvalidate = false) {
using PassModelT =
detail::PassModel<Function, FunctionPassT, PreservedAnalyses,
FunctionAnalysisManager>;
// Do not use make_unique, it causes too many template instantiations,
// causing terrible compile times.
return CGSCCToFunctionPassAdaptor(
std::unique_ptr<CGSCCToFunctionPassAdaptor::PassConceptT>(
new PassModelT(std::forward<FunctionPassT>(Pass))));
new PassModelT(std::forward<FunctionPassT>(Pass))),
EagerlyInvalidate);
}

/// A helper that repeats an SCC pass each time an indirect call is refined to
Expand Down
12 changes: 8 additions & 4 deletions llvm/include/llvm/IR/PassManager.h
Expand Up @@ -1204,8 +1204,9 @@ class ModuleToFunctionPassAdaptor
public:
using PassConceptT = detail::PassConcept<Function, FunctionAnalysisManager>;

explicit ModuleToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass)
: Pass(std::move(Pass)) {}
explicit ModuleToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass,
bool EagerlyInvalidate)
: Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate) {}

/// Runs the function pass across every function in the module.
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
Expand All @@ -1216,21 +1217,24 @@ class ModuleToFunctionPassAdaptor

private:
std::unique_ptr<PassConceptT> Pass;
bool EagerlyInvalidate;
};

/// A function to deduce a function pass type and wrap it in the
/// templated adaptor.
template <typename FunctionPassT>
ModuleToFunctionPassAdaptor
createModuleToFunctionPassAdaptor(FunctionPassT &&Pass) {
createModuleToFunctionPassAdaptor(FunctionPassT &&Pass,
bool EagerlyInvalidate = false) {
using PassModelT =
detail::PassModel<Function, FunctionPassT, PreservedAnalyses,
FunctionAnalysisManager>;
// Do not use make_unique, it causes too many template instantiations,
// causing terrible compile times.
return ModuleToFunctionPassAdaptor(
std::unique_ptr<ModuleToFunctionPassAdaptor::PassConceptT>(
new PassModelT(std::forward<FunctionPassT>(Pass))));
new PassModelT(std::forward<FunctionPassT>(Pass))),
EagerlyInvalidate);
}

/// A utility pass template to force an analysis result to be available.
Expand Down
9 changes: 9 additions & 0 deletions llvm/include/llvm/Passes/PassBuilder.h
Expand Up @@ -73,6 +73,15 @@ class PipelineTuningOptions {
/// Tuning option to enable/disable function merging. Its default value is
/// false.
bool MergeFunctions;

// Experimental option to eagerly invalidate more analyses. This has the
// potential to decrease max memory usage in exchange for more compile time.
// This may affect codegen due to either passes using analyses only when
// cached, or invalidating and recalculating an analysis that was
// stale/imprecise but still valid. Currently this invalidates all function
// analyses after various module->function or cgscc->function adaptors in the
// default pipelines.
bool EagerlyInvalidateAnalyses;
};

/// This class provides access to building LLVM's passes.
Expand Down
5 changes: 1 addition & 4 deletions llvm/lib/Analysis/CGSCCPassManager.cpp
Expand Up @@ -38,8 +38,6 @@ using namespace llvm;
// Explicit template instantiations and specialization definitions for core
// template typedefs.
namespace llvm {
extern cl::opt<bool> EagerlyInvalidateAnalyses;

static cl::opt<bool> AbortOnMaxDevirtIterationsReached(
"abort-on-max-devirt-iterations-reached",
cl::desc("Abort when the max iterations for devirtualization CGSCC repeat "
Expand Down Expand Up @@ -557,8 +555,7 @@ PreservedAnalyses CGSCCToFunctionPassAdaptor::run(LazyCallGraph::SCC &C,
// We know that the function pass couldn't have invalidated any other
// function's analyses (that's the contract of a function pass), so
// directly handle the function analysis manager's invalidation here.
FAM.invalidate(F, EagerlyInvalidateAnalyses ? PreservedAnalyses::none()
: PassPA);
FAM.invalidate(F, EagerlyInvalidate ? PreservedAnalyses::none() : PassPA);

// Then intersect the preserved set so that invalidation of module
// analyses will eventually occur when the module pass completes.
Expand Down
19 changes: 5 additions & 14 deletions llvm/lib/IR/PassManager.cpp
Expand Up @@ -15,17 +15,6 @@
using namespace llvm;

namespace llvm {
// Experimental option to eagerly invalidate more analyses. This has the
// potential to decrease max memory usage in exchange for more compile time.
// This may affect codegen due to either passes using analyses only when
// cached, or invalidating and recalculating an analysis that was
// stale/imprecise but still valid. Currently this invalidates all function
// analyses after a module->function or cgscc->function adaptor.
// TODO: make this a PipelineTuningOption.
cl::opt<bool> EagerlyInvalidateAnalyses(
"eagerly-invalidate-analyses", cl::init(false), cl::Hidden,
cl::desc("Eagerly invalidate more analyses in default pipelines"));

// Explicit template instantiations and specialization defininitions for core
// template typedefs.
template class AllAnalysesOn<Module>;
Expand Down Expand Up @@ -105,7 +94,10 @@ bool FunctionAnalysisManagerModuleProxy::Result::invalidate(

void ModuleToFunctionPassAdaptor::printPipeline(
raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
OS << "function(";
OS << "function";
if (EagerlyInvalidate)
OS << "<eager-inv>";
OS << "(";
Pass->printPipeline(OS, MapClassName2PassName);
OS << ")";
}
Expand Down Expand Up @@ -141,8 +133,7 @@ PreservedAnalyses ModuleToFunctionPassAdaptor::run(Module &M,
// We know that the function pass couldn't have invalidated any other
// function's analyses (that's the contract of a function pass), so
// directly handle the function analysis manager's invalidation here.
FAM.invalidate(F, EagerlyInvalidateAnalyses ? PreservedAnalyses::none()
: PassPA);
FAM.invalidate(F, EagerlyInvalidate ? PreservedAnalyses::none() : PassPA);

// Then intersect the preserved set so that invalidation of module
// analyses will eventually occur when the module pass completes.
Expand Down
16 changes: 9 additions & 7 deletions llvm/lib/Passes/PassBuilder.cpp
Expand Up @@ -832,7 +832,7 @@ static bool isModulePassName(StringRef Name, CallbacksT &Callbacks) {
return true;
if (Name == "cgscc")
return true;
if (Name == "function")
if (Name == "function" || Name == "function<eager-inv>")
return true;

// Explicitly handle custom-parsed pass names.
Expand All @@ -858,7 +858,7 @@ static bool isCGSCCPassName(StringRef Name, CallbacksT &Callbacks) {
// Explicitly handle pass manager names.
if (Name == "cgscc")
return true;
if (Name == "function")
if (Name == "function" || Name == "function<eager-inv>")
return true;

// Explicitly handle custom-parsed pass names.
Expand All @@ -884,7 +884,7 @@ static bool isCGSCCPassName(StringRef Name, CallbacksT &Callbacks) {
template <typename CallbacksT>
static bool isFunctionPassName(StringRef Name, CallbacksT &Callbacks) {
// Explicitly handle pass manager names.
if (Name == "function")
if (Name == "function" || Name == "function<eager-inv>")
return true;
if (Name == "loop" || Name == "loop-mssa")
return true;
Expand Down Expand Up @@ -1013,11 +1013,12 @@ Error PassBuilder::parseModulePass(ModulePassManager &MPM,
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));
return Error::success();
}
if (Name == "function") {
if (Name == "function" || Name == "function<eager-inv>") {
FunctionPassManager FPM;
if (auto Err = parseFunctionPassPipeline(FPM, InnerPipeline))
return Err;
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM),
Name != "function"));
return Error::success();
}
if (auto Count = parseRepeatPassName(Name)) {
Expand Down Expand Up @@ -1180,12 +1181,13 @@ Error PassBuilder::parseCGSCCPass(CGSCCPassManager &CGPM,
CGPM.addPass(std::move(NestedCGPM));
return Error::success();
}
if (Name == "function") {
if (Name == "function" || Name == "function<eager-inv>") {
FunctionPassManager FPM;
if (auto Err = parseFunctionPassPipeline(FPM, InnerPipeline))
return Err;
// Add the nested pass manager with the appropriate adaptor.
CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM)));
CGPM.addPass(
createCGSCCToFunctionPassAdaptor(std::move(FPM), Name != "function"));
return Error::success();
}
if (auto Count = parseRepeatPassName(Name)) {
Expand Down
35 changes: 25 additions & 10 deletions llvm/lib/Passes/PassBuilderPipelines.cpp
Expand Up @@ -162,6 +162,10 @@ 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> EnableEagerlyInvalidateAnalyses(
"eagerly-invalidate-analyses", cl::init(false), cl::Hidden,
cl::desc("Eagerly invalidate more analyses in default pipelines"));

PipelineTuningOptions::PipelineTuningOptions() {
LoopInterleaving = true;
LoopVectorization = true;
Expand All @@ -172,6 +176,7 @@ PipelineTuningOptions::PipelineTuningOptions() {
LicmMssaNoAccForPromotionCap = SetLicmMssaNoAccForPromotionCap;
CallGraphProfile = true;
MergeFunctions = false;
EagerlyInvalidateAnalyses = EnableEagerlyInvalidateAnalyses;
}

namespace llvm {
Expand Down Expand Up @@ -596,7 +601,8 @@ void PassBuilder::addPGOInstrPasses(ModulePassManager &MPM,
FPM.addPass(InstCombinePass()); // Combine silly sequences.
invokePeepholeEPCallbacks(FPM, Level);

CGPipeline.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM)));
CGPipeline.addPass(createCGSCCToFunctionPassAdaptor(
std::move(FPM), PTO.EagerlyInvalidateAnalyses));

MPM.addPass(std::move(MIWP));

Expand All @@ -623,7 +629,8 @@ void PassBuilder::addPGOInstrPasses(ModulePassManager &MPM,
FPM.addPass(createFunctionToLoopPassAdaptor(
LoopRotatePass(Level != OptimizationLevel::Oz), /*UseMemorySSA=*/false,
/*UseBlockFrequencyInfo=*/false));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM),
PTO.EagerlyInvalidateAnalyses));

// Add the profile lowering pass.
InstrProfOptions Options;
Expand Down Expand Up @@ -723,7 +730,8 @@ PassBuilder::buildInlinerPipeline(OptimizationLevel Level,
// Lastly, add the core function simplification pipeline nested inside the
// CGSCC walk.
MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor(
buildFunctionSimplificationPipeline(Level, Phase)));
buildFunctionSimplificationPipeline(Level, Phase),
PTO.EagerlyInvalidateAnalyses));

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

Expand Down Expand Up @@ -792,7 +800,8 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
// FIXME: revisit how SampleProfileLoad/Inliner/ICP is structured.
if (LoadSampleProfile)
EarlyFPM.addPass(InstCombinePass());
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(EarlyFPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(EarlyFPM),
PTO.EagerlyInvalidateAnalyses));

if (LoadSampleProfile) {
// Annotate sample profile right after early FPM to ensure freshness of
Expand Down Expand Up @@ -866,7 +875,8 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
invokePeepholeEPCallbacks(GlobalCleanupPM, Level);

GlobalCleanupPM.addPass(SimplifyCFGPass());
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(GlobalCleanupPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(GlobalCleanupPM),
PTO.EagerlyInvalidateAnalyses));

// Add all the requested passes for instrumentation PGO, if requested.
if (PGOOpt && Phase != ThinOrFullLTOPhase::ThinLTOPostLink &&
Expand Down Expand Up @@ -1154,7 +1164,8 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
OptimizePM.addPass(CoroCleanupPass());

// Add the core optimizing pipeline.
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizePM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(OptimizePM),
PTO.EagerlyInvalidateAnalyses));

for (auto &C : OptimizerLastEPCallbacks)
C(MPM, Level);
Expand Down Expand Up @@ -1397,7 +1408,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
if (Level.getSpeedupLevel() > 1) {
FunctionPassManager EarlyFPM;
EarlyFPM.addPass(CallSiteSplittingPass());
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(EarlyFPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(
std::move(EarlyFPM), PTO.EagerlyInvalidateAnalyses));

// Indirect call promotion. This should promote all the targets that are
// left by the earlier promotion pass that promotes intra-module targets.
Expand Down Expand Up @@ -1473,7 +1485,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
PeepholeFPM.addPass(InstCombinePass());
invokePeepholeEPCallbacks(PeepholeFPM, Level);

MPM.addPass(createModuleToFunctionPassAdaptor(std::move(PeepholeFPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(PeepholeFPM),
PTO.EagerlyInvalidateAnalyses));

// Note: historically, the PruneEH pass was run first to deduce nounwind and
// generally clean up exception handling overhead. It isn't clear this is
Expand Down Expand Up @@ -1520,7 +1533,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
FPM.addPass(TailCallElimPass());

// Run a few AA driver optimizations here and now to cleanup the code.
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM),
PTO.EagerlyInvalidateAnalyses));

MPM.addPass(
createModuleToPostOrderCGSCCPassAdaptor(PostOrderFunctionAttrsPass()));
Expand Down Expand Up @@ -1577,7 +1591,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,

invokePeepholeEPCallbacks(MainFPM, Level);
MainFPM.addPass(JumpThreadingPass(/*InsertFreezeWhenUnfoldingSelect*/ true));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(MainFPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(MainFPM),
PTO.EagerlyInvalidateAnalyses));

// Lower type metadata and the type.test intrinsic. This pass supports
// clang's control flow integrity mechanisms (-fsanitize=cfi*) and needs
Expand Down
29 changes: 24 additions & 5 deletions llvm/test/Other/new-pm-eager-invalidate.ll
@@ -1,8 +1,27 @@
; RUN: opt -disable-verify -debug-pass-manager -passes='function(require<no-op-function>)' -disable-output -eagerly-invalidate-analyses %s 2>&1 | FileCheck %s
; RUN: opt -disable-verify -debug-pass-manager -passes='cgscc(function(require<no-op-function>))' -disable-output -eagerly-invalidate-analyses %s 2>&1 | FileCheck %s
; RUN: opt -disable-verify -debug-pass-manager -passes='function(require<no-op-function>)' -disable-output %s 2>&1 | FileCheck %s --check-prefix=NORMAL
; RUN: opt -disable-verify -debug-pass-manager -passes='cgscc(function(require<no-op-function>))' -disable-output %s 2>&1 | FileCheck %s --check-prefix=NORMAL
; RUN: opt -disable-verify -debug-pass-manager -passes='function<eager-inv>(require<no-op-function>)' -disable-output %s 2>&1 | FileCheck %s --check-prefix=EAGER
; RUN: opt -disable-verify -debug-pass-manager -passes='cgscc(function<eager-inv>(require<no-op-function>))' -disable-output %s 2>&1 | FileCheck %s --check-prefix=EAGER

; CHECK: Invalidating analysis: NoOpFunctionAnalysis
; RUN: opt -disable-verify -debug-pass-manager -passes='default<O2>' -disable-output %s 2>&1 | FileCheck %s --check-prefix=PIPELINE
; RUN: opt -disable-verify -debug-pass-manager -passes='default<O2>' -eagerly-invalidate-analyses -disable-output %s 2>&1 | FileCheck %s --check-prefix=PIPELINE-EAGER

define void @foo() {
unreachable
; NORMAL-NOT: Invalidating analysis: NoOpFunctionAnalysis
; EAGER: Invalidating analysis: NoOpFunctionAnalysis
; PIPELINE-NOT: Invalidating analysis: DominatorTreeAnalysis
; PIPELINE-EAGER: Invalidating analysis: DominatorTreeAnalysis

declare void @bar() local_unnamed_addr

define void @foo(i32 %n) local_unnamed_addr {
entry:
br label %loop
loop:
%iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
%iv.next = add i32 %iv, 1
tail call void @bar()
%cmp = icmp eq i32 %iv, %n
br i1 %cmp, label %exit, label %loop
exit:
ret void
}
3 changes: 3 additions & 0 deletions llvm/test/Other/new-pm-print-pipeline.ll
Expand Up @@ -66,3 +66,6 @@

; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='scc-oz-module-inliner' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-21
; CHECK-21: require<globals-aa>,function(invalidate<aa>),require<profile-summary>,cgscc(devirt<4>(inline<only-mandatory>,inline,{{.*}},instcombine{{.*}}))

; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='cgscc(function<eager-inv>(no-op-function)),function<eager-inv>(no-op-function)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-22
; CHECK-22: cgscc(function<eager-inv>(no-op-function)),function<eager-inv>(no-op-function)

0 comments on commit 7175886

Please sign in to comment.