Author: Ta-Wei Tu Date: 2020-12-22T08:47:38+08:00 New Revision: d7a6f3a1056a5f5212fa561a909fcfa502126074
URL: https://github.com/llvm/llvm-project/commit/d7a6f3a1056a5f5212fa561a909fcfa502126074 DIFF: https://github.com/llvm/llvm-project/commit/d7a6f3a1056a5f5212fa561a909fcfa502126074.diff LOG: [LoopNest] Extend `LPMUpdater` and adaptor to handle loop-nest passes This is a follow-up patch of D87045. The patch implements "loop-nest mode" for `LPMUpdater` and `FunctionToLoopPassAdaptor` in which only top-level loops are operated. `createFunctionToLoopPassAdaptor` decides whether the returned adaptor is in loop-nest mode or not based on the given pass. If the pass is a loop-nest pass or the pass is a `LoopPassManager` which contains only loop-nest passes, the loop-nest version of adaptor is returned; otherwise, the normal (loop) version of adaptor is returned. Reviewed By: Whitney Differential Revision: https://reviews.llvm.org/D87531 Added: Modified: llvm/include/llvm/Transforms/Scalar/LoopPassManager.h llvm/lib/Transforms/Scalar/LoopPassManager.cpp llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp Removed: ################################################################################ diff --git a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h index a0bef89b36cf..2a342fcda3c2 100644 --- a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h +++ b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h @@ -53,6 +53,16 @@ namespace llvm { // Forward declarations of an update tracking API used in the pass manager. class LPMUpdater; +namespace { + +template <typename PassT> +using HasRunOnLoopT = decltype(std::declval<PassT>().run( + std::declval<Loop &>(), std::declval<LoopAnalysisManager &>(), + std::declval<LoopStandardAnalysisResults &>(), + std::declval<LPMUpdater &>())); + +} // namespace + // Explicit specialization and instantiation declarations for the pass manager. // See the comments on the definition of the specialization for details on how // it diff ers from the primary template. @@ -62,13 +72,6 @@ class PassManager<Loop, LoopAnalysisManager, LoopStandardAnalysisResults &, : public PassInfoMixin< PassManager<Loop, LoopAnalysisManager, LoopStandardAnalysisResults &, LPMUpdater &>> { -private: - template <typename PassT> - using HasRunOnLoopT = decltype(std::declval<PassT>().run( - std::declval<Loop &>(), std::declval<LoopAnalysisManager &>(), - std::declval<LoopStandardAnalysisResults &>(), - std::declval<LPMUpdater &>())); - public: /// Construct a pass manager. /// @@ -154,6 +157,9 @@ class PassManager<Loop, LoopAnalysisManager, LoopStandardAnalysisResults &, static bool isRequired() { return true; } + size_t getNumLoopPasses() const { return LoopPasses.size(); } + size_t getNumLoopNestPasses() const { return LoopNestPasses.size(); } + protected: using LoopPassConceptT = detail::PassConcept<Loop, LoopAnalysisManager, @@ -227,6 +233,13 @@ class FunctionToLoopPassAdaptor; /// A reference to an instance of this class is passed as an argument to each /// Loop pass, and Loop passes should use it to update LPM infrastructure if /// they modify the loop nest structure. +/// +/// \c LPMUpdater comes with two modes: the loop mode and the loop-nest mode. In +/// loop mode, all the loops in the function will be pushed into the worklist +/// and when new loops are added to the pipeline, their subloops are also +/// inserted recursively. On the other hand, in loop-nest mode, only top-level +/// loops are contained in the worklist and the addition of new (top-level) +/// loops will not trigger the addition of their subloops. class LPMUpdater { public: /// This can be queried by loop passes which run other loop passes (like pass @@ -248,6 +261,8 @@ class LPMUpdater { /// state, this routine will mark that the current loop should be skipped by /// the rest of the pass management infrastructure. void markLoopAsDeleted(Loop &L, llvm::StringRef Name) { + assert((!LoopNestMode || L.isOutermost()) && + "L should be a top-level loop in loop-nest mode."); LAM.clear(L, Name); assert((&L == CurrentL || CurrentL->contains(&L)) && "Cannot delete a loop outside of the " @@ -263,6 +278,8 @@ class LPMUpdater { /// loops within them will be visited in postorder as usual for the loop pass /// manager. void addChildLoops(ArrayRef<Loop *> NewChildLoops) { + assert(!LoopNestMode && + "Child loops should not be pushed in loop-nest mode."); // Insert ourselves back into the worklist first, as this loop should be // revisited after all the children have been processed. Worklist.insert(CurrentL); @@ -294,7 +311,10 @@ class LPMUpdater { "All of the new loops must be siblings of the current loop!"); #endif - appendLoopsToWorklist(NewSibLoops, Worklist); + if (LoopNestMode) + Worklist.insert(NewSibLoops); + else + appendLoopsToWorklist(NewSibLoops, Worklist); // No need to skip the current loop or revisit it, as sibling loops // shouldn't impact anything. @@ -324,6 +344,7 @@ class LPMUpdater { Loop *CurrentL; bool SkipCurrentLoop; + const bool LoopNestMode; #ifndef NDEBUG // In debug builds we also track the parent loop to implement asserts even in @@ -332,8 +353,8 @@ class LPMUpdater { #endif LPMUpdater(SmallPriorityWorklist<Loop *, 4> &Worklist, - LoopAnalysisManager &LAM) - : Worklist(Worklist), LAM(LAM) {} + LoopAnalysisManager &LAM, bool LoopNestMode = false) + : Worklist(Worklist), LAM(LAM), LoopNestMode(LoopNestMode) {} }; template <typename IRUnitT, typename PassT> @@ -366,6 +387,15 @@ Optional<PreservedAnalyses> LoopPassManager::runSinglePass( /// 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. +/// +/// The adaptor comes with two modes: the loop mode and the loop-nest mode, and +/// the worklist updater lived inside will be in the same mode as the adaptor +/// (refer to the documentation of \c LPMUpdater for more detailed explanation). +/// Specifically, in loop mode, all loops in the funciton will be pushed into +/// the worklist and processed by \p Pass, while only top-level loops are +/// processed in loop-nest mode. Please refer to the various specializations of +/// \fn createLoopFunctionToLoopPassAdaptor to see when loop mode and loop-nest +/// mode are used. class FunctionToLoopPassAdaptor : public PassInfoMixin<FunctionToLoopPassAdaptor> { public: @@ -376,10 +406,12 @@ class FunctionToLoopPassAdaptor explicit FunctionToLoopPassAdaptor(std::unique_ptr<PassConceptT> Pass, bool UseMemorySSA = false, bool UseBlockFrequencyInfo = false, - bool DebugLogging = false) + bool DebugLogging = false, + bool LoopNestMode = false) : Pass(std::move(Pass)), LoopCanonicalizationFPM(DebugLogging), UseMemorySSA(UseMemorySSA), - UseBlockFrequencyInfo(UseBlockFrequencyInfo) { + UseBlockFrequencyInfo(UseBlockFrequencyInfo), + LoopNestMode(LoopNestMode) { LoopCanonicalizationFPM.addPass(LoopSimplifyPass()); LoopCanonicalizationFPM.addPass(LCSSAPass()); } @@ -389,6 +421,8 @@ class FunctionToLoopPassAdaptor static bool isRequired() { return true; } + bool isLoopNestMode() const { return LoopNestMode; } + private: std::unique_ptr<PassConceptT> Pass; @@ -396,12 +430,16 @@ class FunctionToLoopPassAdaptor bool UseMemorySSA = false; bool UseBlockFrequencyInfo = false; + const bool LoopNestMode; }; /// A function to deduce a loop pass type and wrap it in the templated /// adaptor. +/// +/// If \p Pass is a loop pass, the returned adaptor will be in loop mode. template <typename LoopPassT> -FunctionToLoopPassAdaptor +inline std::enable_if_t<is_detected<HasRunOnLoopT, LoopPassT>::value, + FunctionToLoopPassAdaptor> createFunctionToLoopPassAdaptor(LoopPassT Pass, bool UseMemorySSA = false, bool UseBlockFrequencyInfo = false, bool DebugLogging = false) { @@ -410,7 +448,46 @@ createFunctionToLoopPassAdaptor(LoopPassT Pass, bool UseMemorySSA = false, LoopStandardAnalysisResults &, LPMUpdater &>; return FunctionToLoopPassAdaptor( std::make_unique<PassModelT>(std::move(Pass)), UseMemorySSA, - UseBlockFrequencyInfo, DebugLogging); + UseBlockFrequencyInfo, DebugLogging, false); +} + +/// If \p Pass is a loop-nest pass, \p Pass will first be wrapped into a +/// \c LoopPassManager and the returned adaptor will be in loop-nest mode. +template <typename LoopNestPassT> +inline std::enable_if_t<!is_detected<HasRunOnLoopT, LoopNestPassT>::value, + FunctionToLoopPassAdaptor> +createFunctionToLoopPassAdaptor(LoopNestPassT Pass, bool UseMemorySSA = false, + bool UseBlockFrequencyInfo = false, + bool DebugLogging = false) { + LoopPassManager LPM(DebugLogging); + LPM.addPass(std::move(Pass)); + using PassModelT = + detail::PassModel<Loop, LoopPassManager, PreservedAnalyses, + LoopAnalysisManager, LoopStandardAnalysisResults &, + LPMUpdater &>; + return FunctionToLoopPassAdaptor(std::make_unique<PassModelT>(std::move(LPM)), + UseMemorySSA, UseBlockFrequencyInfo, + DebugLogging, true); +} + +/// If \p Pass is an instance of \c LoopPassManager, the returned adaptor will +/// be in loop-nest mode if the pass manager contains only loop-nest passes. +template <> +inline FunctionToLoopPassAdaptor +createFunctionToLoopPassAdaptor<LoopPassManager>(LoopPassManager LPM, + bool UseMemorySSA, + bool UseBlockFrequencyInfo, + bool DebugLogging) { + // Check if LPM contains any loop pass and if it does not, returns an adaptor + // in loop-nest mode. + using PassModelT = + detail::PassModel<Loop, LoopPassManager, PreservedAnalyses, + LoopAnalysisManager, LoopStandardAnalysisResults &, + LPMUpdater &>; + bool LoopNestMode = (LPM.getNumLoopPasses() == 0); + return FunctionToLoopPassAdaptor(std::make_unique<PassModelT>(std::move(LPM)), + UseMemorySSA, UseBlockFrequencyInfo, + DebugLogging, LoopNestMode); } /// Pass for printing a loop's contents as textual IR. diff --git a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp index 5bc41552e1a3..3fe8e7259114 100644 --- a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp +++ b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp @@ -222,11 +222,16 @@ PreservedAnalyses FunctionToLoopPassAdaptor::run(Function &F, // Register the worklist and loop analysis manager so that loop passes can // update them when they mutate the loop nest structure. - LPMUpdater Updater(Worklist, LAM); + LPMUpdater Updater(Worklist, LAM, LoopNestMode); // Add the loop nests in the reverse order of LoopInfo. See method // declaration. - appendLoopsToWorklist(LI, Worklist); + if (!LoopNestMode) { + appendLoopsToWorklist(LI, Worklist); + } else { + for (Loop *L : LI) + Worklist.insert(L); + } #ifndef NDEBUG PI.pushBeforeNonSkippedPassCallback([&LAR, &LI](StringRef PassID, Any IR) { @@ -247,6 +252,8 @@ PreservedAnalyses FunctionToLoopPassAdaptor::run(Function &F, do { Loop *L = Worklist.pop_back_val(); + assert(!(LoopNestMode && L->getParentLoop()) && + "L should be a top-level loop in loop-nest mode."); // Reset the update structure for this loop. Updater.CurrentL = L; diff --git a/llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp b/llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp index fc41bfa00ead..a03d43b10ba4 100644 --- a/llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp +++ b/llvm/unittests/Transforms/Scalar/LoopPassManagerTest.cpp @@ -1603,28 +1603,69 @@ TEST_F(LoopPassManagerTest, LoopDeletion) { } TEST_F(LoopPassManagerTest, HandleLoopNestPass) { - ::testing::InSequence MakeExpectationsSequenced; + ::testing::Sequence FSequence, GSequence; - 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"), _, _, _)); + EXPECT_CALL(MLPHandle, run(HasName("loop.0.0"), _, _, _)) + .Times(2) + .InSequence(FSequence); + EXPECT_CALL(MLPHandle, run(HasName("loop.0.1"), _, _, _)) + .Times(2) + .InSequence(FSequence); + EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence); + EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)) + .InSequence(FSequence); + EXPECT_CALL(MLPHandle, run(HasName("loop.0"), _, _, _)).InSequence(FSequence); + EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)) + .InSequence(FSequence); + EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)) + .InSequence(GSequence); + EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)) + .InSequence(GSequence); + EXPECT_CALL(MLPHandle, run(HasName("loop.g.0"), _, _, _)) + .InSequence(GSequence); + EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)) + .InSequence(GSequence); - LoopPassManager LPM(true); - LPM.addPass(MLPHandle.getPass()); - LPM.addPass(MLNPHandle.getPass()); - LPM.addPass(MLPHandle.getPass()); - LPM.addPass(MLNPHandle.getPass()); + EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)) + .InSequence(FSequence); + EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)) + .InSequence(GSequence); + + EXPECT_CALL(MLNPHandle, run(HasName("loop.0"), _, _, _)) + .InSequence(FSequence); + EXPECT_CALL(MLNPHandle, run(HasName("loop.g.0"), _, _, _)) + .InSequence(GSequence); ModulePassManager MPM(true); - MPM.addPass(createModuleToFunctionPassAdaptor( - createFunctionToLoopPassAdaptor(std::move(LPM)))); + FunctionPassManager FPM(true); + + { + LoopPassManager LPM(true); + LPM.addPass(MLPHandle.getPass()); + LPM.addPass(MLNPHandle.getPass()); + LPM.addPass(MLPHandle.getPass()); + LPM.addPass(MLNPHandle.getPass()); + + auto Adaptor = createFunctionToLoopPassAdaptor(std::move(LPM)); + ASSERT_FALSE(Adaptor.isLoopNestMode()); + FPM.addPass(std::move(Adaptor)); + } + + { + auto Adaptor = createFunctionToLoopPassAdaptor(MLNPHandle.getPass()); + ASSERT_TRUE(Adaptor.isLoopNestMode()); + FPM.addPass(std::move(Adaptor)); + } + + { + LoopPassManager LPM(true); + LPM.addPass(MLNPHandle.getPass()); + auto Adaptor = createFunctionToLoopPassAdaptor(MLNPHandle.getPass()); + ASSERT_TRUE(Adaptor.isLoopNestMode()); + FPM.addPass(std::move(Adaptor)); + } + + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); MPM.run(*M, MAM); } _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits