591 changes: 591 additions & 0 deletions llvm/include/llvm/Analysis/CGSCCPassManager.h

Large diffs are not rendered by default.

167 changes: 167 additions & 0 deletions llvm/lib/Analysis/CGSCCPassManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//===- CGSCCPassManager.cpp - Managing & running CGSCC passes -------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"

using namespace llvm;

static cl::opt<bool>
DebugPM("debug-cgscc-pass-manager", cl::Hidden,
cl::desc("Print CGSCC pass management debugging information"));

PreservedAnalyses CGSCCPassManager::run(LazyCallGraph::SCC *C,
CGSCCAnalysisManager *AM) {
PreservedAnalyses PA = PreservedAnalyses::all();

if (DebugPM)
dbgs() << "Starting CGSCC pass manager run.\n";

for (unsigned Idx = 0, Size = Passes.size(); Idx != Size; ++Idx) {
if (DebugPM)
dbgs() << "Running CGSCC pass: " << Passes[Idx]->name() << "\n";

PreservedAnalyses PassPA = Passes[Idx]->run(C, AM);
if (AM)
AM->invalidate(C, PassPA);
PA.intersect(std::move(PassPA));
}

if (DebugPM)
dbgs() << "Finished CGSCC pass manager run.\n";

return PA;
}

bool CGSCCAnalysisManager::empty() const {
assert(CGSCCAnalysisResults.empty() == CGSCCAnalysisResultLists.empty() &&
"The storage and index of analysis results disagree on how many there "
"are!");
return CGSCCAnalysisResults.empty();
}

void CGSCCAnalysisManager::clear() {
CGSCCAnalysisResults.clear();
CGSCCAnalysisResultLists.clear();
}

CGSCCAnalysisManager::ResultConceptT &
CGSCCAnalysisManager::getResultImpl(void *PassID, LazyCallGraph::SCC *C) {
CGSCCAnalysisResultMapT::iterator RI;
bool Inserted;
std::tie(RI, Inserted) = CGSCCAnalysisResults.insert(std::make_pair(
std::make_pair(PassID, C), CGSCCAnalysisResultListT::iterator()));

// If we don't have a cached result for this function, look up the pass and
// run it to produce a result, which we then add to the cache.
if (Inserted) {
CGSCCAnalysisResultListT &ResultList = CGSCCAnalysisResultLists[C];
ResultList.emplace_back(PassID, lookupPass(PassID).run(C, this));
RI->second = std::prev(ResultList.end());
}

return *RI->second->second;
}

CGSCCAnalysisManager::ResultConceptT *
CGSCCAnalysisManager::getCachedResultImpl(void *PassID,
LazyCallGraph::SCC *C) const {
CGSCCAnalysisResultMapT::const_iterator RI =
CGSCCAnalysisResults.find(std::make_pair(PassID, C));
return RI == CGSCCAnalysisResults.end() ? nullptr : &*RI->second->second;
}

void CGSCCAnalysisManager::invalidateImpl(void *PassID, LazyCallGraph::SCC *C) {
CGSCCAnalysisResultMapT::iterator RI =
CGSCCAnalysisResults.find(std::make_pair(PassID, C));
if (RI == CGSCCAnalysisResults.end())
return;

CGSCCAnalysisResultLists[C].erase(RI->second);
}

void CGSCCAnalysisManager::invalidateImpl(LazyCallGraph::SCC *C,
const PreservedAnalyses &PA) {
// Clear all the invalidated results associated specifically with this
// function.
SmallVector<void *, 8> InvalidatedPassIDs;
CGSCCAnalysisResultListT &ResultsList = CGSCCAnalysisResultLists[C];
for (CGSCCAnalysisResultListT::iterator I = ResultsList.begin(),
E = ResultsList.end();
I != E;)
if (I->second->invalidate(C, PA)) {
InvalidatedPassIDs.push_back(I->first);
I = ResultsList.erase(I);
} else {
++I;
}
while (!InvalidatedPassIDs.empty())
CGSCCAnalysisResults.erase(
std::make_pair(InvalidatedPassIDs.pop_back_val(), C));
CGSCCAnalysisResultLists.erase(C);
}

char CGSCCAnalysisManagerModuleProxy::PassID;

CGSCCAnalysisManagerModuleProxy::Result
CGSCCAnalysisManagerModuleProxy::run(Module *M) {
assert(CGAM->empty() && "CGSCC analyses ran prior to the module proxy!");
return Result(*CGAM);
}

CGSCCAnalysisManagerModuleProxy::Result::~Result() {
// Clear out the analysis manager if we're being destroyed -- it means we
// didn't even see an invalidate call when we got invalidated.
CGAM->clear();
}

