[llvm-branch-commits] [polly] [Polly] Update ScopInliner for NPM (PR #125427)

Michael Kruse via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Feb 4 16:27:53 PST 2025


https://github.com/Meinersbur updated https://github.com/llvm/llvm-project/pull/125427

>From 06b025db36dff8c1a3b0b22ae884d6506611f455 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Sun, 2 Feb 2025 18:48:32 +0100
Subject: [PATCH 1/2] Update ScopInliner to support NPM

---
 polly/docs/ReleaseNotes.rst                   |   2 +
 polly/include/polly/LinkAllPasses.h           |   2 +-
 polly/include/polly/ScopInliner.h             |  34 ++++
 polly/lib/Support/PollyPasses.def             |   6 +
 polly/lib/Support/RegisterPasses.cpp          |  41 ++++-
 polly/lib/Transform/ScopInliner.cpp           | 159 +++++++++++-------
 polly/test/ScopInliner/ignore-declares.ll     |   3 +-
 polly/test/ScopInliner/invariant-load-func.ll |   5 +-
 polly/test/ScopInliner/simple-inline-loop.ll  |   3 +-
 9 files changed, 184 insertions(+), 71 deletions(-)
 create mode 100644 polly/include/polly/ScopInliner.h

diff --git a/polly/docs/ReleaseNotes.rst b/polly/docs/ReleaseNotes.rst
index f7c9689089be27..f5ea47b69cf02b 100644
--- a/polly/docs/ReleaseNotes.rst
+++ b/polly/docs/ReleaseNotes.rst
@@ -11,3 +11,5 @@ In Polly |version| the following important changes have been incorporated.
     the new features that have recently been committed to our development
     branch.
 
+ * ScopInliner has been updated for the New Pass Manager.
+
diff --git a/polly/include/polly/LinkAllPasses.h b/polly/include/polly/LinkAllPasses.h
index 54e7c5a43ab93f..65846653f98e5f 100644
--- a/polly/include/polly/LinkAllPasses.h
+++ b/polly/include/polly/LinkAllPasses.h
@@ -120,7 +120,7 @@ struct PollyForcePassLinking {
 
 namespace llvm {
 void initializeCodePreparationPass(llvm::PassRegistry &);
-void initializeScopInlinerPass(llvm::PassRegistry &);
+void initializeScopInlinerWrapperPassPass(llvm::PassRegistry &);
 void initializeScopDetectionWrapperPassPass(llvm::PassRegistry &);
 void initializeScopDetectionPrinterLegacyPassPass(llvm::PassRegistry &);
 void initializeScopInfoRegionPassPass(PassRegistry &);
diff --git a/polly/include/polly/ScopInliner.h b/polly/include/polly/ScopInliner.h
new file mode 100644
index 00000000000000..014667804330fb
--- /dev/null
+++ b/polly/include/polly/ScopInliner.h
@@ -0,0 +1,34 @@
+//===------ ScopInliner.h ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef POLLY_POLLYINLINER_H
+#define POLLY_POLLYINLINER_H
+
+#include "llvm/Analysis/CGSCCPassManager.h"
+#include "llvm/Analysis/LazyCallGraph.h"
+#include "llvm/IR/PassManager.h"
+
+namespace polly {
+class ScopInlinerPass : public llvm::PassInfoMixin<ScopInlinerPass> {
+public:
+  ScopInlinerPass();
+
+  llvm::PreservedAnalyses run(llvm::LazyCallGraph::SCC &C,
+                              llvm::CGSCCAnalysisManager &AM,
+                              llvm::LazyCallGraph &CG,
+                              llvm::CGSCCUpdateResult &UR);
+};
+
+llvm::Pass *createScopInlinerWrapperPass();
+} // namespace polly
+
+namespace llvm {
+void initializeScopInlinerWrapperPassPass(llvm::PassRegistry &);
+}
+
+#endif /* POLLY_POLLYINLINER_H */
diff --git a/polly/lib/Support/PollyPasses.def b/polly/lib/Support/PollyPasses.def
index e068f31fdb703c..2c792a5867100f 100644
--- a/polly/lib/Support/PollyPasses.def
+++ b/polly/lib/Support/PollyPasses.def
@@ -1,3 +1,9 @@
+#ifndef CGSCC_PASS
+#define CGSCC_PASS(NAME, CREATE_PASS, PARSER)
+#endif
+CGSCC_PASS("polly-inline", ScopInlinerPass(), parseNoOptions)
+#undef CGSCC_PASS
+
 #ifndef FUNCTION_ANALYSIS
 #define FUNCTION_ANALYSIS(NAME, CREATE_PASS)
 #endif
diff --git a/polly/lib/Support/RegisterPasses.cpp b/polly/lib/Support/RegisterPasses.cpp
index a46e61aafbeb75..3ace336cb588be 100644
--- a/polly/lib/Support/RegisterPasses.cpp
+++ b/polly/lib/Support/RegisterPasses.cpp
@@ -35,6 +35,7 @@
 #include "polly/ScopDetection.h"
 #include "polly/ScopGraphPrinter.h"
 #include "polly/ScopInfo.h"
+#include "polly/ScopInliner.h"
 #include "polly/Simplify.h"
 #include "polly/Support/DumpFunctionPass.h"
 #include "polly/Support/DumpModulePass.h"
@@ -46,10 +47,13 @@
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/Passes/PassPlugin.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Transforms/IPO.h"
 
+using namespace llvm;
 namespace cl = llvm::cl;
+using namespace polly;
 
 using llvm::FunctionPassManager;
 using llvm::OptimizationLevel;
@@ -233,7 +237,7 @@ void initializePollyPasses(llvm::PassRegistry &Registry) {
   initializePollyCanonicalizePass(Registry);
   initializeScopDetectionWrapperPassPass(Registry);
   initializeScopDetectionPrinterLegacyPassPass(Registry);
-  initializeScopInlinerPass(Registry);
+  initializeScopInlinerWrapperPassPass(Registry);
   initializeScopInfoRegionPassPass(Registry);
   initializeScopInfoPrinterLegacyRegionPassPass(Registry);
   initializeScopInfoWrapperPassPass(Registry);
@@ -434,6 +438,16 @@ static void buildLatePollyPipeline(FunctionPassManager &PM,
         false);
 }
 
+static llvm::Expected<std::monostate> parseNoOptions(StringRef Params) {
+  if (!Params.empty())
+    return make_error<StringError>(
+        formatv("'{0}' passed to pass that does not take any options", Params)
+            .str(),
+        inconvertibleErrorCode());
+
+  return std::monostate{};
+}
+
 static OwningScopAnalysisManagerFunctionProxy
 createScopAnalyses(FunctionAnalysisManager &FAM,
                    PassInstrumentationCallbacks *PIC) {
@@ -461,6 +475,25 @@ static void registerFunctionAnalyses(FunctionAnalysisManager &FAM,
   FAM.registerPass([&FAM, PIC] { return createScopAnalyses(FAM, PIC); });
 }
 
+static llvm::Expected<bool>
+parseCGPipeline(StringRef Name, llvm::CGSCCPassManager &CGPM,
+                PassInstrumentationCallbacks *PIC,
+                ArrayRef<PassBuilder::PipelineElement> Pipeline) {
+  assert(Pipeline.empty());
+
+#define CGSCC_PASS(NAME, CREATE_PASS, PARSER)                                  \
+  if (PassBuilder::checkParametrizedPassName(Name, NAME)) {                    \
+    auto Params = PassBuilder::parsePassParameters(PARSER, Name, NAME);        \
+    if (!Params)                                                               \
+      return Params.takeError();                                               \
+    CGPM.addPass(CREATE_PASS);                                                 \
+    return true;                                                               \
+  }
+#include "PollyPasses.def"
+
+  return false;
+}
+
 static bool
 parseFunctionPipeline(StringRef Name, FunctionPassManager &FPM,
                       ArrayRef<PassBuilder::PipelineElement> Pipeline) {
@@ -590,6 +623,12 @@ parseTopLevelPipeline(llvm::ModulePassManager &MPM,
 /// handle LICMed code to make it useful.
 void registerPollyPasses(PassBuilder &PB) {
   PassInstrumentationCallbacks *PIC = PB.getPassInstrumentationCallbacks();
+  PB.registerPipelineParsingCallback(
+      [PIC](StringRef Name, CGSCCPassManager &CGPM,
+            ArrayRef<PassBuilder::PipelineElement> Pipeline) -> bool {
+        ExitOnError Err("Unable to parse Polly call graph pass: ");
+        return Err(parseCGPipeline(Name, CGPM, PIC, Pipeline));
+      });
   PB.registerAnalysisRegistrationCallback([PIC](FunctionAnalysisManager &FAM) {
     registerFunctionAnalyses(FAM, PIC);
   });
diff --git a/polly/lib/Transform/ScopInliner.cpp b/polly/lib/Transform/ScopInliner.cpp
index b78206c1e40bad..c04ba3498339ed 100644
--- a/polly/lib/Transform/ScopInliner.cpp
+++ b/polly/lib/Transform/ScopInliner.cpp
@@ -13,10 +13,14 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "polly/LinkAllPasses.h"
+#include "polly/ScopInliner.h"
 #include "polly/ScopDetection.h"
+#include "polly/ScopInliner.h"
 #include "llvm/Analysis/CallGraph.h"
 #include "llvm/Analysis/CallGraphSCCPass.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/RegionInfo.h"
+#include "llvm/IR/Dominators.h"
 #include "llvm/IR/PassManager.h"
 #include "llvm/Passes/PassBuilder.h"
 #include "llvm/Transforms/IPO/AlwaysInliner.h"
@@ -28,13 +32,77 @@ using namespace llvm;
 using namespace polly;
 
 namespace {
-class ScopInliner final : public CallGraphSCCPass {
+
+/// Inliner implementation that works with both, LPM (using SCC_t=CallGraph) and
+/// NPM (using SCC_t=LazyCallGraph::SCC)
+template <typename SCC_t> bool runScopInlinerImpl(Function *F, SCC_t &SCC) {
+  // We do not try to inline non-trivial SCCs because this would lead to
+  // "infinite" inlining if we are not careful.
+  if (SCC.size() > 1)
+    return false;
+  assert(SCC.size() == 1 && "found empty SCC");
+
+  // If the function is a nullptr, or the function is a declaration.
+  if (!F)
+    return false;
+  if (F->isDeclaration()) {
+    POLLY_DEBUG(dbgs() << "Skipping " << F->getName()
+                       << "because it is a declaration.\n");
+    return false;
+  }
+
+  PassBuilder PB;
+  // Populate analysis managers and register Polly-specific analyses.
+  LoopAnalysisManager LAM;
+  FunctionAnalysisManager FAM;
+  CGSCCAnalysisManager CGAM;
+  ModuleAnalysisManager MAM;
+  PB.registerModuleAnalyses(MAM);
+  PB.registerCGSCCAnalyses(CGAM);
+  PB.registerFunctionAnalyses(FAM);
+  PB.registerLoopAnalyses(LAM);
+  PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
+
+  auto &DT = FAM.getResult<DominatorTreeAnalysis>(*F);
+  auto &SE = FAM.getResult<ScalarEvolutionAnalysis>(*F);
+  auto &LI = FAM.getResult<LoopAnalysis>(*F);
+  auto &RI = FAM.getResult<RegionInfoAnalysis>(*F);
+  auto &AA = FAM.getResult<AAManager>(*F);
+  auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F);
+  ScopDetection SD(DT, SE, LI, RI, AA, ORE);
+  SD.detect(*F);
+
+  const bool HasScopAsTopLevelRegion =
+      SD.ValidRegions.contains(RI.getTopLevelRegion());
+
+  bool Changed = false;
+  if (HasScopAsTopLevelRegion) {
+    POLLY_DEBUG(dbgs() << "Skipping " << F->getName()
+                       << " has scop as top level region");
+    F->addFnAttr(llvm::Attribute::AlwaysInline);
+
+    ModulePassManager MPM;
+    MPM.addPass(AlwaysInlinerPass());
+    Module *M = F->getParent();
+    assert(M && "Function has illegal module");
+    PreservedAnalyses PA = MPM.run(*M, MAM);
+    if (!PA.areAllPreserved())
+      Changed = true;
+  } else {
+    POLLY_DEBUG(dbgs() << F->getName()
+                       << " does NOT have scop as top level region\n");
+  }
+
+  return Changed;
+}
+
+class ScopInlinerWrapperPass final : public CallGraphSCCPass {
   using llvm::Pass::doInitialization;
 
 public:
   static char ID;
 
-  ScopInliner() : CallGraphSCCPass(ID) {}
+  ScopInlinerWrapperPass() : CallGraphSCCPass(ID) {}
 
   bool doInitialization(CallGraph &CG) override {
     if (!polly::PollyAllowFullFunction) {
@@ -50,60 +118,8 @@ class ScopInliner final : public CallGraphSCCPass {
   }
 
   bool runOnSCC(CallGraphSCC &SCC) override {
-    // We do not try to inline non-trivial SCCs because this would lead to
-    // "infinite" inlining if we are not careful.
-    if (SCC.size() > 1)
-      return false;
-    assert(SCC.size() == 1 && "found empty SCC");
     Function *F = (*SCC.begin())->getFunction();
-
-    // If the function is a nullptr, or the function is a declaration.
-    if (!F)
-      return false;
-    if (F->isDeclaration()) {
-      POLLY_DEBUG(dbgs() << "Skipping " << F->getName()
-                         << "because it is a declaration.\n");
-      return false;
-    }
-
-    PassBuilder PB;
-    // Populate analysis managers and register Polly-specific analyses.
-    LoopAnalysisManager LAM;
-    FunctionAnalysisManager FAM;
-    CGSCCAnalysisManager CGAM;
-    ModuleAnalysisManager MAM;
-    FAM.registerPass([] { return ScopAnalysis(); });
-    PB.registerModuleAnalyses(MAM);
-    PB.registerCGSCCAnalyses(CGAM);
-    PB.registerFunctionAnalyses(FAM);
-    PB.registerLoopAnalyses(LAM);
-    PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
-
-    RegionInfo &RI = FAM.getResult<RegionInfoAnalysis>(*F);
-    ScopDetection &SD = FAM.getResult<ScopAnalysis>(*F);
-
-    const bool HasScopAsTopLevelRegion =
-        SD.ValidRegions.contains(RI.getTopLevelRegion());
-
-    bool Changed = false;
-    if (HasScopAsTopLevelRegion) {
-      POLLY_DEBUG(dbgs() << "Skipping " << F->getName()
-                         << " has scop as top level region");
-      F->addFnAttr(llvm::Attribute::AlwaysInline);
-
-      ModulePassManager MPM;
-      MPM.addPass(AlwaysInlinerPass());
-      Module *M = F->getParent();
-      assert(M && "Function has illegal module");
-      PreservedAnalyses PA = MPM.run(*M, MAM);
-      if (!PA.areAllPreserved())
-        Changed = true;
-    } else {
-      POLLY_DEBUG(dbgs() << F->getName()
-                         << " does NOT have scop as top level region\n");
-    }
-
-    return Changed;
+    return runScopInlinerImpl(F, SCC);
   };
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
@@ -111,18 +127,39 @@ class ScopInliner final : public CallGraphSCCPass {
   }
 };
 } // namespace
-char ScopInliner::ID;
+char ScopInlinerWrapperPass::ID;
 
-Pass *polly::createScopInlinerPass() {
-  ScopInliner *pass = new ScopInliner();
+Pass *polly::createScopInlinerWrapperPass() {
+  ScopInlinerWrapperPass *pass = new ScopInlinerWrapperPass();
   return pass;
 }
 
 INITIALIZE_PASS_BEGIN(
-    ScopInliner, "polly-scop-inliner",
+    ScopInlinerWrapperPass, "polly-scop-inliner",
     "inline functions based on how much of the function is a scop.", false,
     false)
 INITIALIZE_PASS_END(
-    ScopInliner, "polly-scop-inliner",
+    ScopInlinerWrapperPass, "polly-scop-inliner",
     "inline functions based on how much of the function is a scop.", false,
     false)
+
+polly::ScopInlinerPass::ScopInlinerPass() {
+  if (!polly::PollyAllowFullFunction) {
+    report_fatal_error(
+        "Aborting from ScopInliner because it only makes sense to run with "
+        "-polly-allow-full-function. "
+        "The heurtistic for ScopInliner checks that the full function is a "
+        "Scop, which happens if and only if polly-allow-full-function is "
+        " enabled. "
+        " If not, the entry block is not included in the Scop");
+  }
+}
+
+PreservedAnalyses polly::ScopInlinerPass::run(llvm::LazyCallGraph::SCC &SCC,
+                                              llvm::CGSCCAnalysisManager &AM,
+                                              llvm::LazyCallGraph &CG,
+                                              llvm::CGSCCUpdateResult &UR) {
+  Function *F = &SCC.begin()->getFunction();
+  bool Changed = runScopInlinerImpl(F, SCC);
+  return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
+}
diff --git a/polly/test/ScopInliner/ignore-declares.ll b/polly/test/ScopInliner/ignore-declares.ll
index 11722dcb321668..5c0cfa103f0bf1 100644
--- a/polly/test/ScopInliner/ignore-declares.ll
+++ b/polly/test/ScopInliner/ignore-declares.ll
@@ -1,5 +1,4 @@
-; RUN: opt %loadPolly -polly-detect-full-functions -polly-scop-inliner \
-; RUN: -polly-scops -disable-output < %s
+; RUN: opt %loadNPMPolly -polly-detect-full-functions '-passes=cgscc(polly-inline),function(print<polly-function-scops>)' -disable-output < %s
 
 ; Check that we do not crash if there are declares. We should skip function
 ; declarations and not try to query for domtree.
diff --git a/polly/test/ScopInliner/invariant-load-func.ll b/polly/test/ScopInliner/invariant-load-func.ll
index ffd2ec9cdb60f5..58c556a455fb97 100644
--- a/polly/test/ScopInliner/invariant-load-func.ll
+++ b/polly/test/ScopInliner/invariant-load-func.ll
@@ -1,12 +1,9 @@
-; RUN: opt %loadNPMPolly -polly-detect-full-functions -polly-scop-inliner \
-; RUN: -polly-invariant-load-hoisting '-passes=print<polly-function-scops>' -disable-output < %s | FileCheck %s
+; RUN: opt %loadNPMPolly -polly-detect-full-functions -polly-invariant-load-hoisting '-passes=cgscc(polly-inline),function(print<polly-function-scops>)' -disable-output < %s 2>&1 | FileCheck %s
 
 ; Check that we inline a function that requires invariant load hoisting
 ; correctly.
 ; CHECK:    Max Loop Depth:  2
 
-; REQUIRES: pollyacc
-
 
 ; void to_be_inlined(int A[], int *begin, int *end) {
 ;     for(int i = *begin; i < *end; i++) {
diff --git a/polly/test/ScopInliner/simple-inline-loop.ll b/polly/test/ScopInliner/simple-inline-loop.ll
index a5e3483edad050..f12798a3d831a5 100644
--- a/polly/test/ScopInliner/simple-inline-loop.ll
+++ b/polly/test/ScopInliner/simple-inline-loop.ll
@@ -1,5 +1,4 @@
-; RUN: opt %loadPolly -polly-detect-full-functions -polly-scop-inliner \
-; RUN: -polly-print-scops -disable-output < %s | FileCheck %s
+; RUN: opt %loadNPMPolly -polly-detect-full-functions '-passes=cgscc(polly-inline),function(print<polly-function-scops>)' -disable-output < %s  2>&1 | FileCheck %s
 
 ; Check that we get the 2 nested loops by inlining `to_be_inlined` into
 ; `inline_site`.

>From 8705fdc402b91a068e7a5025cbd38d4a6e14655b Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Tue, 4 Feb 2025 22:02:51 +0100
Subject: [PATCH 2/2] Sort registerPipelineParsingCallback

---
 polly/lib/Support/RegisterPasses.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/polly/lib/Support/RegisterPasses.cpp b/polly/lib/Support/RegisterPasses.cpp
index 3ace336cb588be..ad37760780c08a 100644
--- a/polly/lib/Support/RegisterPasses.cpp
+++ b/polly/lib/Support/RegisterPasses.cpp
@@ -623,12 +623,6 @@ parseTopLevelPipeline(llvm::ModulePassManager &MPM,
 /// handle LICMed code to make it useful.
 void registerPollyPasses(PassBuilder &PB) {
   PassInstrumentationCallbacks *PIC = PB.getPassInstrumentationCallbacks();
-  PB.registerPipelineParsingCallback(
-      [PIC](StringRef Name, CGSCCPassManager &CGPM,
-            ArrayRef<PassBuilder::PipelineElement> Pipeline) -> bool {
-        ExitOnError Err("Unable to parse Polly call graph pass: ");
-        return Err(parseCGPipeline(Name, CGPM, PIC, Pipeline));
-      });
   PB.registerAnalysisRegistrationCallback([PIC](FunctionAnalysisManager &FAM) {
     registerFunctionAnalyses(FAM, PIC);
   });
@@ -638,6 +632,12 @@ void registerPollyPasses(PassBuilder &PB) {
             ArrayRef<PassBuilder::PipelineElement> Pipeline) -> bool {
         return parseScopPipeline(Name, FPM, PIC, Pipeline);
       });
+  PB.registerPipelineParsingCallback(
+      [PIC](StringRef Name, CGSCCPassManager &CGPM,
+            ArrayRef<PassBuilder::PipelineElement> Pipeline) -> bool {
+        ExitOnError Err("Unable to parse Polly call graph pass: ");
+        return Err(parseCGPipeline(Name, CGPM, PIC, Pipeline));
+      });
   PB.registerParseTopLevelPipelineCallback(
       [PIC](llvm::ModulePassManager &MPM,
             ArrayRef<PassBuilder::PipelineElement> Pipeline) -> bool {



More information about the llvm-branch-commits mailing list