[llvm] [NewPM][CodeGen] Refactoring CodeGenPassBuilder (PR #89708)

via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 22 23:53:49 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: None (paperchalice)

<details>
<summary>Changes</summary>

- Remove redundant `std::move`.
- Use a unified function to add passes.
- Support pass substitution.
Based on #<!-- -->88610.

---

Patch is 47.68 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/89708.diff


4 Files Affected:

- (modified) llvm/include/llvm/Passes/CodeGenPassBuilder.h (+286-264) 
- (modified) llvm/lib/Target/X86/X86CodeGenPassBuilder.cpp (+6-7) 
- (modified) llvm/unittests/CodeGen/CMakeLists.txt (+1) 
- (added) llvm/unittests/CodeGen/CodeGenPassBuilderTest.cpp (+126) 


``````````diff
diff --git a/llvm/include/llvm/Passes/CodeGenPassBuilder.h b/llvm/include/llvm/Passes/CodeGenPassBuilder.h
index 2e94a19502131a..3c122d67eda1b7 100644
--- a/llvm/include/llvm/Passes/CodeGenPassBuilder.h
+++ b/llvm/include/llvm/Passes/CodeGenPassBuilder.h
@@ -72,6 +72,7 @@
 #include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
 #include "llvm/Transforms/Utils/LowerInvoke.h"
 #include <cassert>
+#include <stack>
 #include <type_traits>
 #include <utility>
 
@@ -115,9 +116,6 @@ template <typename DerivedT, typename TargetMachineT> class CodeGenPassBuilder {
                               const CGPassBuilderOption &Opts,
                               PassInstrumentationCallbacks *PIC)
       : TM(TM), Opt(Opts), PIC(PIC) {
-    // Target could set CGPassBuilderOption::MISchedPostRA to true to achieve
-    //     substitutePass(&PostRASchedulerID, &PostMachineSchedulerID)
-
     // Target should override TM.Options.EnableIPRA in their target-specific
     // LLVMTM ctor. See TargetMachine::setGlobalISel for example.
     if (Opt.EnableIPRA)
@@ -131,142 +129,103 @@ template <typename DerivedT, typename TargetMachineT> class CodeGenPassBuilder {
   }
 
   Error buildPipeline(ModulePassManager &MPM, raw_pwrite_stream &Out,
-                      raw_pwrite_stream *DwoOut,
-                      CodeGenFileType FileType) const;
+                      raw_pwrite_stream *DwoOut, CodeGenFileType FileType);
 
-  PassInstrumentationCallbacks *getPassInstrumentationCallbacks() const {
+  PassInstrumentationCallbacks *getPassInstrumentationCallbacks() {
     return PIC;
   }
 
+  /// Add one pass to pass manager, it can handle pass nesting automatically.
+  template <typename PassT> void addPass(PassT &&Pass) {
+    using ResultT = decltype(derived().template substitutePass<PassT>());
+    constexpr bool IsVoid = std::is_void_v<ResultT>;
+    StringRef PassName = std::conditional_t<IsVoid, PassT, ResultT>::name();
+
+    if (!runBeforeAdding(PassName))
+      return;
+
+    if constexpr (IsVoid)
+      addPassImpl(std::forward<PassT>(Pass));
+    else
+      addPassImpl(derived().template substitutePass<PassT>());
+
+    runAfterAdding(PassName);
+  }
+
+  /// Add multiple passes.
+  template <typename PassT, typename... PassTs>
+  void addPass(PassT &&Pass, PassTs &&...Passes) {
+    addPass(std::forward<PassT>(Pass));
+    addPass(std::forward<PassTs>(Passes)...);
+  }
+
+  /// Add multiple passes with default constructor.
+  template <typename... PassTs> void addPass() { addPass(PassTs()...); }
+
+  /// @brief Substitute PassT with given pass, target can specialize this
+  /// function template or override it in subclass, if it is overriden by
+  /// subclass, it must return CodeGenPassBuilder::substitutePass() to get
+  /// default return value. See also
+  /// unittests/CodeGen/CodeGenPassBuilderTest.cpp.
+  /// @tparam PassT The pass type that needs to be replaced.
+  /// @return The replaced pass if substitution occurs, otherwise return void.
+  template <typename PassT> auto substitutePass() {}
+
 protected:
   template <typename PassT>
   using is_module_pass_t = decltype(std::declval<PassT &>().run(
       std::declval<Module &>(), std::declval<ModuleAnalysisManager &>()));
 
+  template <typename PassT>
+  static constexpr bool is_module_pass_v =
+      is_detected<is_module_pass_t, PassT>::value;
+
   template <typename PassT>
   using is_function_pass_t = decltype(std::declval<PassT &>().run(
       std::declval<Function &>(), std::declval<FunctionAnalysisManager &>()));
 
+  template <typename PassT>
+  static constexpr bool is_function_pass_v =
+      is_detected<is_function_pass_t, PassT>::value;
+
   template <typename PassT>
   using is_machine_function_pass_t = decltype(std::declval<PassT &>().run(
       std::declval<MachineFunction &>(),
       std::declval<MachineFunctionAnalysisManager &>()));
 
-  // Function object to maintain state while adding codegen IR passes.
-  // TODO: add a Function -> MachineFunction adaptor and merge
-  // AddIRPass/AddMachinePass so we can have a function pipeline that runs both
-  // function passes and machine function passes.
-  class AddIRPass {
-  public:
-    AddIRPass(ModulePassManager &MPM, const DerivedT &PB) : MPM(MPM), PB(PB) {}
-    ~AddIRPass() {
-      if (!FPM.isEmpty())
-        MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
-    }
-
-    template <typename PassT>
-    void operator()(PassT &&Pass, StringRef Name = PassT::name()) {
-      static_assert((is_detected<is_function_pass_t, PassT>::value ||
-                     is_detected<is_module_pass_t, PassT>::value) &&
-                    "Only module pass and function pass are supported.");
-
-      if (!PB.runBeforeAdding(Name))
-        return;
-
-      // Add Function Pass
-      if constexpr (is_detected<is_function_pass_t, PassT>::value) {
-        FPM.addPass(std::forward<PassT>(Pass));
-      } else {
-        // Add Module Pass
-        if (!FPM.isEmpty()) {
-          MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
-          FPM = FunctionPassManager();
-        }
-
-        MPM.addPass(std::forward<PassT>(Pass));
-      }
-    }
-
-  private:
-    ModulePassManager &MPM;
-    FunctionPassManager FPM;
-    const DerivedT &PB;
-  };
-
-  // Function object to maintain state while adding codegen machine passes.
-  class AddMachinePass {
-  public:
-    AddMachinePass(ModulePassManager &MPM, const DerivedT &PB)
-        : MPM(MPM), PB(PB) {}
-    ~AddMachinePass() {
-      if (!MFPM.isEmpty())
-        MPM.addPass(createModuleToMachineFunctionPassAdaptor(std::move(MFPM)));
-    }
-
-    template <typename PassT>
-    void operator()(PassT &&Pass, bool Force = false,
-                    StringRef Name = PassT::name()) {
-      static_assert((is_detected<is_machine_function_pass_t, PassT>::value ||
-                     is_detected<is_module_pass_t, PassT>::value) &&
-                    "Only module pass and function pass are supported.");
-
-      if (!Force && !PB.runBeforeAdding(Name))
-        return;
-
-      // Add Function Pass
-      if constexpr (is_detected<is_machine_function_pass_t, PassT>::value) {
-        MFPM.addPass(std::forward<PassT>(Pass));
-      } else {
-        // Add Module Pass
-        if (!MFPM.isEmpty()) {
-          MPM.addPass(
-              createModuleToMachineFunctionPassAdaptor(std::move(MFPM)));
-          MFPM = MachineFunctionPassManager();
-        }
-
-        MPM.addPass(std::forward<PassT>(Pass));
-      }
-
-      for (auto &C : PB.AfterCallbacks)
-        C(Name, MFPM);
-    }
-
-  private:
-    ModulePassManager &MPM;
-    MachineFunctionPassManager MFPM;
-    const DerivedT &PB;
-  };
+  template <typename PassT>
+  static constexpr bool is_machine_function_pass_v =
+      is_detected<is_machine_function_pass_t, PassT>::value;
 
   TargetMachineT &TM;
   CGPassBuilderOption Opt;
   PassInstrumentationCallbacks *PIC;
 
-  template <typename TMC> TMC &getTM() const { return static_cast<TMC &>(TM); }
-  CodeGenOptLevel getOptLevel() const { return TM.getOptLevel(); }
+  CodeGenOptLevel getOptLevel() { return TM.getOptLevel(); }
 
   /// Check whether or not GlobalISel should abort on error.
   /// When this is disabled, GlobalISel will fall back on SDISel instead of
   /// erroring out.
-  bool isGlobalISelAbortEnabled() const {
+  bool isGlobalISelAbortEnabled() {
     return TM.Options.GlobalISelAbort == GlobalISelAbortMode::Enable;
   }
 
   /// Check whether or not a diagnostic should be emitted when GlobalISel
   /// uses the fallback path. In other words, it will emit a diagnostic
   /// when GlobalISel failed and isGlobalISelAbortEnabled is false.
-  bool reportDiagnosticWhenGlobalISelFallback() const {
+  bool reportDiagnosticWhenGlobalISelFallback() {
     return TM.Options.GlobalISelAbort == GlobalISelAbortMode::DisableWithDiag;
   }
 
   /// addInstSelector - This method should install an instruction selector pass,
   /// which converts from LLVM code to machine instructions.
-  Error addInstSelector(AddMachinePass &) const {
+  Error addInstSelector() {
     return make_error<StringError>("addInstSelector is not overridden",
                                    inconvertibleErrorCode());
   }
 
   /// Target can override this to add GlobalMergePass before all IR passes.
-  void addGlobalMergePass(AddIRPass &) const {}
+  void addGlobalMergePass() {}
 
   /// Add passes that optimize instruction level parallelism for out-of-order
   /// targets. These passes are run while the machine code is still in SSA
@@ -274,11 +233,11 @@ template <typename DerivedT, typename TargetMachineT> class CodeGenPassBuilder {
   ///
   /// All passes added here should preserve the MachineDominatorTree,
   /// MachineLoopInfo, and MachineTraceMetrics analyses.
-  void addILPOpts(AddMachinePass &) const {}
+  void addILPOpts() {}
 
   /// This method may be implemented by targets that want to run passes
   /// immediately before register allocation.
-  void addPreRegAlloc(AddMachinePass &) const {}
+  void addPreRegAlloc() {}
 
   /// addPreRewrite - Add passes to the optimized register allocation pipeline
   /// after register allocation is complete, but before virtual registers are
@@ -292,79 +251,77 @@ template <typename DerivedT, typename TargetMachineT> class CodeGenPassBuilder {
   /// Note if the target overloads addRegAssignAndRewriteOptimized, this may not
   /// be honored. This is also not generally used for the fast variant,
   /// where the allocation and rewriting are done in one pass.
-  void addPreRewrite(AddMachinePass &) const {}
+  void addPreRewrite() {}
 
   /// Add passes to be run immediately after virtual registers are rewritten
   /// to physical registers.
-  void addPostRewrite(AddMachinePass &) const {}
+  void addPostRewrite() {}
 
   /// This method may be implemented by targets that want to run passes after
   /// register allocation pass pipeline but before prolog-epilog insertion.
-  void addPostRegAlloc(AddMachinePass &) const {}
+  void addPostRegAlloc() {}
 
   /// This method may be implemented by targets that want to run passes after
   /// prolog-epilog insertion and before the second instruction scheduling pass.
-  void addPreSched2(AddMachinePass &) const {}
+  void addPreSched2() {}
 
   /// This pass may be implemented by targets that want to run passes
   /// immediately before machine code is emitted.
-  void addPreEmitPass(AddMachinePass &) const {}
+  void addPreEmitPass() {}
 
   /// Targets may add passes immediately before machine code is emitted in this
   /// callback. This is called even later than `addPreEmitPass`.
   // FIXME: Rename `addPreEmitPass` to something more sensible given its actual
   // position and remove the `2` suffix here as this callback is what
   // `addPreEmitPass` *should* be but in reality isn't.
-  void addPreEmitPass2(AddMachinePass &) const {}
+  void addPreEmitPass2() {}
 
   /// {{@ For GlobalISel
   ///
 
   /// addPreISel - This method should add any "last minute" LLVM->LLVM
   /// passes (which are run just before instruction selector).
-  void addPreISel(AddIRPass &) const {
-    llvm_unreachable("addPreISel is not overridden");
-  }
+  void addPreISel() { llvm_unreachable("addPreISel is not overridden"); }
 
   /// This method should install an IR translator pass, which converts from
   /// LLVM code to machine instructions with possibly generic opcodes.
-  Error addIRTranslator(AddMachinePass &) const {
+  Error addIRTranslator() {
     return make_error<StringError>("addIRTranslator is not overridden",
                                    inconvertibleErrorCode());
   }
 
   /// This method may be implemented by targets that want to run passes
   /// immediately before legalization.
-  void addPreLegalizeMachineIR(AddMachinePass &) const {}
+  void addPreLegalizeMachineIR() {}
 
   /// This method should install a legalize pass, which converts the instruction
   /// sequence into one that can be selected by the target.
-  Error addLegalizeMachineIR(AddMachinePass &) const {
+  Error addLegalizeMachineIR() {
     return make_error<StringError>("addLegalizeMachineIR is not overridden",
                                    inconvertibleErrorCode());
   }
 
   /// This method may be implemented by targets that want to run passes
   /// immediately before the register bank selection.
-  void addPreRegBankSelect(AddMachinePass &) const {}
+  void addPreRegBankSelect() {}
 
   /// This method should install a register bank selector pass, which
   /// assigns register banks to virtual registers without a register
   /// class or register banks.
-  Error addRegBankSelect(AddMachinePass &) const {
+  Error addRegBankSelect() {
     return make_error<StringError>("addRegBankSelect is not overridden",
                                    inconvertibleErrorCode());
   }
 
   /// This method may be implemented by targets that want to run passes
   /// immediately before the (global) instruction selection.
-  void addPreGlobalInstructionSelect(AddMachinePass &) const {}
+  void addPreGlobalInstructionSelect() {}
 
   /// This method should install a (global) instruction selector pass, which
   /// converts possibly generic instructions to fully target-specific
   /// instructions, thereby constraining all generic virtual registers to
   /// register classes.
-  Error addGlobalInstructionSelect(AddMachinePass &) const {
+  Error addGlobalInstructionSelect() {
     return make_error<StringError>(
         "addGlobalInstructionSelect is not overridden",
         inconvertibleErrorCode());
@@ -375,30 +332,30 @@ template <typename DerivedT, typename TargetMachineT> class CodeGenPassBuilder {
   /// representation to the MI representation.
   /// Adds IR based lowering and target specific optimization passes and finally
   /// the core instruction selection passes.
-  void addISelPasses(AddIRPass &) const;
+  void addISelPasses();
 
   /// Add the actual instruction selection passes. This does not include
   /// preparation passes on IR.
-  Error addCoreISelPasses(AddMachinePass &) const;
+  Error addCoreISelPasses();
 
   /// Add the complete, standard set of LLVM CodeGen passes.
   /// Fully developed targets will not generally override this.
-  Error addMachinePasses(AddMachinePass &) const;
+  Error addMachinePasses();
 
   /// Add passes to lower exception handling for the code generator.
-  void addPassesToHandleExceptions(AddIRPass &) const;
+  void addPassesToHandleExceptions();
 
   /// Add common target configurable passes that perform LLVM IR to IR
   /// transforms following machine independent optimization.
-  void addIRPasses(AddIRPass &) const;
+  void addIRPasses();
 
   /// Add pass to prepare the LLVM IR for code generation. This should be done
   /// before exception handling preparation passes.
-  void addCodeGenPrepare(AddIRPass &) const;
+  void addCodeGenPrepare();
 
   /// Add common passes that perform LLVM IR to IR transforms in preparation for
   /// instruction selection.
-  void addISelPrepare(AddIRPass &) const;
+  void addISelPrepare();
 
   /// Methods with trivial inline returns are convenient points in the common
   /// codegen pass pipeline where targets may insert passes. Methods with
@@ -409,30 +366,30 @@ template <typename DerivedT, typename TargetMachineT> class CodeGenPassBuilder {
 
   /// addMachineSSAOptimization - Add standard passes that optimize machine
   /// instructions in SSA form.
-  void addMachineSSAOptimization(AddMachinePass &) const;
+  void addMachineSSAOptimization();
 
   /// addFastRegAlloc - Add the minimum set of target-independent passes that
   /// are required for fast register allocation.
-  Error addFastRegAlloc(AddMachinePass &) const;
+  Error addFastRegAlloc();
 
   /// addOptimizedRegAlloc - Add passes related to register allocation.
   /// LLVMTargetMachine provides standard regalloc passes for most targets.
-  void addOptimizedRegAlloc(AddMachinePass &) const;
+  void addOptimizedRegAlloc();
 
   /// Add passes that optimize machine instructions after register allocation.
-  void addMachineLateOptimization(AddMachinePass &) const;
+  void addMachineLateOptimization();
 
   /// addGCPasses - Add late codegen passes that analyze code for garbage
   /// collection. This should return true if GC info should be printed after
   /// these passes.
-  void addGCPasses(AddMachinePass &) const {}
+  void addGCPasses() {}
 
   /// Add standard basic block placement passes.
-  void addBlockPlacement(AddMachinePass &) const;
+  void addBlockPlacement();
 
   using CreateMCStreamer =
       std::function<Expected<std::unique_ptr<MCStreamer>>(MCContext &)>;
-  void addAsmPrinter(AddMachinePass &, CreateMCStreamer) const {
+  void addAsmPrinter(CreateMCStreamer) {
     llvm_unreachable("addAsmPrinter is not overridden");
   }
 
@@ -441,67 +398,161 @@ template <typename DerivedT, typename TargetMachineT> class CodeGenPassBuilder {
 
   /// createTargetRegisterAllocator - Create the register allocator pass for
   /// this target at the current optimization level.
-  void addTargetRegisterAllocator(AddMachinePass &, bool Optimized) const;
+  void addTargetRegisterAllocator(bool Optimized);
 
   /// addMachinePasses helper to create the target-selected or overriden
   /// regalloc pass.
-  void addRegAllocPass(AddMachinePass &, bool Optimized) const;
+  void addRegAllocPass(bool Optimized);
 
   /// Add core register alloator passes which do the actual register assignment
   /// and rewriting. \returns true if any passes were added.
-  Error addRegAssignmentFast(AddMachinePass &) const;
-  Error addRegAssignmentOptimized(AddMachinePass &) const;
+  Error addRegAssignmentFast();
+  Error addRegAssignmentOptimized();
+
+  /// Merge all pass manager into one ModulePassManager
+  void mergePassManager();
 
   /// Allow the target to disable a specific pass by default.
   /// Backend can declare unwanted passes in constructor.
-  template <typename... PassTs> void disablePass() {
+  template <typename PassT> void disablePass(unsigned InstanceNum = 0) {
     BeforeCallbacks.emplace_back(
-        [](StringRef Name) { return ((Name != PassTs::name()) && ...); });
+        [Cnt = 0u, InstanceNum](StringRef Name) mutable {
+          if (!InstanceNum)
+            return PassT::name() != Name;
+          if (PassT::name() == Name)
+            return ++Cnt == InstanceNum;
+          return true;
+        });
+  }
+  template <typename PassT1, typename PassT2, typename... PassTs>
+  void disablePass() {
+    BeforeCallbacks.emplace_back([](StringRef Name) {
+      return Name != PassT1::name() && Name != PassT2::name() &&
+             ((Name != PassTs::name()) && ...);
+    });
   }
 
   /// Insert InsertedPass pass after TargetPass pass.
   /// Only machine function passes are supported.
   template <typename TargetPassT, typename InsertedPassT>
-  void insertPass(InsertedPassT &&Pass) {
-    AfterCallbacks.emplace_back(
-        [&](StringRef Name, MachineFunctionPassManager &MFPM) mutable {
-          if (Name == TargetPassT::name())
-            MFPM.addPass(std::forward<InsertedPassT>(Pass));
-        });
+  void insertPass(InsertedPassT &&Pass, unsigned InstanceNum = 0) {
+    AfterCallbacks.emplace_back([&, Cnt = 0](StringRef Name) mutable {
+      if (Name == TargetPassT::name()) {
+        if (!InstanceNum) {
+          addPass(std::forward<InsertedPassT>(Pass));
+          return;
+        }
+        if (++Cnt == InstanceNum)
+          addPass(std::forward<InsertedPassT>(Pass));
+      }
+    });
   }
 
+  /// Get internal ModulePassManager, only for test!
+  ModulePassManager &getMPM() { return MPM; }
+
 private:
   DerivedT &derived() { return static_cast<DerivedT &>(*this); }
-  const DerivedT &derived() const {
-    return static_cast<const DerivedT &>(*this);
-  }
 
-  bool runBeforeAdding(StringRef Name) const {
+  /// A monotonic stack based method to add pass.
+  template <typename PassT> void addPassImpl(PassT &&Pass);
+
+  bool runBeforeAdding(StringRef Name) {
     bool ShouldAdd = true;
     for (auto &C : BeforeCallbacks)
       ShouldAdd &= C(Name);
     return ShouldAdd;
   }
 
-  void setStartStopPasses(const TargetPassConfig::StartStopInfo &Info) const;
+  void runAfterAdding(StringRef Name) {
+    for (auto &C : AfterCallbacks)
+      C(Name);
+  }
+
+  void setStartStopPasses(const TargetPassConfig::StartStopInfo &Info);
 
   Error verifyStartStop(const TargetPassConf...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/89708


More information about the llvm-commits mailing list