bool CGSCCAnalysisManagerModuleProxy::Result::invalidate(
Module *M, const PreservedAnalyses &PA) {
// If this proxy isn't marked as preserved, then we can't even invalidate
// individual CGSCC analyses, there may be an invalid set of SCC objects in
// the cache making it impossible to incrementally preserve them.
// Just clear the entire manager.
if (!PA.preserved(ID()))
CGAM->clear();

// Return false to indicate that this result is still a valid proxy.
return false;
}

char ModuleAnalysisManagerCGSCCProxy::PassID;

char FunctionAnalysisManagerCGSCCProxy::PassID;

FunctionAnalysisManagerCGSCCProxy::Result
FunctionAnalysisManagerCGSCCProxy::run(LazyCallGraph::SCC *C) {
assert(FAM->empty() && "Function analyses ran prior to the CGSCC proxy!");
return Result(*FAM);
}

FunctionAnalysisManagerCGSCCProxy::Result::~Result() {
// Clear out the analysis manager if we're being destroyed -- it means we
// didn't even see an invalidate call when we got invalidated.
FAM->clear();
}

bool FunctionAnalysisManagerCGSCCProxy::Result::invalidate(
LazyCallGraph::SCC *C, const PreservedAnalyses &PA) {
// If this proxy isn't marked as preserved, then we can't even invalidate
// individual function analyses, there may be an invalid set of Function
// objects in the cache making it impossible to incrementally preserve them.
// Just clear the entire manager.
if (!PA.preserved(ID()))
FAM->clear();

// Return false to indicate that this result is still a valid proxy.
return false;
}

