126 changes: 124 additions & 2 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ struct NoOpFunctionAnalysis {

char NoOpFunctionAnalysis::PassID;

/// \brief No-op loop pass which does nothing.
struct NoOpLoopPass {
PreservedAnalyses run(Loop &L) { return PreservedAnalyses::all(); }
static StringRef name() { return "NoOpLoopPass"; }
};

/// \brief No-op loop analysis.
struct NoOpLoopAnalysis {
struct Result {};
Result run(Loop &) { return Result(); }
static StringRef name() { return "NoOpLoopAnalysis"; }
static void *ID() { return (void *)&PassID; }
private:
static char PassID;
};

char NoOpLoopAnalysis::PassID;

} // End anonymous namespace.

void PassBuilder::registerModuleAnalyses(ModuleAnalysisManager &MAM) {
Expand All @@ -127,6 +145,12 @@ void PassBuilder::registerFunctionAnalyses(FunctionAnalysisManager &FAM) {
#include "PassRegistry.def"
}

void PassBuilder::registerLoopAnalyses(LoopAnalysisManager &LAM) {
#define LOOP_ANALYSIS(NAME, CREATE_PASS) \
LAM.registerPass([&] { return CREATE_PASS; });
#include "PassRegistry.def"
}

#ifndef NDEBUG
static bool isModulePassName(StringRef Name) {
#define MODULE_PASS(NAME, CREATE_PASS) if (Name == NAME) return true;
Expand Down Expand Up @@ -159,6 +183,16 @@ static bool isFunctionPassName(StringRef Name) {
return false;
}

static bool isLoopPassName(StringRef Name) {
#define LOOP_PASS(NAME, CREATE_PASS) if (Name == NAME) return true;
#define LOOP_ANALYSIS(NAME, CREATE_PASS) \
if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \
return true;
#include "PassRegistry.def"

return false;
}

bool PassBuilder::parseModulePassName(ModulePassManager &MPM, StringRef Name) {
#define MODULE_PASS(NAME, CREATE_PASS) \
if (Name == NAME) { \
Expand Down Expand Up @@ -220,6 +254,27 @@ bool PassBuilder::parseFunctionPassName(FunctionPassManager &FPM,
return false;
}

bool PassBuilder::parseLoopPassName(LoopPassManager &FPM,
StringRef Name) {
#define LOOP_PASS(NAME, CREATE_PASS) \
if (Name == NAME) { \
FPM.addPass(CREATE_PASS); \
return true; \
}
#define LOOP_ANALYSIS(NAME, CREATE_PASS) \
if (Name == "require<" NAME ">") { \
FPM.addPass(RequireAnalysisPass<decltype(CREATE_PASS)>()); \
return true; \
} \
if (Name == "invalidate<" NAME ">") { \
FPM.addPass(InvalidateAnalysisPass<decltype(CREATE_PASS)>()); \
return true; \
}
#include "PassRegistry.def"

return false;
}

bool PassBuilder::parseAAPassName(AAManager &AA, StringRef Name) {
#define FUNCTION_ALIAS_ANALYSIS(NAME, CREATE_PASS) \
if (Name == NAME) { \
Expand All @@ -231,6 +286,45 @@ bool PassBuilder::parseAAPassName(AAManager &AA, StringRef Name) {
return false;
}

bool PassBuilder::parseLoopPassPipeline(LoopPassManager &LPM,
StringRef &PipelineText,
bool VerifyEachPass,
bool DebugLogging) {
for (;;) {
// Parse nested pass managers by recursing.
if (PipelineText.startswith("loop(")) {
LoopPassManager NestedLPM(DebugLogging);

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

// Add the nested pass manager with the appropriate adaptor.
LPM.addPass(std::move(NestedLPM));
} else {
// Otherwise try to parse a pass name.
size_t End = PipelineText.find_first_of(",)");
if (!parseLoopPassName(LPM, PipelineText.substr(0, End)))
return false;
// TODO: Ideally, we would run a LoopVerifierPass() here in the
// VerifyEachPass case, but we don't have such a verifier yet.

PipelineText = PipelineText.substr(End);
}

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

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

bool PassBuilder::parseFunctionPassPipeline(FunctionPassManager &FPM,
StringRef &PipelineText,
bool VerifyEachPass,
Expand All @@ -251,6 +345,20 @@ bool PassBuilder::parseFunctionPassPipeline(FunctionPassManager &FPM,

// Add the nested pass manager with the appropriate adaptor.
FPM.addPass(std::move(NestedFPM));
} else if (PipelineText.startswith("loop(")) {
LoopPassManager NestedLPM(DebugLogging);

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

// Add the nested pass manager with the appropriate adaptor.
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(NestedLPM)));
} else {
// Otherwise try to parse a pass name.
size_t End = PipelineText.find_first_of(",)");
Expand Down Expand Up @@ -411,7 +519,7 @@ bool PassBuilder::parsePassPipeline(ModulePassManager &MPM,

// If this looks like a CGSCC pass, parse the whole thing as a CGSCC
// pipeline.
if (isCGSCCPassName(FirstName)) {
if (PipelineText.startswith("cgscc(") || isCGSCCPassName(FirstName)) {
CGSCCPassManager CGPM(DebugLogging);
if (!parseCGSCCPassPipeline(CGPM, PipelineText, VerifyEachPass,
DebugLogging) ||
Expand All @@ -423,7 +531,7 @@ bool PassBuilder::parsePassPipeline(ModulePassManager &MPM,

// Similarly, if this looks like a Function pass, parse the whole thing as
// a Function pipelien.
if (isFunctionPassName(FirstName)) {
if (PipelineText.startswith("function(") || isFunctionPassName(FirstName)) {
FunctionPassManager FPM(DebugLogging);
if (!parseFunctionPassPipeline(FPM, PipelineText, VerifyEachPass,
DebugLogging) ||
Expand All @@ -433,6 +541,20 @@ bool PassBuilder::parsePassPipeline(ModulePassManager &MPM,
return true;
}

// If this looks like a Loop pass, parse the whole thing as a Loop pipeline.
if (PipelineText.startswith("loop(") || isLoopPassName(FirstName)) {
LoopPassManager LPM(DebugLogging);
if (!parseLoopPassPipeline(LPM, PipelineText, VerifyEachPass,
DebugLogging) ||
!PipelineText.empty())
return false;
FunctionPassManager FPM(DebugLogging);
FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
return true;
}


return false;
}

Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,17 @@ FUNCTION_PASS("sroa", SROA())
FUNCTION_PASS("verify", VerifierPass())
FUNCTION_PASS("verify<domtree>", DominatorTreeVerifierPass())
#undef FUNCTION_PASS

#ifndef LOOP_ANALYSIS
#define LOOP_ANALYSIS(NAME, CREATE_PASS)
#endif
LOOP_ANALYSIS("no-op-loop", NoOpLoopAnalysis())
#undef LOOP_ANALYSIS

#ifndef LOOP_PASS
#define LOOP_PASS(NAME, CREATE_PASS)
#endif
LOOP_PASS("invalidate<all>", InvalidateAllAnalysesPass())
LOOP_PASS("no-op-loop", NoOpLoopPass())
LOOP_PASS("print", PrintLoopPass(dbgs()))
#undef LOOP_PASS
35 changes: 35 additions & 0 deletions llvm/test/Other/loop-pass-ordering.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
; RUN: opt -disable-output -debug-pass-manager \
; RUN: -passes='no-op-loop' %s 2>&1 \
; RUN: | FileCheck %s

; @f()
; / \
; loop.0 loop.1
; / \ \
; loop.0.0 loop.0.1 loop.1.0
;
; CHECK: Running pass: NoOpLoopPass on loop.1.0
; CHECK: Running pass: NoOpLoopPass on loop.1
; CHECK: Running pass: NoOpLoopPass on loop.0.0
; CHECK: Running pass: NoOpLoopPass on loop.0.1
; CHECK: Running pass: NoOpLoopPass on loop.0
define void @f() {
entry:
br label %loop.0
loop.0:
br i1 undef, label %loop.0.0, label %loop.1
loop.0.0:
br i1 undef, label %loop.0.0, label %loop.0.1
loop.0.1:
br i1 undef, label %loop.0.1, label %loop.0
loop.1:
br i1 undef, label %loop.1, label %loop.1.bb1
loop.1.bb1:
br i1 undef, label %loop.1, label %loop.1.bb2
loop.1.bb2:
br i1 undef, label %end, label %loop.1.0
loop.1.0:
br i1 undef, label %loop.1.0, label %loop.1
end:
ret void
}
42 changes: 41 additions & 1 deletion llvm/test/Other/pass-pipeline-parsing.ll
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,46 @@
; CHECK-NESTED-MP-CG-FP: Finished pass manager
; CHECK-NESTED-MP-CG-FP: Finished pass manager

; RUN: opt -disable-output -debug-pass-manager \
; RUN: -passes='no-op-loop,no-op-loop' %s 2>&1 \
; RUN: | FileCheck %s --check-prefix=CHECK-TWO-NOOP-LOOP
; CHECK-TWO-NOOP-LOOP: Starting pass manager
; CHECK-TWO-NOOP-LOOP: Running pass: ModuleToFunctionPassAdaptor
; CHECK-TWO-NOOP-LOOP: Starting pass manager
; CHECK-TWO-NOOP-LOOP: Running pass: FunctionToLoopPassAdaptor
; CHECK-TWO-NOOP-LOOP: Starting pass manager
; CHECK-TWO-NOOP-LOOP: Running pass: NoOpLoopPass
; CHECK-TWO-NOOP-LOOP: Running pass: NoOpLoopPass
; CHECK-TWO-NOOP-LOOP: Finished pass manager
; CHECK-TWO-NOOP-LOOP: Finished pass manager
; CHECK-TWO-NOOP-LOOP: Finished pass manager

; RUN: opt -disable-output -debug-pass-manager \
; RUN: -passes='module(function(loop(no-op-loop)))' %s 2>&1 \
; RUN: | FileCheck %s --check-prefix=CHECK-NESTED-FP-LP
; RUN: opt -disable-output -debug-pass-manager \
; RUN: -passes='function(loop(no-op-loop))' %s 2>&1 \
; RUN: | FileCheck %s --check-prefix=CHECK-NESTED-FP-LP
; RUN: opt -disable-output -debug-pass-manager \
; RUN: -passes='loop(no-op-loop)' %s 2>&1 \
; RUN: | FileCheck %s --check-prefix=CHECK-NESTED-FP-LP
; RUN: opt -disable-output -debug-pass-manager \
; RUN: -passes='no-op-loop' %s 2>&1 \
; RUN: | FileCheck %s --check-prefix=CHECK-NESTED-FP-LP
; CHECK-NESTED-FP-LP: Starting pass manager
; CHECK-NESTED-FP-LP: Running pass: ModuleToFunctionPassAdaptor
; CHECK-NESTED-FP-LP: Starting pass manager
; CHECK-NESTED-FP-LP: Running pass: FunctionToLoopPassAdaptor
; CHECK-NESTED-FP-LP: Starting pass manager
; CHECK-NESTED-FP-LP: Running pass: NoOpLoopPass
; CHECK-NESTED-FP-LP: Finished pass manager
; CHECK-NESTED-FP-LP: Finished pass manager
; CHECK-NESTED-FP-LP: Finished pass manager


define void @f() {
ret void
entry:
br label %loop
loop:
br label %loop
}
5 changes: 5 additions & 0 deletions llvm/tools/opt/NewPMDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LoopPassManager.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRPrintingPasses.h"
Expand Down Expand Up @@ -62,6 +63,7 @@ bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M,
return false;
}

LoopAnalysisManager LAM(DebugPM);
FunctionAnalysisManager FAM(DebugPM);
CGSCCAnalysisManager CGAM(DebugPM);
ModuleAnalysisManager MAM(DebugPM);
Expand All @@ -73,6 +75,7 @@ bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M,
PB.registerModuleAnalyses(MAM);
PB.registerCGSCCAnalyses(CGAM);
PB.registerFunctionAnalyses(FAM);
PB.registerLoopAnalyses(LAM);

// Cross register the analysis managers through their proxies.
MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
Expand All @@ -81,6 +84,8 @@ bool llvm::runPassPipeline(StringRef Arg0, LLVMContext &Context, Module &M,
CGAM.registerPass([&] { return ModuleAnalysisManagerCGSCCProxy(MAM); });
FAM.registerPass([&] { return CGSCCAnalysisManagerFunctionProxy(CGAM); });
FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });
FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); });
LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); });

ModulePassManager MPM(DebugPM);
if (VK > VK_NoVerifier)
Expand Down
1 change: 1 addition & 0 deletions llvm/unittests/Analysis/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_llvm_unittest(AnalysisTests
CFGTest.cpp
CGSCCPassManagerTest.cpp
LazyCallGraphTest.cpp
LoopPassManagerTest.cpp
ScalarEvolutionTest.cpp
MixedTBAATest.cpp
ValueTrackingTest.cpp
Expand Down
205 changes: 205 additions & 0 deletions llvm/unittests/Analysis/LoopPassManagerTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
//===- llvm/unittest/Analysis/LoopPassManagerTest.cpp - LPM tests ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "gtest/gtest.h"
#include "llvm/Analysis/LoopPassManager.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/SourceMgr.h"

using namespace llvm;

namespace {

class TestLoopAnalysis {
/// \brief Private static data to provide unique ID.
static char PassID;

int &Runs;

public:
struct Result {
Result(int Count) : BlockCount(Count) {}
int BlockCount;
};

/// \brief Returns an opaque, unique ID for this pass type.
static void *ID() { return (void *)&PassID; }

/// \brief Returns the name of the analysis.
static StringRef name() { return "TestLoopAnalysis"; }

TestLoopAnalysis(int &Runs) : Runs(Runs) {}

/// \brief Run the analysis pass over the loop and return a result.
Result run(Loop &L, AnalysisManager<Loop> *AM) {
++Runs;
int Count = 0;

for (auto I = L.block_begin(), E = L.block_end(); I != E; ++I)
++Count;
return Result(Count);
}
};

char TestLoopAnalysis::PassID;

class TestLoopPass {
std::vector<StringRef> &VisitedLoops;
int &AnalyzedBlockCount;
bool OnlyUseCachedResults;

public:
TestLoopPass(std::vector<StringRef> &VisitedLoops, int &AnalyzedBlockCount,
bool OnlyUseCachedResults = false)
: VisitedLoops(VisitedLoops), AnalyzedBlockCount(AnalyzedBlockCount),
OnlyUseCachedResults(OnlyUseCachedResults) {}

PreservedAnalyses run(Loop &L, AnalysisManager<Loop> *AM) {
VisitedLoops.push_back(L.getName());

if (OnlyUseCachedResults) {
// Hack to force the use of the cached interface.
if (auto *AR = AM->getCachedResult<TestLoopAnalysis>(L))
AnalyzedBlockCount += AR->BlockCount;
} else {
// Typical path just runs the analysis as needed.
auto &AR = AM->getResult<TestLoopAnalysis>(L);
AnalyzedBlockCount += AR.BlockCount;
}

return PreservedAnalyses::all();
}

static StringRef name() { return "TestLoopPass"; }
};

// A test loop pass that invalidates the analysis for loops with the given name.
class TestLoopInvalidatingPass {
StringRef Name;

public:
TestLoopInvalidatingPass(StringRef LoopName) : Name(LoopName) {}

PreservedAnalyses run(Loop &L, AnalysisManager<Loop> *AM) {
return L.getName() == Name ? PreservedAnalyses::none()
: PreservedAnalyses::all();
}

static StringRef name() { return "TestLoopInvalidatingPass"; }
};

std::unique_ptr<Module> parseIR(const char *IR) {
LLVMContext &C = getGlobalContext();
SMDiagnostic Err;
return parseAssemblyString(IR, Err, C);
}

class LoopPassManagerTest : public ::testing::Test {
protected:
std::unique_ptr<Module> M;

public:
LoopPassManagerTest()
: M(parseIR("define void @f() {\n"
"entry:\n"
" br label %loop.0\n"
"loop.0:\n"
" br i1 undef, label %loop.0.0, label %end\n"
"loop.0.0:\n"
" br i1 undef, label %loop.0.0, label %loop.0.1\n"
"loop.0.1:\n"
" br i1 undef, label %loop.0.1, label %loop.0\n"
"end:\n"
" ret void\n"
"}\n"
"\n"
"define void @g() {\n"
"entry:\n"
" br label %loop.g.0\n"
"loop.g.0:\n"
" br i1 undef, label %loop.g.0, label %end\n"
"end:\n"
" ret void\n"
"}\n")) {}
};

#define EXPECT_N_ELEMENTS_EQ(N, EXPECTED, ACTUAL) \
do { \
EXPECT_EQ(N##UL, ACTUAL.size()); \
for (int I = 0; I < N; ++I) \
EXPECT_TRUE(EXPECTED[I] == ACTUAL[I]) << "Element " << I << " is " \
<< ACTUAL[I] << ". Expected " \
<< EXPECTED[I] << "."; \
} while (0)

TEST_F(LoopPassManagerTest, Basic) {
LoopAnalysisManager LAM(true);
int LoopAnalysisRuns = 0;
LAM.registerPass([&] { return TestLoopAnalysis(LoopAnalysisRuns); });

FunctionAnalysisManager FAM(true);
// We need DominatorTreeAnalysis for LoopAnalysis.
FAM.registerPass([&] { return DominatorTreeAnalysis(); });
FAM.registerPass([&] { return LoopAnalysis(); });
FAM.registerPass([&] { return LoopAnalysisManagerFunctionProxy(LAM); });
LAM.registerPass([&] { return FunctionAnalysisManagerLoopProxy(FAM); });

ModuleAnalysisManager MAM(true);
MAM.registerPass([&] { return FunctionAnalysisManagerModuleProxy(FAM); });
FAM.registerPass([&] { return ModuleAnalysisManagerFunctionProxy(MAM); });

ModulePassManager MPM(true);
FunctionPassManager FPM(true);

// Visit all of the loops.
std::vector<StringRef> VisitedLoops1;
int AnalyzedBlockCount1 = 0;
{
LoopPassManager LPM;
LPM.addPass(TestLoopPass(VisitedLoops1, AnalyzedBlockCount1));

FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
}

// Only use cached analyses.
std::vector<StringRef> VisitedLoops2;
int AnalyzedBlockCount2 = 0;
{
LoopPassManager LPM;
LPM.addPass(TestLoopInvalidatingPass("loop.g.0"));
LPM.addPass(TestLoopPass(VisitedLoops2, AnalyzedBlockCount2,
/*OnlyUseCachedResults=*/true));

FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM)));
}

MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
MPM.run(*M, &MAM);

StringRef ExpectedLoops[] = {"loop.0.0", "loop.0.1", "loop.0", "loop.g.0"};

// Validate the counters and order of loops visited.
// loop.0 has 3 blocks whereas loop.0.0, loop.0.1, and loop.g.0 each have 1.
EXPECT_N_ELEMENTS_EQ(4, ExpectedLoops, VisitedLoops1);
EXPECT_EQ(6, AnalyzedBlockCount1);

EXPECT_N_ELEMENTS_EQ(4, ExpectedLoops, VisitedLoops2);
// The block from loop.g.0 won't be counted, since it wasn't cached.
EXPECT_EQ(5, AnalyzedBlockCount2);

// The first LPM runs the loop analysis for all four loops, the second uses
// cached results for everything.
EXPECT_EQ(4, LoopAnalysisRuns);
}
}