From da5e92486c1c5a86e22710e3bdb3c0dc982efcbf Mon Sep 17 00:00:00 2001 From: Justin Bogner Date: Thu, 25 Feb 2016 07:23:08 +0000 Subject: [PATCH] PM: Implement a basic loop pass manager This creates the new-style LoopPassManager and wires it up with dummy and print passes. This version doesn't support modifying the loop nest at all. It will be far easier to discuss and evaluate the approaches to that with this in place so that the boilerplate is out of the way. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@261831 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Analysis/LoopInfo.h | 7 + include/llvm/Analysis/LoopPassManager.h | 268 +++++++++++++++++++++ include/llvm/Passes/PassBuilder.h | 12 +- lib/Analysis/CMakeLists.txt | 1 + lib/Analysis/LoopPassManager.cpp | 45 ++++ lib/Passes/PassBuilder.cpp | 126 +++++++++- lib/Passes/PassRegistry.def | 14 ++ test/Other/loop-pass-ordering.ll | 35 +++ test/Other/pass-pipeline-parsing.ll | 42 +++- tools/opt/NewPMDriver.cpp | 5 + unittests/Analysis/CMakeLists.txt | 1 + unittests/Analysis/LoopPassManagerTest.cpp | 205 ++++++++++++++++ 12 files changed, 757 insertions(+), 4 deletions(-) create mode 100644 include/llvm/Analysis/LoopPassManager.h create mode 100644 lib/Analysis/LoopPassManager.cpp create mode 100644 test/Other/loop-pass-ordering.ll create mode 100644 unittests/Analysis/LoopPassManagerTest.cpp diff --git a/include/llvm/Analysis/LoopInfo.h b/include/llvm/Analysis/LoopInfo.h index 03b7ccf46ea..d9d2b859665 100644 --- a/include/llvm/Analysis/LoopInfo.h +++ b/include/llvm/Analysis/LoopInfo.h @@ -478,6 +478,13 @@ public: return DebugLoc(); } + StringRef getName() const { + if (BasicBlock *Header = getHeader()) + if (Header->hasName()) + return Header->getName(); + return ""; + } + private: friend class LoopInfoBase; explicit Loop(BasicBlock *BB) : LoopBase(BB) {} diff --git a/include/llvm/Analysis/LoopPassManager.h b/include/llvm/Analysis/LoopPassManager.h new file mode 100644 index 00000000000..da740ce5f19 --- /dev/null +++ b/include/llvm/Analysis/LoopPassManager.h @@ -0,0 +1,268 @@ +//===- LoopPassManager.h - Loop pass management -----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This header provides classes for managing passes over loops in LLVM IR. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_LOOPPASSMANAGER_H +#define LLVM_ANALYSIS_LOOPPASSMANAGER_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/PassManager.h" + +namespace llvm { + +/// \brief The loop pass manager. +/// +/// See the documentation for the PassManager template for details. It runs a +/// sequency of loop passes over each loop that the manager is run over. This +/// typedef serves as a convenient way to refer to this construct. +typedef PassManager LoopPassManager; + +/// \brief The loop analysis manager. +/// +/// See the documentation for the AnalysisManager template for detail +/// documentation. This typedef serves as a convenient way to refer to this +/// construct in the adaptors and proxies used to integrate this into the larger +/// pass manager infrastructure. +typedef AnalysisManager LoopAnalysisManager; + +/// \brief A function analysis which acts as a proxy for a loop analysis +/// manager. +/// +/// This primarily proxies invalidation information from the function analysis +/// manager and function pass manager to a loop analysis manager. You should +/// never use a loop analysis manager from within (transitively) a function +/// pass manager unless your parent function pass has received a proxy result +/// object for it. +class LoopAnalysisManagerFunctionProxy { +public: + class Result { + public: + explicit Result(LoopAnalysisManager &LAM) : LAM(&LAM) {} + // We have to explicitly define all the special member functions because + // MSVC refuses to generate them. + Result(const Result &Arg) : LAM(Arg.LAM) {} + Result(Result &&Arg) : LAM(std::move(Arg.LAM)) {} + Result &operator=(Result RHS) { + std::swap(LAM, RHS.LAM); + return *this; + } + ~Result(); + + /// \brief Accessor for the \c LoopAnalysisManager. + LoopAnalysisManager &getManager() { return *LAM; } + + /// \brief Handler for invalidation of the function. + /// + /// If this analysis itself is preserved, then we assume that the function + /// hasn't changed and thus we don't need to invalidate *all* cached data + /// associated with a \c Loop* in the \c LoopAnalysisManager. + /// + /// Regardless of whether this analysis is marked as preserved, all of the + /// analyses in the \c LoopAnalysisManager are potentially invalidated based + /// on the set of preserved analyses. + bool invalidate(Function &F, const PreservedAnalyses &PA); + + private: + LoopAnalysisManager *LAM; + }; + + static void *ID() { return (void *)&PassID; } + + static StringRef name() { return "LoopAnalysisManagerFunctionProxy"; } + + explicit LoopAnalysisManagerFunctionProxy(LoopAnalysisManager &LAM) + : LAM(&LAM) {} + // We have to explicitly define all the special member functions because MSVC + // refuses to generate them. + LoopAnalysisManagerFunctionProxy(const LoopAnalysisManagerFunctionProxy &Arg) + : LAM(Arg.LAM) {} + LoopAnalysisManagerFunctionProxy(LoopAnalysisManagerFunctionProxy &&Arg) + : LAM(std::move(Arg.LAM)) {} + LoopAnalysisManagerFunctionProxy & + operator=(LoopAnalysisManagerFunctionProxy RHS) { + std::swap(LAM, RHS.LAM); + return *this; + } + + /// \brief Run the analysis pass and create our proxy result object. + /// + /// This doesn't do any interesting work, it is primarily used to insert our + /// proxy result object into the function analysis cache so that we can proxy + /// invalidation to the loop analysis manager. + /// + /// In debug builds, it will also assert that the analysis manager is empty as + /// no queries should arrive at the loop analysis manager prior to this + /// analysis being requested. + Result run(Function &F); + +private: + static char PassID; + + LoopAnalysisManager *LAM; +}; + +/// \brief A loop analysis which acts as a proxy for a function analysis +/// manager. +/// +/// This primarily provides an accessor to a parent function analysis manager to +/// loop passes. Only the const interface of the function analysis manager is +/// provided to indicate that once inside of a loop analysis pass you cannot +/// request a function analysis to actually run. Instead, the user must rely on +/// the \c getCachedResult API. +/// +/// This proxy *doesn't* manage the invalidation in any way. That is handled by +/// the recursive return path of each layer of the pass manager and the +/// returned PreservedAnalysis set. +class FunctionAnalysisManagerLoopProxy { +public: + /// \brief Result proxy object for \c FunctionAnalysisManagerLoopProxy. + class Result { + public: + explicit Result(const FunctionAnalysisManager &FAM) : FAM(&FAM) {} + // We have to explicitly define all the special member functions because + // MSVC refuses to generate them. + Result(const Result &Arg) : FAM(Arg.FAM) {} + Result(Result &&Arg) : FAM(std::move(Arg.FAM)) {} + Result &operator=(Result RHS) { + std::swap(FAM, RHS.FAM); + return *this; + } + + const FunctionAnalysisManager &getManager() const { return *FAM; } + + /// \brief Handle invalidation by ignoring it, this pass is immutable. + bool invalidate(Function &) { return false; } + + private: + const FunctionAnalysisManager *FAM; + }; + + static void *ID() { return (void *)&PassID; } + + static StringRef name() { return "FunctionAnalysisManagerLoopProxy"; } + + FunctionAnalysisManagerLoopProxy(const FunctionAnalysisManager &FAM) + : FAM(&FAM) {} + // We have to explicitly define all the special member functions because MSVC + // refuses to generate them. + FunctionAnalysisManagerLoopProxy(const FunctionAnalysisManagerLoopProxy &Arg) + : FAM(Arg.FAM) {} + FunctionAnalysisManagerLoopProxy(FunctionAnalysisManagerLoopProxy &&Arg) + : FAM(std::move(Arg.FAM)) {} + FunctionAnalysisManagerLoopProxy & + operator=(FunctionAnalysisManagerLoopProxy RHS) { + std::swap(FAM, RHS.FAM); + return *this; + } + + /// \brief Run the analysis pass and create our proxy result object. + /// Nothing to see here, it just forwards the \c FAM reference into the + /// result. + Result run(Loop &) { return Result(*FAM); } + +private: + static char PassID; + + const FunctionAnalysisManager *FAM; +}; + +/// \brief Adaptor that maps from a function to its loops. +/// +/// Designed to allow composition of a LoopPass(Manager) and a +/// FunctionPassManager. Note that if this pass is constructed with a \c +/// FunctionAnalysisManager it will run the \c LoopAnalysisManagerFunctionProxy +/// analysis prior to running the loop passes over the function to enable a \c +/// LoopAnalysisManager to be used within this run safely. +template class FunctionToLoopPassAdaptor { +public: + explicit FunctionToLoopPassAdaptor(LoopPassT Pass) + : Pass(std::move(Pass)) {} + // We have to explicitly define all the special member functions because MSVC + // refuses to generate them. + FunctionToLoopPassAdaptor(const FunctionToLoopPassAdaptor &Arg) + : Pass(Arg.Pass) {} + FunctionToLoopPassAdaptor(FunctionToLoopPassAdaptor &&Arg) + : Pass(std::move(Arg.Pass)) {} + friend void swap(FunctionToLoopPassAdaptor &LHS, + FunctionToLoopPassAdaptor &RHS) { + using std::swap; + swap(LHS.Pass, RHS.Pass); + } + FunctionToLoopPassAdaptor &operator=(FunctionToLoopPassAdaptor RHS) { + swap(*this, RHS); + return *this; + } + + /// \brief Runs the loop passes across every loop in the function. + PreservedAnalyses run(Function &F, FunctionAnalysisManager *AM) { + assert(AM && "We need analyses to compute the loop structure!"); + + // Setup the loop analysis manager from its proxy. + LoopAnalysisManager *LAM = + &AM->getResult(F).getManager(); + // Get the loop structure for this function + LoopInfo &LI = AM->getResult(F); + + PreservedAnalyses PA = PreservedAnalyses::all(); + + // We want to visit the loops in reverse post-order. We'll build the stack + // of loops to visit in Loops by first walking the loops in pre-order. + SmallVector Loops; + SmallVector WorkList(LI.begin(), LI.end()); + while (!WorkList.empty()) { + Loop *L = WorkList.pop_back_val(); + WorkList.insert(WorkList.end(), L->begin(), L->end()); + Loops.push_back(L); + } + + // Now pop each element off of the stack to visit the loops in reverse + // post-order. + for (auto *L : reverse(Loops)) { + PreservedAnalyses PassPA = Pass.run(*L, LAM); + + // We know that the loop pass couldn't have invalidated any other loop's + // analyses (that's the contract of a loop pass), so directly handle the + // loop analysis manager's invalidation here. Also, update the + // preserved analyses to reflect that once invalidated these can again + // be preserved. + PassPA = LAM->invalidate(*L, std::move(PassPA)); + + // Then intersect the preserved set so that invalidation of module + // analyses will eventually occur when the module pass completes. + PA.intersect(std::move(PassPA)); + } + + // By definition we preserve the proxy. This precludes *any* invalidation of + // loop analyses by the proxy, but that's OK because we've taken care to + // invalidate analyses in the loop analysis manager incrementally above. + PA.preserve(); + return PA; + } + + static StringRef name() { return "FunctionToLoopPassAdaptor"; } + +private: + LoopPassT Pass; +}; + +/// \brief A function to deduce a loop pass type and wrap it in the templated +/// adaptor. +template +FunctionToLoopPassAdaptor +createFunctionToLoopPassAdaptor(LoopPassT Pass) { + return FunctionToLoopPassAdaptor(std::move(Pass)); +} +} + +#endif // LLVM_ANALYSIS_LOOPPASSMANAGER_H diff --git a/include/llvm/Passes/PassBuilder.h b/include/llvm/Passes/PassBuilder.h index 8b182e1955e..14075035c96 100644 --- a/include/llvm/Passes/PassBuilder.h +++ b/include/llvm/Passes/PassBuilder.h @@ -18,6 +18,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/LoopPassManager.h" #include "llvm/IR/PassManager.h" namespace llvm { @@ -60,6 +61,13 @@ public: /// pre-register analyses and this will not override those. void registerFunctionAnalyses(FunctionAnalysisManager &FAM); + /// \brief Registers all available loop analysis passes. + /// + /// This is an interface that can be used to populate a \c LoopAnalysisManager + /// with all registered loop analyses. Callers can still manually register any + /// additional analyses. + void registerLoopAnalyses(LoopAnalysisManager &LAM); + /// \brief Parse a textual pass pipeline description into a \c ModulePassManager. /// /// The format of the textual pass pipeline description looks something like: @@ -112,7 +120,10 @@ private: bool parseModulePassName(ModulePassManager &MPM, StringRef Name); bool parseCGSCCPassName(CGSCCPassManager &CGPM, StringRef Name); bool parseFunctionPassName(FunctionPassManager &FPM, StringRef Name); + bool parseLoopPassName(LoopPassManager &LPM, StringRef Name); bool parseAAPassName(AAManager &AA, StringRef Name); + bool parseLoopPassPipeline(LoopPassManager &LPM, StringRef &PipelineText, + bool VerifyEachPass, bool DebugLogging); bool parseFunctionPassPipeline(FunctionPassManager &FPM, StringRef &PipelineText, bool VerifyEachPass, bool DebugLogging); @@ -121,7 +132,6 @@ private: bool parseModulePassPipeline(ModulePassManager &MPM, StringRef &PipelineText, bool VerifyEachPass, bool DebugLogging); }; - } #endif diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 38234207d9a..72669289b08 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -42,6 +42,7 @@ add_llvm_library(LLVMAnalysis LoopUnrollAnalyzer.cpp LoopInfo.cpp LoopPass.cpp + LoopPassManager.cpp MemDepPrinter.cpp MemDerefPrinter.cpp MemoryBuiltins.cpp diff --git a/lib/Analysis/LoopPassManager.cpp b/lib/Analysis/LoopPassManager.cpp new file mode 100644 index 00000000000..b1d1140d11b --- /dev/null +++ b/lib/Analysis/LoopPassManager.cpp @@ -0,0 +1,45 @@ +//===- LoopPassManager.cpp - Loop pass management -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/LoopPassManager.h" + +using namespace llvm; + +char LoopAnalysisManagerFunctionProxy::PassID; + +LoopAnalysisManagerFunctionProxy::Result +LoopAnalysisManagerFunctionProxy::run(Function &F) { + // TODO: In FunctionAnalysisManagerModuleProxy we assert that the + // AnalysisManager is empty, but if we do that here we run afoul of the fact + // that we still have results for previous functions alive. Should we be + // clearing those when we finish a function? + //assert(LAM->empty() && "Loop analyses ran prior to the function proxy!"); + return Result(*LAM); +} + +LoopAnalysisManagerFunctionProxy::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. + LAM->clear(); +} + +bool LoopAnalysisManagerFunctionProxy::Result::invalidate( + Function &F, const PreservedAnalyses &PA) { + // If this proxy isn't marked as preserved, then we can't even invalidate + // individual loop analyses, there may be an invalid set of Loops in the cache + // making it impossible to incrementally preserve them. Just clear the entire + // manager. + if (!PA.preserved(ID())) + LAM->clear(); + + // Return false to indicate that this result is still a valid proxy. + return false; +} + +char FunctionAnalysisManagerLoopProxy::PassID; diff --git a/lib/Passes/PassBuilder.cpp b/lib/Passes/PassBuilder.cpp index bf646104230..f043c92fd4f 100644 --- a/lib/Passes/PassBuilder.cpp +++ b/lib/Passes/PassBuilder.cpp @@ -107,6 +107,24 @@ private: 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) { @@ -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; @@ -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) { \ @@ -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()); \ + return true; \ + } \ + if (Name == "invalidate<" NAME ">") { \ + FPM.addPass(InvalidateAnalysisPass()); \ + return true; \ + } +#include "PassRegistry.def" + + return false; +} + bool PassBuilder::parseAAPassName(AAManager &AA, StringRef Name) { #define FUNCTION_ALIAS_ANALYSIS(NAME, CREATE_PASS) \ if (Name == NAME) { \ @@ -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, @@ -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(",)"); @@ -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) || @@ -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) || @@ -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; } diff --git a/lib/Passes/PassRegistry.def b/lib/Passes/PassRegistry.def index 9feba877ed3..1721fa1bc23 100644 --- a/lib/Passes/PassRegistry.def +++ b/lib/Passes/PassRegistry.def @@ -96,3 +96,17 @@ FUNCTION_PASS("sroa", SROA()) FUNCTION_PASS("verify", VerifierPass()) FUNCTION_PASS("verify", 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", InvalidateAllAnalysesPass()) +LOOP_PASS("no-op-loop", NoOpLoopPass()) +LOOP_PASS("print", PrintLoopPass(dbgs())) +#undef LOOP_PASS diff --git a/test/Other/loop-pass-ordering.ll b/test/Other/loop-pass-ordering.ll new file mode 100644 index 00000000000..ceda0d3869d --- /dev/null +++ b/test/Other/loop-pass-ordering.ll @@ -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 +} diff --git a/test/Other/pass-pipeline-parsing.ll b/test/Other/pass-pipeline-parsing.ll index da0e7604003..3c09b01b481 100644 --- a/test/Other/pass-pipeline-parsing.ll +++ b/test/Other/pass-pipeline-parsing.ll @@ -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 } diff --git a/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp index a95a4ed8a00..e0cc88bc33f 100644 --- a/tools/opt/NewPMDriver.cpp +++ b/tools/opt/NewPMDriver.cpp @@ -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" @@ -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); @@ -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); }); @@ -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) diff --git a/unittests/Analysis/CMakeLists.txt b/unittests/Analysis/CMakeLists.txt index 17e1383ea7e..70207662bfe 100644 --- a/unittests/Analysis/CMakeLists.txt +++ b/unittests/Analysis/CMakeLists.txt @@ -11,6 +11,7 @@ add_llvm_unittest(AnalysisTests CFGTest.cpp CGSCCPassManagerTest.cpp LazyCallGraphTest.cpp + LoopPassManagerTest.cpp ScalarEvolutionTest.cpp MixedTBAATest.cpp ValueTrackingTest.cpp diff --git a/unittests/Analysis/LoopPassManagerTest.cpp b/unittests/Analysis/LoopPassManagerTest.cpp new file mode 100644 index 00000000000..9fb46cbae02 --- /dev/null +++ b/unittests/Analysis/LoopPassManagerTest.cpp @@ -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 *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 &VisitedLoops; + int &AnalyzedBlockCount; + bool OnlyUseCachedResults; + +public: + TestLoopPass(std::vector &VisitedLoops, int &AnalyzedBlockCount, + bool OnlyUseCachedResults = false) + : VisitedLoops(VisitedLoops), AnalyzedBlockCount(AnalyzedBlockCount), + OnlyUseCachedResults(OnlyUseCachedResults) {} + + PreservedAnalyses run(Loop &L, AnalysisManager *AM) { + VisitedLoops.push_back(L.getName()); + + if (OnlyUseCachedResults) { + // Hack to force the use of the cached interface. + if (auto *AR = AM->getCachedResult(L)) + AnalyzedBlockCount += AR->BlockCount; + } else { + // Typical path just runs the analysis as needed. + auto &AR = AM->getResult(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 *AM) { + return L.getName() == Name ? PreservedAnalyses::none() + : PreservedAnalyses::all(); + } + + static StringRef name() { return "TestLoopInvalidatingPass"; } +}; + +std::unique_ptr parseIR(const char *IR) { + LLVMContext &C = getGlobalContext(); + SMDiagnostic Err; + return parseAssemblyString(IR, Err, C); +} + +class LoopPassManagerTest : public ::testing::Test { +protected: + std::unique_ptr 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 VisitedLoops1; + int AnalyzedBlockCount1 = 0; + { + LoopPassManager LPM; + LPM.addPass(TestLoopPass(VisitedLoops1, AnalyzedBlockCount1)); + + FPM.addPass(createFunctionToLoopPassAdaptor(std::move(LPM))); + } + + // Only use cached analyses. + std::vector 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); +} +}