diff --git a/llvm/include/llvm/Analysis/LoopNestAnalysis.h b/llvm/include/llvm/Analysis/LoopNestAnalysis.h --- a/llvm/include/llvm/Analysis/LoopNestAnalysis.h +++ b/llvm/include/llvm/Analysis/LoopNestAnalysis.h @@ -128,6 +128,8 @@ [](const Loop *L) { return L->isLoopSimplifyForm(); }); } + StringRef getName() const { return Loops.front()->getName(); } + protected: const unsigned MaxPerfectDepth; // maximum perfect nesting depth level. LoopVectorTy Loops; // the loops in the nest (in breadth first order). diff --git a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h --- a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h +++ b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h @@ -44,6 +44,7 @@ #include "llvm/Analysis/GlobalsModRef.h" #include "llvm/Analysis/LoopAnalysisManager.h" #include "llvm/Analysis/LoopInfo.h" +#include "llvm/Analysis/LoopNestAnalysis.h" #include "llvm/Analysis/MemorySSA.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/ScalarEvolutionAliasAnalysis.h" @@ -65,13 +66,128 @@ // See the comments on the definition of the specialization for details on how // it differs from the primary template. template <> -PreservedAnalyses -PassManager::run(Loop &InitialL, LoopAnalysisManager &AM, - LoopStandardAnalysisResults &AnalysisResults, - LPMUpdater &U); -extern template class PassManager; +class PassManager + : public PassInfoMixin< + PassManager> { +private: + template + using HasRunOnLoopT = decltype(std::declval().run( + std::declval(), std::declval(), + std::declval(), + std::declval())); + +public: + /// Construct a pass manager. + /// + /// If \p DebugLogging is true, we'll log our progress to llvm::dbgs(). + explicit PassManager(bool DebugLogging = false) + : DebugLogging(DebugLogging) {} + + // FIXME: These are equivalent to the default move constructor/move + // assignment. However, using = default triggers linker errors due to the + // explicit instantiations below. Find a way to use the default and remove the + // duplicated code here. + PassManager(PassManager &&Arg) + : IsLoopNestPass(std::move(Arg.IsLoopNestPass)), + LoopPasses(std::move(Arg.LoopPasses)), + LoopNestPasses(std::move(Arg.LoopNestPasses)), + DebugLogging(std::move(Arg.DebugLogging)) {} + + PassManager &operator=(PassManager &&RHS) { + IsLoopNestPass = std::move(RHS.IsLoopNestPass); + LoopPasses = std::move(RHS.LoopPasses); + LoopNestPasses = std::move(RHS.LoopNestPasses); + DebugLogging = std::move(RHS.DebugLogging); + return *this; + } + + PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, LPMUpdater &U); + + template + std::enable_if_t::value> + addPass(PassT Pass) { + using LoopPassModelT = + detail::PassModel; + IsLoopNestPass.push_back(false); + LoopPasses.emplace_back(new LoopPassModelT(std::move(Pass))); + } + + template + std::enable_if_t::value> + addPass(PassT Pass) { + using LoopNestPassModelT = + detail::PassModel; + IsLoopNestPass.push_back(true); + LoopNestPasses.emplace_back(new LoopNestPassModelT(std::move(Pass))); + } + + // Specializations of `addPass` for `RepeatedPass`. These are necessary since + // `RepeatedPass` has a templated `run` method that will result in incorrect + // behavior of `IsLoopNestPass`. + template + std::enable_if_t::value> + addPass(RepeatedPass Pass) { + using RepeatedLoopPassModelT = + detail::PassModel, PreservedAnalyses, + LoopAnalysisManager, LoopStandardAnalysisResults &, + LPMUpdater &>; + IsLoopNestPass.push_back(false); + LoopPasses.emplace_back(new RepeatedLoopPassModelT(std::move(Pass))); + } + + template + std::enable_if_t::value> + addPass(RepeatedPass Pass) { + using RepeatedLoopNestPassModelT = + detail::PassModel, PreservedAnalyses, + LoopAnalysisManager, LoopStandardAnalysisResults &, + LPMUpdater &>; + IsLoopNestPass.push_back(true); + LoopNestPasses.emplace_back( + new RepeatedLoopNestPassModelT(std::move(Pass))); + } + + static bool isRequired() { return true; } + +protected: + using LoopPassConceptT = + detail::PassConcept; + using LoopNestPassConceptT = + detail::PassConcept; + + // BitVector that identifies whether the passes are loop passes or loop-nest + // passes (true for loop-nest passes). + BitVector IsLoopNestPass; + std::vector> LoopPasses; + std::vector> LoopNestPasses; + + /// Flag indicating whether we should do debug logging. + bool DebugLogging; + + /// Run either a loop pass or a loop-nest pass. Returns `None` if + /// PassInstrumentation's BeforePass returns false. Otherwise, returns the + /// preserved analyses of the pass. + template + Optional + runSinglePass(IRUnitT &IR, PassT &Pass, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, LPMUpdater &U, + PassInstrumentation &PI); + + PreservedAnalyses runWithLoopNestPasses(Loop &L, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, + LPMUpdater &U); + PreservedAnalyses runWithoutLoopNestPasses(Loop &L, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, + LPMUpdater &U); +}; /// The Loop pass manager. /// @@ -221,6 +337,29 @@ : Worklist(Worklist), LAM(LAM) {} }; +template +Optional LoopPassManager::runSinglePass( + IRUnitT &IR, PassT &Pass, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, LPMUpdater &U, PassInstrumentation &PI) { + // Check the PassInstrumentation's BeforePass callbacks before running the + // pass, skip its execution completely if asked to (callback returns false). + if (!PI.runBeforePass(*Pass, IR)) + return None; + + PreservedAnalyses PA; + { + TimeTraceScope TimeScope(Pass->name(), IR.getName()); + PA = Pass->run(IR, AM, AR, U); + } + + // do not pass deleted Loop into the instrumentation + if (U.skipCurrentLoop()) + PI.runAfterPassInvalidated(*Pass, PA); + else + PI.runAfterPass(*Pass, IR, PA); + return PA; +} + /// Adaptor that maps from a function to its loops. /// /// Designed to allow composition of a LoopPass(Manager) and a @@ -301,8 +440,10 @@ PI.pushBeforeNonSkippedPassCallback([&LAR, &LI](StringRef PassID, Any IR) { if (isSpecialPass(PassID, {"PassManager"})) return; - assert(any_isa(IR)); - const Loop *L = any_cast(IR); + assert(any_isa(IR) || any_isa(IR)); + const Loop *L = any_isa(IR) + ? any_cast(IR) + : &any_cast(IR)->getOutermostLoop(); assert(L && "Loop should be valid for printing"); // Verify the loop structure and LCSSA form before visiting the loop. diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -1,4 +1,5 @@ if (DEFINED LLVM_HAVE_TF_AOT OR DEFINED LLVM_HAVE_TF_API) + include_directories(/usr/include/tensorflow) if (DEFINED LLVM_HAVE_TF_AOT) include(TensorFlowCompile) tfcompile(models/inliner serve action InlinerSizeModel llvm::InlinerSizeModel) diff --git a/llvm/lib/Analysis/LoopNestAnalysis.cpp b/llvm/lib/Analysis/LoopNestAnalysis.cpp --- a/llvm/lib/Analysis/LoopNestAnalysis.cpp +++ b/llvm/lib/Analysis/LoopNestAnalysis.cpp @@ -306,6 +306,8 @@ return true; } +AnalysisKey LoopNestAnalysis::Key; + raw_ostream &llvm::operator<<(raw_ostream &OS, const LoopNest &LN) { OS << "IsPerfect="; if (LN.getMaxPerfectDepth() == LN.getNestDepth()) diff --git a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp --- a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp +++ b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp @@ -12,58 +12,90 @@ using namespace llvm; -// Explicit template instantiations and specialization defininitions for core -// template typedefs. namespace llvm { -template class PassManager; /// Explicitly specialize the pass manager's run method to handle loop nest /// structure updates. -template <> PreservedAnalyses PassManager::run(Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &U) { - PreservedAnalyses PA = PreservedAnalyses::all(); if (DebugLogging) dbgs() << "Starting Loop pass manager run.\n"; + // Runs loop-nest passes only when the current loop is a top-level one. + PreservedAnalyses PA = (!L.getParentLoop() && !LoopNestPasses.empty()) + ? runWithLoopNestPasses(L, AM, AR, U) + : runWithoutLoopNestPasses(L, AM, AR, U); + + // Invalidation for the current loop should be handled above, and other loop + // analysis results shouldn't be impacted by runs over this loop. Therefore, + // the remaining analysis results in the AnalysisManager are preserved. We + // mark this with a set so that we don't need to inspect each one + // individually. + // FIXME: This isn't correct! This loop and all nested loops' analyses should + // be preserved, but unrolling should invalidate the parent loop's analyses. + PA.preserveSet>(); + + if (DebugLogging) + dbgs() << "Finished Loop pass manager run.\n"; + + return PA; +} + +PreservedAnalyses +LoopPassManager::runWithLoopNestPasses(Loop &L, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, + LPMUpdater &U) { + assert(!L.getParentLoop() && + "Loop-nest passes should only run on top-level loops."); + PreservedAnalyses PA = PreservedAnalyses::all(); + // Request PassInstrumentation from analysis manager, will use it to run // instrumenting callbacks for the passes later. PassInstrumentation PI = AM.getResult(L, AR); - for (auto &Pass : Passes) { - // Check the PassInstrumentation's BeforePass callbacks before running the - // pass, skip its execution completely if asked to (callback returns false). - if (!PI.runBeforePass(*Pass, L)) - continue; - PreservedAnalyses PassPA; - { - TimeTraceScope TimeScope(Pass->name(), L.getName()); - PassPA = Pass->run(L, AM, AR, U); + unsigned LoopPassIndex = 0, LoopNestPassIndex = 0; + std::unique_ptr LoopNestPtr; + bool IsLoopNestPtrValid = false; + + for (size_t I = 0, E = IsLoopNestPass.size(); I != E; ++I) { + Optional PassPA; + if (!IsLoopNestPass[I]) { + auto &Pass = LoopPasses[LoopPassIndex++]; + PassPA = runSinglePass(L, Pass, AM, AR, U, PI); + } else { + auto &Pass = LoopNestPasses[LoopNestPassIndex++]; + + // If the loop-nest object calculated before is no longer valid, + // re-calculate it here before running the loop-nest pass. + if (!IsLoopNestPtrValid) { + LoopNestPtr = LoopNest::getLoopNest(L, AR.SE); + IsLoopNestPtrValid = true; + } + PassPA = runSinglePass(*LoopNestPtr, Pass, AM, AR, U, PI); } - // do not pass deleted Loop into the instrumentation - if (U.skipCurrentLoop()) - PI.runAfterPassInvalidated(*Pass, PassPA); - else - PI.runAfterPass(*Pass, L, PassPA); + if (!PassPA) + continue; // If the loop was deleted, abort the run and return to the outer walk. if (U.skipCurrentLoop()) { - PA.intersect(std::move(PassPA)); + PA.intersect(std::move(*PassPA)); break; } // Update the analysis manager as each pass runs and potentially // invalidates analyses. - AM.invalidate(L, PassPA); + AM.invalidate(L, *PassPA); // Finally, we intersect the final preserved analyses to compute the // aggregate preserved set for this pass manager. - PA.intersect(std::move(PassPA)); + PA.intersect(std::move(*PassPA)); + + // Check if the current pass preserved the loop-nest object or not. + IsLoopNestPtrValid &= PassPA->getChecker().preserved(); // FIXME: Historically, the pass managers all called the LLVM context's // yield function here. We don't have a generic way to acquire the @@ -71,22 +103,46 @@ // in the new pass manager so it is currently omitted. // ...getContext().yield(); } + return PA; +} - // Invalidation for the current loop should be handled above, and other loop - // analysis results shouldn't be impacted by runs over this loop. Therefore, - // the remaining analysis results in the AnalysisManager are preserved. We - // mark this with a set so that we don't need to inspect each one - // individually. - // FIXME: This isn't correct! This loop and all nested loops' analyses should - // be preserved, but unrolling should invalidate the parent loop's analyses. - PA.preserveSet>(); +PreservedAnalyses +LoopPassManager::runWithoutLoopNestPasses(Loop &L, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, + LPMUpdater &U) { + PreservedAnalyses PA = PreservedAnalyses::all(); - if (DebugLogging) - dbgs() << "Finished Loop pass manager run.\n"; + // Request PassInstrumentation from analysis manager, will use it to run + // instrumenting callbacks for the passes later. + PassInstrumentation PI = AM.getResult(L, AR); + for (auto &Pass : LoopPasses) { + Optional PassPA = runSinglePass(L, Pass, AM, AR, U, PI); + if (!PassPA) + continue; + // If the loop was deleted, abort the run and return to the outer walk. + if (U.skipCurrentLoop()) { + PA.intersect(std::move(*PassPA)); + break; + } + + // Update the analysis manager as each pass runs and potentially + // invalidates analyses. + AM.invalidate(L, *PassPA); + + // Finally, we intersect the final preserved analyses to compute the + // aggregate preserved set for this pass manager. + PA.intersect(std::move(*PassPA)); + + // FIXME: Historically, the pass managers all called the LLVM context's + // yield function here. We don't have a generic way to acquire the + // context and it isn't yet clear what the right pattern is for yielding + // in the new pass manager so it is currently omitted. + // ...getContext().yield(); + } return PA; } -} +} // namespace llvm PrintLoopPass::PrintLoopPass() : OS(dbgs()) {} PrintLoopPass::PrintLoopPass(raw_ostream &OS, const std::string &Banner) diff --git a/llvm/unittests/IR/PassBuilderCallbacksTest.cpp b/llvm/unittests/IR/PassBuilderCallbacksTest.cpp --- a/llvm/unittests/IR/PassBuilderCallbacksTest.cpp +++ b/llvm/unittests/IR/PassBuilderCallbacksTest.cpp @@ -174,6 +174,22 @@ MockPassHandle() { setDefaults(); } }; +template <> +struct MockPassHandle + : MockPassHandleBase, LoopNest, + LoopAnalysisManager, LoopStandardAnalysisResults &, + LPMUpdater &> { + MOCK_METHOD4(run, + PreservedAnalyses(LoopNest &, LoopAnalysisManager &, + LoopStandardAnalysisResults &, LPMUpdater &)); + static void invalidateLoopNest(LoopNest &L, LoopAnalysisManager &, + LoopStandardAnalysisResults &, + LPMUpdater &Updater) { + Updater.markLoopAsDeleted(L.getOutermostLoop(), L.getName()); + } + MockPassHandle() { setDefaults(); } +}; + template <> struct MockPassHandle : MockPassHandleBase, Function> { @@ -284,6 +300,8 @@ return any_cast(WrappedIR)->getName().str(); if (any_isa(WrappedIR)) return any_cast(WrappedIR)->getName().str(); + if (any_isa(WrappedIR)) + return any_cast(WrappedIR)->getName().str(); if (any_isa(WrappedIR)) return any_cast(WrappedIR)->getName(); return ""; @@ -383,6 +401,11 @@ } }; +template +using ExtraMockPassHandle = + std::conditional_t::value, + MockPassHandle, MockPassHandle>; + template class PassBuilderCallbacksTest; /// This test fixture is shared between all the actual tests below and @@ -415,6 +438,8 @@ ModuleAnalysisManager AM; MockPassHandle PassHandle; + ExtraMockPassHandle ExtraPassHandle; + MockAnalysisHandle AnalysisHandle; static PreservedAnalyses getAnalysisResult(IRUnitT &U, AnalysisManagerT &AM, @@ -474,6 +499,8 @@ /// Parse the name of our pass mock handle if (Name == "test-transform") { PM.addPass(PassHandle.getPass()); + if (std::is_same::value) + PM.addPass(ExtraPassHandle.getPass()); return true; } return false; @@ -780,6 +807,7 @@ EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); + EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)); StringRef PipelineText = "test-transform"; ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) @@ -797,6 +825,7 @@ EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); + EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)); // PassInstrumentation calls should happen in-sequence, in the same order // as passes/analyses are scheduled. @@ -820,6 +849,19 @@ runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"), _)) .InSequence(PISequence); + EXPECT_CALL(CallbacksHandle, + runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"), + HasName("loop"))) + .InSequence(PISequence); + EXPECT_CALL(CallbacksHandle, + runBeforeNonSkippedPass( + HasNameRegex("MockPassHandle<.*LoopNest>"), HasName("loop"))) + .InSequence(PISequence); + EXPECT_CALL(CallbacksHandle, + runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"), + HasName("loop"), _)) + .InSequence(PISequence); + // Our mock pass does not invalidate IR. EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) @@ -886,6 +928,77 @@ PM.run(*M, AM); } +TEST_F(LoopCallbacksTest, InstrumentedInvalidatingLoopNestPasses) { + CallbacksHandle.registerPassInstrumentation(); + // Non-mock instrumentation not specifically mentioned below can be ignored. + CallbacksHandle.ignoreNonMockPassInstrumentation(""); + CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); + CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); + + EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); + EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) + .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); + EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)) + .WillOnce(DoAll(Invoke(ExtraPassHandle.invalidateLoopNest), + Invoke([&](LoopNest &, LoopAnalysisManager &, + LoopStandardAnalysisResults &, LPMUpdater &) { + return PreservedAnalyses::all(); + }))); + + // PassInstrumentation calls should happen in-sequence, in the same order + // as passes/analyses are scheduled. + ::testing::Sequence PISequence; + EXPECT_CALL(CallbacksHandle, + runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))) + .InSequence(PISequence); + EXPECT_CALL( + CallbacksHandle, + runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))) + .InSequence(PISequence); + EXPECT_CALL( + CallbacksHandle, + runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) + .InSequence(PISequence); + EXPECT_CALL( + CallbacksHandle, + runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) + .InSequence(PISequence); + EXPECT_CALL(CallbacksHandle, + runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"), _)) + .InSequence(PISequence); + + EXPECT_CALL(CallbacksHandle, + runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"), + HasName("loop"))) + .InSequence(PISequence); + EXPECT_CALL(CallbacksHandle, + runBeforeNonSkippedPass( + HasNameRegex("MockPassHandle<.*LoopNest>"), HasName("loop"))) + .InSequence(PISequence); + EXPECT_CALL( + CallbacksHandle, + runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*LoopNest>"), _)) + .InSequence(PISequence); + + EXPECT_CALL(CallbacksHandle, + runAfterPassInvalidated(HasNameRegex("^PassManager"), _)) + .InSequence(PISequence); + + // Our mock pass invalidates IR, thus normal runAfterPass is never called. + EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( + HasNameRegex("MockPassHandle<.*Loop>"), _)) + .Times(0); + EXPECT_CALL(CallbacksHandle, + runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"), + HasName("loop"), _)) + .Times(0); + + StringRef PipelineText = "test-transform"; + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) + << "Pipeline was: " << PipelineText; + PM.run(*M, AM); +} + TEST_F(LoopCallbacksTest, InstrumentedSkippedPasses) { CallbacksHandle.registerPassInstrumentation(); // Non-mock instrumentation run here can safely be ignored. @@ -894,28 +1007,51 @@ CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); // Skip the pass by returning false. + EXPECT_CALL( + CallbacksHandle, + runBeforePass(HasNameRegex("MockPassHandle<.*Loop>"), HasName("loop"))) + .WillOnce(Return(false)); + EXPECT_CALL(CallbacksHandle, - runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))) + runBeforeSkippedPass(HasNameRegex("MockPassHandle<.*Loop>"), + HasName("loop"))) + .Times(1); + + EXPECT_CALL(CallbacksHandle, + runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"), + HasName("loop"))) .WillOnce(Return(false)); - EXPECT_CALL( - CallbacksHandle, - runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))) + EXPECT_CALL(CallbacksHandle, + runBeforeSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>"), + HasName("loop"))) .Times(1); EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)).Times(0); EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).Times(0); + EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)).Times(0); // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis // as well. - EXPECT_CALL(CallbacksHandle, - runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)) + EXPECT_CALL(CallbacksHandle, runBeforeNonSkippedPass( + HasNameRegex("MockPassHandle<.*Loop>"), _)) .Times(0); EXPECT_CALL(CallbacksHandle, - runAfterPass(HasNameRegex("MockPassHandle"), _, _)) + runAfterPass(HasNameRegex("MockPassHandle<.*Loop>"), _, _)) + .Times(0); + EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( + HasNameRegex("MockPassHandle<.*Loop>"), _)) + .Times(0); + EXPECT_CALL( + CallbacksHandle, + runBeforeNonSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>"), _)) .Times(0); EXPECT_CALL(CallbacksHandle, - runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) + runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"), _, _)) + .Times(0); + EXPECT_CALL( + CallbacksHandle, + runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*LoopNest>"), _)) .Times(0); EXPECT_CALL(CallbacksHandle, runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) diff --git a/llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp b/llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp --- a/llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp +++ b/llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp @@ -190,6 +190,16 @@ MockLoopPassHandle() { setDefaults(); } }; +struct MockLoopNestPassHandle + : MockPassHandleBase { + MOCK_METHOD4(run, + PreservedAnalyses(LoopNest &, LoopAnalysisManager &, + LoopStandardAnalysisResults &, LPMUpdater &)); + + MockLoopNestPassHandle() { setDefaults(); } +}; + struct MockFunctionPassHandle : MockPassHandleBase { MOCK_METHOD2(run, PreservedAnalyses(Function &, FunctionAnalysisManager &)); @@ -239,6 +249,7 @@ MockLoopAnalysisHandle MLAHandle; MockLoopPassHandle MLPHandle; + MockLoopNestPassHandle MLNPHandle; MockFunctionPassHandle MFPHandle; MockModulePassHandle MMPHandle; @@ -1584,4 +1595,31 @@ MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); MPM.run(*M, MAM); } + +TEST_F(LoopPassManagerTest, HandleLoopNestPass) { + ::testing::InSequence MakeExpectationsSequenced; + + EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)).Times(2); + EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)).Times(2); + EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)); + EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)); + EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)); + EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)); + EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)); + EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)); + EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)); + EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)); + + LoopPassManager LPM(true); + LPM.addPass(MLPHandle.getPass()); + LPM.addPass(MLNPHandle.getPass()); + LPM.addPass(MLPHandle.getPass()); + LPM.addPass(MLNPHandle.getPass()); + + ModulePassManager MPM(true); + MPM.addPass(createModuleToFunctionPassAdaptor( + createFunctionToLoopPassAdaptor(std::move(LPM)))); + MPM.run(*M, MAM); } + +} // namespace