[llvm-branch-commits] [clang] [llvm] [LTO] Add support for NewPM CodeGen (PR #210249)

Vikram Hegde via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Sep 27 23:31:47 PDT 2026


https://github.com/vikramRH updated https://github.com/llvm/llvm-project/pull/210249

>From 1d61a4122d5d74d38f427a3d712f6f206455f8d8 Mon Sep 17 00:00:00 2001
From: vikhegde <vikram.hegde at amd.com>
Date: Thu, 16 Jul 2026 16:23:49 +0530
Subject: [PATCH] [CodeGen][LTO] Switch to runCodeGenPipeline abstraction

---
 clang/lib/CodeGen/BackendUtil.cpp             | 10 ++--
 llvm/include/llvm/Passes/RunCodeGen.h         | 30 ++++++++---
 llvm/lib/LTO/LTOBackend.cpp                   | 53 ++++++++----------
 llvm/lib/Passes/RunCodeGen.cpp                | 54 +++++++++++--------
 .../CodeGen/AMDGPU/lto-lower-module-lds.ll    | 49 +++++++++++++++++
 5 files changed, 134 insertions(+), 62 deletions(-)

diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 5a01779613c8c..b71dfbdd133ca 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1215,10 +1215,12 @@ void EmitAssemblyHelper::RunCodegenPipeline(
   }
 
   TimeCodegenPasses([&]() {
-    Error CodeGenError = runCodeGenPipeline(
-        *TM, *TheModule, *OS, DwoOS, CGFT, PrintPipelinePasses.has_value(),
-        !CodeGenOpts.VerifyModule, /*DisableSimplifyLibCalls=*/false,
-        CI.getVirtualFileSystemPtr());
+    CodeGenPipelineConfig Config;
+    Config.PrintPipelinePasses = PrintPipelinePasses.has_value();
+    Config.DisableVerify = !CodeGenOpts.VerifyModule;
+    Config.VFS = CI.getVirtualFileSystemPtr();
+    Error CodeGenError =
+        runCodeGenPipeline(*TM, *TheModule, *OS, DwoOS, CGFT, Config);
     if (CodeGenError)
       Diags.Report(diag::err_fe_unable_to_interface_with_target);
   });
diff --git a/llvm/include/llvm/Passes/RunCodeGen.h b/llvm/include/llvm/Passes/RunCodeGen.h
index b61e171363b7d..15aa926300aeb 100644
--- a/llvm/include/llvm/Passes/RunCodeGen.h
+++ b/llvm/include/llvm/Passes/RunCodeGen.h
@@ -16,12 +16,30 @@
 
 namespace llvm {
 
-LLVM_ABI Error runCodeGenPipeline(
-    TargetMachine &TM, Module &M, raw_pwrite_stream &OS,
-    std::unique_ptr<ToolOutputFile> &DwoOS, CodeGenFileType CGFT,
-    bool PrintPipelinePasses = false, bool DisableVerify = true,
-    bool DisableSimplifyLibCalls = false,
-    IntrusiveRefCntPtr<vfs::FileSystem> VFS = vfs::getRealFileSystem());
+class ModuleSummaryIndex;
+
+/// Options for runCodeGenPipeline().
+struct CodeGenPipelineConfig {
+  /// FIXME: Need to be implemented in runCodeGenPipeline().
+  bool PrintPipelinePasses = false;
+
+  bool DisableVerify = true;
+  bool DisableSimplifyLibCalls = false;
+  /// Log each pass as it runs. New pass manager only.
+  bool DebugPassManager = false;
+  /// Verify the IR between passes. New pass manager only.
+  bool VerifyEach = false;
+  /// LTO uses this to expose the combined summary index to summary-consuming
+  /// codegen passes.
+  const ModuleSummaryIndex *SummaryIndex = nullptr;
+  /// For passes needing file access.
+  IntrusiveRefCntPtr<vfs::FileSystem> VFS = nullptr;
+};
+
+Error runCodeGenPipeline(TargetMachine &TM, Module &M, raw_pwrite_stream &OS,
+                         std::unique_ptr<ToolOutputFile> &DwoOS,
+                         CodeGenFileType CGFT,
+                         const CodeGenPipelineConfig &Config = {});
 
 } // namespace llvm
 
diff --git a/llvm/lib/LTO/LTOBackend.cpp b/llvm/lib/LTO/LTOBackend.cpp
index d9a6551139d37..2b1d57d81f5ef 100644
--- a/llvm/lib/LTO/LTOBackend.cpp
+++ b/llvm/lib/LTO/LTOBackend.cpp
@@ -30,6 +30,7 @@
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Object/ModuleSymbolTable.h"
 #include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/RunCodeGen.h"
 #include "llvm/Passes/StandardInstrumentations.h"
 #include "llvm/Plugins/PassPlugin.h"
 #include "llvm/Support/Error.h"
@@ -479,36 +480,28 @@ static void codegen(const Config &Conf, TargetMachine *TM,
   std::unique_ptr<CachedFileStream> &Stream = *StreamOrErr;
   TM->Options.ObjectFilenameForDebug = Stream->ObjectPathName;
 
-  // Create the codegen pipeline in its own scope so it gets deleted before
-  // Stream->commit() is called. The commit function of CacheStream deletes
-  // the raw stream, which is too early as streamers (e.g. MCAsmStreamer)
-  // keep the pointer and may use it until their destruction. See #138194.
-  {
-    legacy::PassManager CodeGenPasses;
-    TargetLibraryInfoImpl TLII(Mod.getTargetTriple(), TM->Options.VecLib);
-    CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
-    CodeGenPasses.add(new RuntimeLibraryInfoWrapper(
-        TM->Options.MCOptions.ABIName, TM->Options.VecLib));
-
-    // No need to make index available if the module is empty.
-    // In theory these passes should not use the index for an empty
-    // module, however, this guards against doing any unnecessary summary-based
-    // analysis in the case of a ThinLTO build where this might be an empty
-    // regular LTO combined module, with a large combined index from ThinLTO.
-    if (!isEmptyModule(Mod))
-      CodeGenPasses.add(
-          createImmutableModuleSummaryIndexWrapperPass(&CombinedIndex));
-    if (Conf.PreCodeGenPassesHook)
-      Conf.PreCodeGenPassesHook(CodeGenPasses);
-    if (TM->addPassesToEmitFile(CodeGenPasses, *Stream->OS,
-                                DwoOut ? &DwoOut->os() : nullptr,
-                                Conf.CGFileType))
-      report_fatal_error("Failed to setup codegen");
-    CodeGenPasses.run(Mod);
-
-    if (DwoOut)
-      DwoOut->keep();
-  }
+  CodeGenPipelineConfig CGConfig;
+  CGConfig.DebugPassManager = Conf.DebugPassManager;
+  CGConfig.VerifyEach = Conf.VerifyEach;
+  // No need to make index available if the module is empty.
+  // In theory these passes should not use the index for an empty
+  // module, however, this guards against doing any unnecessary summary-based
+  // analysis in the case of a ThinLTO build where this might be an empty
+  // regular LTO combined module, with a large combined index from ThinLTO.
+  if (!isEmptyModule(Mod))
+    CGConfig.SummaryIndex = &CombinedIndex;
+
+  // runCodeGenPipeline() keeps the pipeline local to itself so that it gets
+  // deleted before Stream->commit() is called below. The commit function of
+  // CacheStream deletes the raw stream, which is too early as streamers (e.g.
+  // MCAsmStreamer) keep the pointer and may use it until their destruction.
+  // See #138194.
+  if (Error Err = runCodeGenPipeline(*TM, Mod, *Stream->OS, DwoOut,
+                                     Conf.CGFileType, CGConfig))
+    report_fatal_error(std::move(Err));
+
+  if (DwoOut)
+    DwoOut->keep();
 
   if (Error Err = Stream->commit())
     report_fatal_error(std::move(Err));
diff --git a/llvm/lib/Passes/RunCodeGen.cpp b/llvm/lib/Passes/RunCodeGen.cpp
index fee9b8f4e00de..9112ad1df73df 100644
--- a/llvm/lib/Passes/RunCodeGen.cpp
+++ b/llvm/lib/Passes/RunCodeGen.cpp
@@ -9,6 +9,7 @@
 #include "llvm/Passes/RunCodeGen.h"
 #include "llvm/Analysis/CGSCCPassManager.h"
 #include "llvm/Analysis/LoopAnalysisManager.h"
+#include "llvm/Analysis/ModuleSummaryAnalysis.h"
 #include "llvm/Analysis/RuntimeLibcallInfo.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
@@ -32,17 +33,17 @@ static cl::opt<cl::boolOrDefault>
                         "option will default to what the target prefers."),
                cl::init(cl::boolOrDefault::BOU_UNSET));
 