char CGSCCAnalysisManagerFunctionProxy::PassID;
1 change: 1 addition & 0 deletions llvm/lib/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_llvm_library(LLVMAnalysis
BranchProbabilityInfo.cpp
CFG.cpp
CFGPrinter.cpp
CGSCCPassManager.cpp
CaptureTracking.cpp
CostModel.cpp
CodeMetrics.cpp
Expand Down
36 changes: 36 additions & 0 deletions llvm/test/Other/pass-pipeline-parsing.ll
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,42 @@
; RUN: | FileCheck %s --check-prefix=CHECK-UNBALANCED10
; CHECK-UNBALANCED10: unable to parse pass pipeline description

; RUN: opt -disable-output -debug-pass-manager -debug-cgscc-pass-manager \
; RUN: -passes=no-op-cgscc,no-op-cgscc %s 2>&1 \
; RUN: | FileCheck %s --check-prefix=CHECK-TWO-NOOP-CG
; CHECK-TWO-NOOP-CG: Starting module pass manager
; CHECK-TWO-NOOP-CG: Running module pass: ModuleToPostOrderCGSCCPassAdaptor
; CHECK-TWO-NOOP-CG: Starting CGSCC pass manager
; CHECK-TWO-NOOP-CG: Running CGSCC pass: NoOpCGSCCPass
; CHECK-TWO-NOOP-CG: Running CGSCC pass: NoOpCGSCCPass
; CHECK-TWO-NOOP-CG: Finished CGSCC pass manager
; CHECK-TWO-NOOP-CG: Finished module pass manager

; RUN: opt -disable-output -debug-pass-manager -debug-cgscc-pass-manager \
; RUN: -passes='module(function(no-op-function),cgscc(no-op-cgscc,function(no-op-function),no-op-cgscc),function(no-op-function))' %s 2>&1 \
; RUN: | FileCheck %s --check-prefix=CHECK-NESTED-MP-CG-FP
; CHECK-NESTED-MP-CG-FP: Starting module pass manager
; CHECK-NESTED-MP-CG-FP: Starting module pass manager
; CHECK-NESTED-MP-CG-FP: Running module pass: ModuleToFunctionPassAdaptor
; CHECK-NESTED-MP-CG-FP: Starting function pass manager
; CHECK-NESTED-MP-CG-FP: Running function pass: NoOpFunctionPass
; CHECK-NESTED-MP-CG-FP: Finished function pass manager
; CHECK-NESTED-MP-CG-FP: Running module pass: ModuleToPostOrderCGSCCPassAdaptor
; CHECK-NESTED-MP-CG-FP: Starting CGSCC pass manager
; CHECK-NESTED-MP-CG-FP: Running CGSCC pass: NoOpCGSCCPass
; CHECK-NESTED-MP-CG-FP: Running CGSCC pass: CGSCCToFunctionPassAdaptor
; CHECK-NESTED-MP-CG-FP: Starting function pass manager
; CHECK-NESTED-MP-CG-FP: Running function pass: NoOpFunctionPass
; CHECK-NESTED-MP-CG-FP: Finished function pass manager
; CHECK-NESTED-MP-CG-FP: Running CGSCC pass: NoOpCGSCCPass
; CHECK-NESTED-MP-CG-FP: Finished CGSCC pass manager
; CHECK-NESTED-MP-CG-FP: Running module pass: ModuleToFunctionPassAdaptor
; CHECK-NESTED-MP-CG-FP: Starting function pass manager
; CHECK-NESTED-MP-CG-FP: Running function pass: NoOpFunctionPass
; CHECK-NESTED-MP-CG-FP: Finished function pass manager
; CHECK-NESTED-MP-CG-FP: Finished module pass manager
; CHECK-NESTED-MP-CG-FP: Finished module pass manager

define void @f() {
ret void
}
10 changes: 10 additions & 0 deletions llvm/tools/opt/NewPMDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "NewPMDriver.h"
#include "Passes.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LazyCallGraph.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/IR/IRPrintingPasses.h"
Expand All @@ -34,18 +35,27 @@ bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M,
tool_output_file *Out, StringRef PassPipeline,
OutputKind OK, VerifierKind VK) {
FunctionAnalysisManager FAM;
CGSCCAnalysisManager CGAM;
ModuleAnalysisManager MAM;

#define MODULE_ANALYSIS(NAME, CREATE_PASS) \
MAM.registerPass(CREATE_PASS);
#include "PassRegistry.def"

#define CGSCC_ANALYSIS(NAME, CREATE_PASS) \
CGAM.registerPass(CREATE_PASS);
#include "PassRegistry.def"

#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \
FAM.registerPass(CREATE_PASS);
#include "PassRegistry.def"

// Cross register the analysis managers through their proxies.
MAM.registerPass(FunctionAnalysisManagerModuleProxy(FAM));
MAM.registerPass(CGSCCAnalysisManagerModuleProxy(CGAM));
CGAM.registerPass(FunctionAnalysisManagerCGSCCProxy(FAM));
CGAM.registerPass(ModuleAnalysisManagerCGSCCProxy(MAM));
FAM.registerPass(CGSCCAnalysisManagerFunctionProxy(CGAM));
FAM.registerPass(ModuleAnalysisManagerFunctionProxy(MAM));

ModulePassManager MPM;
Expand Down
10 changes: 10 additions & 0 deletions llvm/tools/opt/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ MODULE_PASS("print", PrintModulePass(dbgs()))
MODULE_PASS("print-cg", LazyCallGraphPrinterPass(dbgs()))
#undef MODULE_PASS

#ifndef CGSCC_ANALYSIS
#define CGSCC_ANALYSIS(NAME, CREATE_PASS)
#endif
#undef CGSCC_ANALYSIS

#ifndef CGSCC_PASS
#define CGSCC_PASS(NAME, CREATE_PASS)
#endif
#undef CGSCC_PASS

#ifndef FUNCTION_ANALYSIS
#define FUNCTION_ANALYSIS(NAME, CREATE_PASS)
#endif
Expand Down
114 changes: 114 additions & 0 deletions llvm/tools/opt/Passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//

#include "Passes.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LazyCallGraph.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/PassManager.h"
Expand All @@ -31,6 +32,14 @@ struct NoOpModulePass {
static StringRef name() { return "NoOpModulePass"; }
};

/// \brief No-op CGSCC pass which does nothing.
struct NoOpCGSCCPass {
PreservedAnalyses run(LazyCallGraph::SCC *C) {
return PreservedAnalyses::all();
}
static StringRef name() { return "NoOpCGSCCPass"; }
};

/// \brief No-op function pass which does nothing.
struct NoOpFunctionPass {
PreservedAnalyses run(Function *F) { return PreservedAnalyses::all(); }
Expand All @@ -48,6 +57,15 @@ static bool isModulePassName(StringRef Name) {
return false;
}

static bool isCGSCCPassName(StringRef Name) {
if (Name == "no-op-cgscc") return true;

#define CGSCC_PASS(NAME, CREATE_PASS) if (Name == NAME) return true;
#include "PassRegistry.def"

return false;
}

static bool isFunctionPassName(StringRef Name) {
if (Name == "no-op-function") return true;

Expand All @@ -73,6 +91,22 @@ static bool parseModulePassName(ModulePassManager &MPM, StringRef Name) {
return false;
}

static bool parseCGSCCPassName(CGSCCPassManager &CGPM, StringRef Name) {
if (Name == "no-op-cgscc") {
CGPM.addPass(NoOpCGSCCPass());
return true;
}

#define CGSCC_PASS(NAME, CREATE_PASS) \
if (Name == NAME) { \
CGPM.addPass(CREATE_PASS); \
return true; \
}
#include "PassRegistry.def"

return false;
}

static bool parseFunctionPassName(FunctionPassManager &FPM, StringRef Name) {
if (Name == "no-op-function") {
FPM.addPass(NoOpFunctionPass());
Expand Down Expand Up @@ -126,6 +160,55 @@ static bool parseFunctionPassPipeline(FunctionPassManager &FPM,
}
}

static bool parseCGSCCPassPipeline(CGSCCPassManager &CGPM,
StringRef &PipelineText,
bool VerifyEachPass) {
for (;;) {
// Parse nested pass managers by recursing.
if (PipelineText.startswith("cgscc(")) {
CGSCCPassManager NestedCGPM;

// Parse the inner pipeline into the nested manager.
PipelineText = PipelineText.substr(strlen("cgscc("));
if (!parseCGSCCPassPipeline(NestedCGPM, PipelineText, VerifyEachPass) ||
PipelineText.empty())
return false;
assert(PipelineText[0] == ')');
PipelineText = PipelineText.substr(1);

// Add the nested pass manager with the appropriate adaptor.
CGPM.addPass(std::move(NestedCGPM));
} else if (PipelineText.startswith("function(")) {
FunctionPassManager NestedFPM;

// Parse the inner pipeline inte the nested manager.
PipelineText = PipelineText.substr(strlen("function("));
if (!parseFunctionPassPipeline(NestedFPM, PipelineText, VerifyEachPass) ||
PipelineText.empty())
return false;
assert(PipelineText[0] == ')');
PipelineText = PipelineText.substr(1);

// Add the nested pass manager with the appropriate adaptor.
CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(NestedFPM)));
} else {
// Otherwise try to parse a pass name.
size_t End = PipelineText.find_first_of(",)");
if (!parseCGSCCPassName(CGPM, PipelineText.substr(0, End)))
return false;
// FIXME: No verifier support for CGSCC passes!

PipelineText = PipelineText.substr(End);
}

if (PipelineText.empty() || PipelineText[0] == ')')
return true;

assert(PipelineText[0] == ',');
PipelineText = PipelineText.substr(1);
}
}

static bool parseModulePassPipeline(ModulePassManager &MPM,
StringRef &PipelineText,
bool VerifyEachPass) {
Expand All @@ -144,6 +227,20 @@ static bool parseModulePassPipeline(ModulePassManager &MPM,

// Now add the nested manager as a module pass.
MPM.addPass(std::move(NestedMPM));
} else if (PipelineText.startswith("cgscc(")) {
CGSCCPassManager NestedCGPM;

// Parse the inner pipeline inte the nested manager.
PipelineText = PipelineText.substr(strlen("cgscc("));
if (!parseCGSCCPassPipeline(NestedCGPM, PipelineText, VerifyEachPass) ||
PipelineText.empty())
return false;
assert(PipelineText[0] == ')');
PipelineText = PipelineText.substr(1);

// Add the nested pass manager with the appropriate adaptor.
MPM.addPass(
createModuleToPostOrderCGSCCPassAdaptor(std::move(NestedCGPM)));
} else if (PipelineText.startswith("function(")) {
FunctionPassManager NestedFPM;

Expand Down Expand Up @@ -185,6 +282,14 @@ bool llvm::parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText,
if (PipelineText.startswith("module("))
return parseModulePassPipeline(MPM, PipelineText, VerifyEachPass) &&
PipelineText.empty();
if (PipelineText.startswith("cgscc(")) {
CGSCCPassManager CGPM;
if (!parseCGSCCPassPipeline(CGPM, PipelineText, VerifyEachPass) ||
!PipelineText.empty())
return false;
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));
return true;
}
if (PipelineText.startswith("function(")) {
FunctionPassManager FPM;
if (!parseFunctionPassPipeline(FPM, PipelineText, VerifyEachPass) ||
Expand All @@ -201,6 +306,15 @@ bool llvm::parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText,
return parseModulePassPipeline(MPM, PipelineText, VerifyEachPass) &&
PipelineText.empty();

if (isCGSCCPassName(FirstName)) {
CGSCCPassManager CGPM;
if (!parseCGSCCPassPipeline(CGPM, PipelineText, VerifyEachPass) ||
!PipelineText.empty())
return false;
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM)));
return true;
}

if (isFunctionPassName(FirstName)) {
FunctionPassManager FPM;
if (!parseFunctionPassPipeline(FPM, PipelineText, VerifyEachPass) ||
Expand Down