-static Error
-runCodeGenPipelineLegacy(TargetMachine &TM, Module &M, raw_pwrite_stream &OS,
-                         std::unique_ptr<ToolOutputFile> &DwoOS,
-                         CodeGenFileType CGFT, bool PrintPipelinePasses,
-                         bool DisableVerify, bool DisableSimplifyLibCalls) {
+static Error runCodeGenPipelineLegacy(TargetMachine &TM, Module &M,
+                                      raw_pwrite_stream &OS,
+                                      std::unique_ptr<ToolOutputFile> &DwoOS,
+                                      CodeGenFileType CGFT,
+                                      const CodeGenPipelineConfig &Config) {
   legacy::PassManager CodeGenPasses;
   CodeGenPasses.add(
       createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
   // Add LibraryInfo.
   TargetLibraryInfoImpl TLII(TM.getTargetTriple(), TM.Options.VecLib);
-  if (DisableSimplifyLibCalls)
+  if (Config.DisableSimplifyLibCalls)
     TLII.disableAllFunctions();
   CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
 
@@ -50,8 +51,12 @@ runCodeGenPipelineLegacy(TargetMachine &TM, Module &M, raw_pwrite_stream &OS,
   CodeGenPasses.add(
       new RuntimeLibraryInfoWrapper(Options.MCOptions.ABIName, Options.VecLib));
 
+  if (Config.SummaryIndex)
+    CodeGenPasses.add(
+        createImmutableModuleSummaryIndexWrapperPass(Config.SummaryIndex));
+
   if (TM.addPassesToEmitFile(CodeGenPasses, OS, DwoOS ? &DwoOS->os() : nullptr,
-                             CGFT, DisableVerify))
+                             CGFT, Config.DisableVerify))
     return createStringError("Failed to construct CodeGen pipeline");
   CodeGenPasses.run(M);
 
@@ -61,8 +66,8 @@ runCodeGenPipelineLegacy(TargetMachine &TM, Module &M, raw_pwrite_stream &OS,
 static Error runCodeGenPipelineNewPM(TargetMachine &TM, Module &M,
                                      raw_pwrite_stream &OS,
                                      std::unique_ptr<ToolOutputFile> &DwoOS,
-                                     CodeGenFileType CGFT, bool DisableVerify,
-                                     IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
+                                     CodeGenFileType CGFT,
+                                     const CodeGenPipelineConfig &Config) {
   ModulePassManager MPM;
   MachineFunctionAnalysisManager MFAM;
   LoopAnalysisManager LAM;
@@ -70,17 +75,21 @@ static Error runCodeGenPipelineNewPM(TargetMachine &TM, Module &M,
   CGSCCAnalysisManager CGAM;
   ModuleAnalysisManager MAM;
   CGPassBuilderOption Opt = getCGPassBuilderOption();
-  Opt.DisableVerify = DisableVerify;
+  Opt.DisableVerify = Config.DisableVerify;
   MachineModuleInfo MMI(&TM);
   PassInstrumentationCallbacks PIC;
   PipelineTuningOptions PTOptions;
   TargetMachine *TMPointer = &TM;
-  PassBuilder PB(TMPointer, PTOptions, std::nullopt, &PIC, VFS);
+  // Only resolve the fallback once we know we need one: under clang's IO
+  // sandbox, calling vfs::getRealFileSystem() at all is fatal.
+  PassBuilder PB(TMPointer, PTOptions, std::nullopt, &PIC,
+                 Config.VFS ? Config.VFS : vfs::getRealFileSystem());
 
-  StandardInstrumentations SI(M.getContext(), /*DebugLogging=*/false);
+  StandardInstrumentations SI(M.getContext(), Config.DebugPassManager,
+                              Config.VerifyEach);
   SI.registerCallbacks(PIC, &MAM);
 
-  TargetLibraryInfoImpl TLII(M.getTargetTriple());
+  TargetLibraryInfoImpl TLII(M.getTargetTriple(), TM.Options.VecLib);
   FAM.registerPass([&] { return TargetLibraryAnalysis(TLII); });
   MAM.registerPass([&] { return MachineModuleAnalysis(MMI); });
   MAM.registerPass([&] {
@@ -88,6 +97,11 @@ static Error runCodeGenPipelineNewPM(TargetMachine &TM, Module &M,
     return RuntimeLibraryAnalysis(Options.MCOptions.ABIName, Options.VecLib);
   });
 
+  if (Config.SummaryIndex)
+    MAM.registerPass([&] {
+      return ImmutableModuleSummaryIndexAnalysis(Config.SummaryIndex);
+    });
+
   PB.registerModuleAnalyses(MAM);
   PB.registerCGSCCAnalyses(CGAM);
   PB.registerFunctionAnalyses(FAM);
@@ -108,15 +122,11 @@ static Error runCodeGenPipelineNewPM(TargetMachine &TM, Module &M,
 Error llvm::runCodeGenPipeline(TargetMachine &TM, Module &M,
                                raw_pwrite_stream &OS,
                                std::unique_ptr<ToolOutputFile> &DwoOS,
-                               CodeGenFileType CGFT, bool PrintPipelinePasses,
-                               bool DisableVerify, bool DisableSimplifyLibCalls,
-                               IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
+                               CodeGenFileType CGFT,
+                               const CodeGenPipelineConfig &Config) {
   if (ForceNewPM == cl::boolOrDefault::BOU_TRUE ||
-      (TM.shouldDefaultToNewPM() &&
-       ForceNewPM != cl::boolOrDefault::BOU_FALSE)) {
-    return runCodeGenPipelineNewPM(TM, M, OS, DwoOS, CGFT, DisableVerify, VFS);
-  }
+      (TM.shouldDefaultToNewPM() && ForceNewPM != cl::boolOrDefault::BOU_FALSE))
+    return runCodeGenPipelineNewPM(TM, M, OS, DwoOS, CGFT, Config);
 
-  return runCodeGenPipelineLegacy(TM, M, OS, DwoOS, CGFT, PrintPipelinePasses,
-                                  DisableVerify, DisableSimplifyLibCalls);
+  return runCodeGenPipelineLegacy(TM, M, OS, DwoOS, CGFT, Config);
 }
diff --git a/llvm/test/CodeGen/AMDGPU/lto-lower-module-lds.ll b/llvm/test/CodeGen/AMDGPU/lto-lower-module-lds.ll
index a846a7cc12e49..b81cc4ef3c9c0 100644
--- a/llvm/test/CodeGen/AMDGPU/lto-lower-module-lds.ll
+++ b/llvm/test/CodeGen/AMDGPU/lto-lower-module-lds.ll
@@ -27,11 +27,60 @@
 
 ; First print will be from the New PM during the full LTO pipeline.
 ; Second print will be from the legacy PM during the CG pipeline.
+; These will be updated when NPM becomes default for AMDGPU CG pipeline.
 
 ; CHECK: Running pass: AMDGPULowerModuleLDSPass on [module]
 ; CHECK: ModulePass Manager
 ; CHECK:   Lower uses of LDS variables from non-kernel functions
 
+; Test -force-new-pm-codegen=true.
+
+; ENABLE-NPM Default O0
+; RUN: opt -mtriple=amdgpu10.30-- %s -o %t.bc
+; RUN: llvm-lto2 run -O0 -cg-opt-level 0 %t.bc -o %t.s -r %t.bc,test,px -force-new-pm-codegen=true -debug-pass-manager 2>&1 | FileCheck --check-prefix=ENABLE-NPM %s
+
+; ENABLE-NPM Unified O0
+; RUN: opt -unified-lto -thinlto-split-lto-unit -thinlto-bc -mtriple=amdgpu10.30-- %s -o %t.bc
+; RUN: llvm-lto2 run -unified-lto=full -O0 -cg-opt-level 0 %t.bc -o %t.s -r %t.bc,test,px -force-new-pm-codegen=true -debug-pass-manager 2>&1 | FileCheck --check-prefix=ENABLE-NPM %s
+
+; ENABLE-NPM Default O2
+; RUN: opt -mtriple=amdgpu10.30-- %s -o %t.bc
+; RUN: llvm-lto2 run -O2 -cg-opt-level 2 %t.bc -o %t.s -r %t.bc,test,px -force-new-pm-codegen=true -debug-pass-manager 2>&1 | FileCheck --check-prefix=ENABLE-NPM %s
+
+; ENABLE-NPM Unified O2
+; RUN: opt -unified-lto -thinlto-split-lto-unit -thinlto-bc -mtriple=amdgpu10.30-- %s -o %t.bc
+; RUN: llvm-lto2 run -unified-lto=full -O2 -cg-opt-level 2 %t.bc -o %t.s -r %t.bc,test,px -force-new-pm-codegen=true -debug-pass-manager 2>&1 | FileCheck --check-prefix=ENABLE-NPM %s
+
+; ENABLE-NPM-NOT: ModulePass Manager
+; ENABLE-NPM: Running pass: AMDGPULowerModuleLDSPass on [module]
+; ENABLE-NPM: Running pass: SelectionDAGISelPass on test
+; ENABLE-NPM: Running pass: PrologEpilogInserterPass on test
+; ENABLE-NPM: Running pass: AMDGPUAsmPrinterPass on test
+
+; Test -force-new-pm-codegen=false drives the CodeGen pipeline
+; with the legacy PM, regardless of the target default.
+
+; DISABLE-NPM Default O0
+; RUN: opt -mtriple=amdgpu10.30-- %s -o %t.bc
+; RUN: llvm-lto2 run -O0 -cg-opt-level 0 %t.bc -o %t.s -r %t.bc,test,px -force-new-pm-codegen=false -debug-pass-manager -debug-pass=Structure 2>&1 | FileCheck --check-prefix=DISABLE-NPM %s
+
+; DISABLE-NPM Unified O0
+; RUN: opt -unified-lto -thinlto-split-lto-unit -thinlto-bc -mtriple=amdgpu10.30-- %s -o %t.bc
+; RUN: llvm-lto2 run -unified-lto=full -O0 -cg-opt-level 0 %t.bc -o %t.s -r %t.bc,test,px -force-new-pm-codegen=false -debug-pass-manager -debug-pass=Structure 2>&1 | FileCheck --check-prefix=DISABLE-NPM %s
+
+; DISABLE-NPM Default O2
+; RUN: opt -mtriple=amdgpu10.30-- %s -o %t.bc
+; RUN: llvm-lto2 run -O2 -cg-opt-level 2 %t.bc -o %t.s -r %t.bc,test,px -force-new-pm-codegen=false -debug-pass-manager -debug-pass=Structure 2>&1 | FileCheck --check-prefix=DISABLE-NPM %s
+
+; DISABLE-NPM Unified O2
+; RUN: opt -unified-lto -thinlto-split-lto-unit -thinlto-bc -mtriple=amdgpu10.30-- %s -o %t.bc
+; RUN: llvm-lto2 run -unified-lto=full -O2 -cg-opt-level 2 %t.bc -o %t.s -r %t.bc,test,px -force-new-pm-codegen=false -debug-pass-manager -debug-pass=Structure 2>&1 | FileCheck --check-prefix=DISABLE-NPM %s
+
+; DISABLE-NPM: Running pass: AMDGPULowerModuleLDSPass on [module]
+; DISABLE-NPM: ModulePass Manager
+; DISABLE-NPM:   Lower uses of LDS variables from non-kernel functions
+; DISABLE-NPM-NOT: Running pass: SelectionDAGISelPass
+
 @lds = internal unnamed_addr addrspace(3) global i32 poison, align 4
 
 define amdgpu_kernel void @test() {



More information about the llvm-branch-commits mailing list