[llvm] [NVPTX] Add NewPM codegen pipeline (PR #215712)

Alex MacLean via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 12 14:53:49 PDT 2026


https://github.com/AlexMaclean updated https://github.com/llvm/llvm-project/pull/215712

>From e37588271e0fd14853fd851d8ef58b8173b504be Mon Sep 17 00:00:00 2001
From: Alex Maclean <amaclean at nvidia.com>
Date: Tue, 11 Aug 2026 20:41:30 -0700
Subject: [PATCH 1/2] [NVPTX] Add NewPM codegen pipeline

---
 llvm/lib/Target/NVPTX/CMakeLists.txt          |   1 +
 llvm/lib/Target/NVPTX/NVPTX.h                 |   1 +
 .../Target/NVPTX/NVPTXCodeGenPassBuilder.cpp  | 304 ++++++++++++++++++
 llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp  |  12 +-
 llvm/lib/Target/NVPTX/NVPTXTargetMachine.h    |   6 +
 llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll   | 177 ++++++++++
 6 files changed, 496 insertions(+), 5 deletions(-)
 create mode 100644 llvm/lib/Target/NVPTX/NVPTXCodeGenPassBuilder.cpp
 create mode 100644 llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll

diff --git a/llvm/lib/Target/NVPTX/CMakeLists.txt b/llvm/lib/Target/NVPTX/CMakeLists.txt
index 00f19eee21704..04f00e1d3af57 100644
--- a/llvm/lib/Target/NVPTX/CMakeLists.txt
+++ b/llvm/lib/Target/NVPTX/CMakeLists.txt
@@ -18,6 +18,7 @@ set(NVPTXCodeGen_sources
   NVPTXAsmPrinter.cpp
   NVPTXAssignValidGlobalNames.cpp
   NVPTXAtomicLower.cpp
+  NVPTXCodeGenPassBuilder.cpp
   NVPTXCtorDtorLowering.cpp
   NVPTXDwarfDebug.cpp
   NVPTXForwardParams.cpp
diff --git a/llvm/lib/Target/NVPTX/NVPTX.h b/llvm/lib/Target/NVPTX/NVPTX.h
index 604747dd1a69b..a306b101cf04a 100644
--- a/llvm/lib/Target/NVPTX/NVPTX.h
+++ b/llvm/lib/Target/NVPTX/NVPTX.h
@@ -25,6 +25,7 @@
 #include "llvm/Target/TargetMachine.h"
 
 namespace llvm {
+class SelectionDAGISelPass;
 class FunctionPass;
 class MachineFunctionPass;
 class NVPTXTargetMachine;
diff --git a/llvm/lib/Target/NVPTX/NVPTXCodeGenPassBuilder.cpp b/llvm/lib/Target/NVPTX/NVPTXCodeGenPassBuilder.cpp
new file mode 100644
index 0000000000000..81419d193f092
--- /dev/null
+++ b/llvm/lib/Target/NVPTX/NVPTXCodeGenPassBuilder.cpp
@@ -0,0 +1,304 @@
+//===-- NVPTXCodeGenPassBuilder.cpp - Build NVPTX CodeGen pipeline --------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// New pass manager version of the NVPTX codegen pipeline. This mirrors
+// NVPTXPassConfig in NVPTXTargetMachine.cpp; the two must be kept in sync
+// until the legacy pass manager path is removed.
+//
+//===----------------------------------------------------------------------===//
+
+#include "NVPTX.h"
+#include "NVPTXAsmPrinter.h"
+#include "NVPTXCtorDtorLowering.h"
+#include "NVPTXSubtarget.h"
+#include "NVPTXTargetMachine.h"
+#include "llvm/CodeGen/AtomicExpand.h"
+#include "llvm/CodeGen/DeadMachineInstructionElim.h"
+#include "llvm/CodeGen/MachineCopyPropagation.h"
+#include "llvm/CodeGen/MachineLateInstrsCleanup.h"
+#include "llvm/CodeGen/MachineScheduler.h"
+#include "llvm/CodeGen/PEI.h"
+#include "llvm/CodeGen/PHIElimination.h"
+#include "llvm/CodeGen/PatchableFunction.h"
+#include "llvm/CodeGen/PostRAMachineSink.h"
+#include "llvm/CodeGen/PostRASchedulerList.h"
+#include "llvm/CodeGen/ProcessImplicitDefs.h"
+#include "llvm/CodeGen/RegisterCoalescerPass.h"
+#include "llvm/CodeGen/RemoveLoadsIntoFakeUses.h"
+#include "llvm/CodeGen/ShrinkWrap.h"
+#include "llvm/CodeGen/StackColoring.h"
+#include "llvm/CodeGen/StackSlotColoring.h"
+#include "llvm/CodeGen/TailDuplication.h"
+#include "llvm/CodeGen/TwoAddressInstructionPass.h"
+#include "llvm/CodeGen/UnreachableBlockElim.h"
+#include "llvm/IR/PassInstrumentation.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/Passes/CodeGenPassBuilder.h"
+#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Target/CGPassBuilderOption.h"
+#include "llvm/Transforms/IPO/ExpandVariadics.h"
+#include "llvm/Transforms/Scalar/EarlyCSE.h"
+#include "llvm/Transforms/Scalar/GVN.h"
+#include "llvm/Transforms/Scalar/InferAddressSpaces.h"
+#include "llvm/Transforms/Scalar/NaryReassociate.h"
+#include "llvm/Transforms/Scalar/SROA.h"
+#include "llvm/Transforms/Scalar/SeparateConstOffsetFromGEP.h"
+#include "llvm/Transforms/Scalar/SpeculativeExecution.h"
+#include "llvm/Transforms/Scalar/StraightLineStrengthReduce.h"
+#include "llvm/Transforms/Vectorize/LoadStoreVectorizer.h"
+
+using namespace llvm;
+
+extern cl::opt<bool> DisableLoadStoreVectorizer;
+extern cl::opt<bool> DisableNVPTXIRPeephole;
+
+namespace {
+
+class NVPTXCodeGenPassBuilder
+    : public CodeGenPassBuilder<NVPTXCodeGenPassBuilder, NVPTXTargetMachine> {
+  using Base = CodeGenPassBuilder<NVPTXCodeGenPassBuilder, NVPTXTargetMachine>;
+
+public:
+  explicit NVPTXCodeGenPassBuilder(NVPTXTargetMachine &TM,
+                                   const CGPassBuilderOption &Opts,
+                                   PassInstrumentationCallbacks *PIC)
+      : CodeGenPassBuilder(TM, Opts, PIC) {
+    // The following passes are known to not play well with virtual regs
+    // hanging around after register allocation (which in our case, is *all*
+    // registers). We explicitly disable them here. We do, however, need some
+    // functionality of the PrologEpilogCodeInserter pass, so we emulate that
+    // behavior in the NVPTXPrologEpilog pass (see NVPTXPrologEpilogPass.cpp).
+    disablePass<PrologEpilogInserterPass, MachineLateInstrsCleanupPass,
+                MachineCopyPropagationPass, TailDuplicatePass,
+                StackMapLivenessPass, PostRAMachineSinkingPass,
+                PostRASchedulerPass, FuncletLayoutPass, PatchableFunctionPass,
+                ShrinkWrapPass, RemoveLoadsIntoFakeUsesPass>();
+  }
+
+  void addIRPasses(PassManagerWrapper &PMW) const;
+  Error addInstSelector(PassManagerWrapper &PMW) const;
+  void addPreRegAlloc(PassManagerWrapper &PMW) const;
+  void addPostRegAlloc(PassManagerWrapper &PMW) const;
+
+  // NVPTX has no register allocation; virtual registers are emitted directly.
+  void addTargetRegisterAllocator(PassManagerWrapper &PMW, bool) const {}
+  Error addFastRegAlloc(PassManagerWrapper &PMW) const;
+  Error addOptimizedRegAlloc(PassManagerWrapper &PMW) const;
+
+  void addAsmPrinterBegin(PassManagerWrapper &PMW) const;
+  void addAsmPrinter(PassManagerWrapper &PMW) const;
+  void addAsmPrinterEnd(PassManagerWrapper &PMW) const;
+
+private:
+  // If the opt level is aggressive, add GVN; otherwise, add EarlyCSE.
+  void addEarlyCSEOrGVNPass(PassManagerWrapper &PMW) const;
+
+  // Add passes that propagate special memory spaces.
+  void addAddressSpaceInferencePasses(PassManagerWrapper &PMW) const;
+
+  // Add passes that perform straight-line scalar optimizations.
+  void addStraightLineScalarOptimizationPasses(PassManagerWrapper &PMW) const;
+};
+
+void NVPTXCodeGenPassBuilder::addEarlyCSEOrGVNPass(
+    PassManagerWrapper &PMW) const {
+  if (getOptLevel() == CodeGenOptLevel::Aggressive)
+    // Disable scalar PRE due to Register Pressure increase
+    addFunctionPass(GVNPass(GVNOptions().setScalarPRE(false)), PMW);
+  else
+    addFunctionPass(EarlyCSEPass(), PMW);
+}
+
+void NVPTXCodeGenPassBuilder::addAddressSpaceInferencePasses(
+    PassManagerWrapper &PMW) const {
+  // NVPTXLowerArgs emits alloca for byval parameters which can often
+  // be eliminated by SROA.
+  addFunctionPass(SROAPass(SROAOptions(SROAOptions::PreserveCFG,
+                                       /*AggregateToVector=*/true)),
+                  PMW);
+  addFunctionPass(NVPTXLowerAllocaPass(), PMW);
+  // TODO: Consider running InferAddressSpaces during opt, earlier in the
+  // compilation flow.
+  addFunctionPass(InferAddressSpacesPass(), PMW);
+  addFunctionPass(NVPTXAtomicLowerPass(), PMW);
+}
+
+void NVPTXCodeGenPassBuilder::addStraightLineScalarOptimizationPasses(
+    PassManagerWrapper &PMW) const {
+  addFunctionPass(SeparateConstOffsetFromGEPPass(), PMW);
+  addFunctionPass(SpeculativeExecutionPass(), PMW);
+  // ReassociateGEPs exposes more opportunites for SLSR. See
+  // the example in reassociate-geps-and-slsr.ll.
+  addFunctionPass(StraightLineStrengthReducePass(), PMW);
+  // SeparateConstOffsetFromGEP and SLSR creates common expressions which GVN
+  // or EarlyCSE can reuse. GVN generates significantly better code than
+  // EarlyCSE for some of our benchmarks.
+  addEarlyCSEOrGVNPass(PMW);
+  // Run NaryReassociate after EarlyCSE/GVN to be more effective.
+  addFunctionPass(NaryReassociatePass(), PMW);
+  // NaryReassociate on GEPs creates redundant common expressions, so run
+  // EarlyCSE after it.
+  addFunctionPass(EarlyCSEPass(), PMW);
+}
+
+void NVPTXCodeGenPassBuilder::addIRPasses(PassManagerWrapper &PMW) const {
+  const NVPTXSubtarget &ST = *TM.getSubtargetImpl();
+
+  // NVVMReflectPass is added in the pipeline-start extension point, so
+  // hopefully running it here does nothing. But since we need it for
+  // correctness when lowering to NVPTX, run it here too, in case whoever built
+  // our pass pipeline didn't add it.
+  flushFPMsToMPM(PMW);
+  addModulePass(NVVMReflectPass(ST.getSmVersion()), PMW);
+
+  if (getOptLevel() != CodeGenOptLevel::None)
+    addFunctionPass(NVPTXImageOptimizerPass(), PMW);
+  flushFPMsToMPM(PMW);
+  addModulePass(NVPTXAssignValidGlobalNamesPass(), PMW);
+  addModulePass(GenericToNVVMPass(), PMW);
+
+  // Lower variadic calls before address space inference.
+  addModulePass(ExpandVariadicsPass(ExpandVariadicsMode::Lowering), PMW);
+
+  // NVPTXLowerArgs is required for correctness and should be run right
+  // before the address space inference passes.
+  if (TM.getDrvInterface() == NVPTX::CUDA) {
+    addFunctionPass(NVPTXMarkKernelPtrsGlobalPass(), PMW);
+    flushFPMsToMPM(PMW);
+  }
+  addModulePass(NVPTXPromoteParamAlignPass(), PMW);
+  addModulePass(NVPTXLowerArgsPass(TM), PMW);
+  if (getOptLevel() != CodeGenOptLevel::None) {
+    addAddressSpaceInferencePasses(PMW);
+    addStraightLineScalarOptimizationPasses(PMW);
+  } else {
+    // Required for correct stack lowering
+    addFunctionPass(NVPTXLowerAllocaPass(), PMW);
+  }
+
+  addFunctionPass(AtomicExpandPass(TM), PMW);
+  flushFPMsToMPM(PMW);
+  addModulePass(NVPTXCtorDtorLoweringPass(), PMW);
+
+  // === LSR and other generic IR passes ===
+  Base::addIRPasses(PMW);
+  // EarlyCSE is not always strong enough to clean up what LSR produces. For
+  // example, GVN can combine
+  //
+  //   %0 = add %a, %b
+  //   %1 = add %b, %a
+  //
+  // and
+  //
+  //   %0 = shl nsw %a, 2
+  //   %1 = shl %a, 2
+  //
+  // but EarlyCSE can do neither of them.
+  if (getOptLevel() != CodeGenOptLevel::None) {
+    addEarlyCSEOrGVNPass(PMW);
+    if (!DisableLoadStoreVectorizer)
+      addFunctionPass(LoadStoreVectorizerPass(), PMW);
+    addFunctionPass(SROAPass(SROAOptions(SROAOptions::PreserveCFG,
+                                         /*AggregateToVector=*/true)),
+                    PMW);
+    addFunctionPass(NVPTXTagInvariantLoadsPass(), PMW);
+    if (!DisableNVPTXIRPeephole)
+      addFunctionPass(NVPTXIRPeepholePass(), PMW);
+  }
+
+  if (ST.hasPTXASUnreachableBug()) {
+    // Run LowerUnreachable to WAR a ptxas bug. See the commit description of
+    // 1ee4d880e8760256c606fe55b7af85a4f70d006d for more details.
+    addFunctionPass(NVPTXLowerUnreachablePass(TM.Options.TrapUnreachable,
+                                              TM.Options.NoTrapAfterNoreturn),
+                    PMW);
+  }
+}
+
+Error NVPTXCodeGenPassBuilder::addInstSelector(PassManagerWrapper &PMW) const {
+  addFunctionPass(NVPTXLowerAggrCopiesPass(), PMW);
+  addFunctionPass(NVPTXAllocaHoistingPass(), PMW);
+  addMachineFunctionPass(NVPTXISelDAGToDAGPass(TM, getOptLevel()), PMW);
+  addMachineFunctionPass(NVPTXReplaceImageHandlesPass(), PMW);
+  return Error::success();
+}
+
+void NVPTXCodeGenPassBuilder::addPreRegAlloc(PassManagerWrapper &PMW) const {
+  addMachineFunctionPass(NVPTXForwardParamsPass(), PMW);
+  if (getOptLevel() != CodeGenOptLevel::None)
+    addMachineFunctionPass(NVPTXAddressFolderPass(), PMW);
+  // Remove Proxy Register pseudo instructions used to keep `callseq_end` alive.
+  addMachineFunctionPass(NVPTXProxyRegErasurePass(), PMW);
+}
+
+void NVPTXCodeGenPassBuilder::addPostRegAlloc(PassManagerWrapper &PMW) const {
+  addMachineFunctionPass(NVPTXPrologEpilogPass(), PMW);
+  if (getOptLevel() != CodeGenOptLevel::None) {
+    // NVPTXPrologEpilogPass calculates frame object offset and replaces frame
+    // index with VRFrame register. NVPTXPeephole needs to be run after that
+    // and will replace VRFrame with VRFrameLocal when possible.
+    addMachineFunctionPass(NVPTXPeepholePass(), PMW);
+  }
+}
+
+Error NVPTXCodeGenPassBuilder::addFastRegAlloc(PassManagerWrapper &PMW) const {
+  addMachineFunctionPass(PHIEliminationPass(), PMW);
+  addMachineFunctionPass(TwoAddressInstructionPass(), PMW);
+  return Error::success();
+}
+
+Error NVPTXCodeGenPassBuilder::addOptimizedRegAlloc(
+    PassManagerWrapper &PMW) const {
+  addMachineFunctionPass(ProcessImplicitDefsPass(), PMW);
+  // LiveVariables requires pure SSA form and no unreachable blocks; the legacy
+  // pass manager pulls UnreachableMachineBlockElim in as an implicit
+  // dependency, so add it explicitly here.
+  addMachineFunctionPass(UnreachableMachineBlockElimPass(), PMW);
+  addMachineFunctionPass(
+      RequireAnalysisPass<LiveVariablesAnalysis, MachineFunction>(), PMW);
+  addMachineFunctionPass(
+      RequireAnalysisPass<MachineLoopAnalysis, MachineFunction>(), PMW);
+  addMachineFunctionPass(PHIEliminationPass(), PMW);
+
+  addMachineFunctionPass(TwoAddressInstructionPass(), PMW);
+  addMachineFunctionPass(RegisterCoalescerPass(), PMW);
+
+  // PreRA instruction scheduling.
+  addMachineFunctionPass(MachineSchedulerPass(&TM), PMW);
+
+  addMachineFunctionPass(StackSlotColoringPass(), PMW);
+
+  // FIXME: Needs physical registers
+  // addMachineFunctionPass(MachineLICMPass(), PMW);
+
+  return Error::success();
+}
+
+void NVPTXCodeGenPassBuilder::addAsmPrinterBegin(
+    PassManagerWrapper &PMW) const {
+  addModulePass(NVPTXAsmPrinterBeginPass(), PMW, /*Force=*/true);
+}
+
+void NVPTXCodeGenPassBuilder::addAsmPrinter(PassManagerWrapper &PMW) const {
+  addMachineFunctionPass(NVPTXAsmPrinterPass(), PMW);
+}
+
+void NVPTXCodeGenPassBuilder::addAsmPrinterEnd(PassManagerWrapper &PMW) const {
+  addModulePass(NVPTXAsmPrinterEndPass(), PMW);
+}
+
+} // namespace
+
+Error NVPTXTargetMachine::buildCodeGenPipeline(
+    ModulePassManager &MPM, ModuleAnalysisManager &MAM, raw_pwrite_stream &Out,
+    raw_pwrite_stream *DwoOut, CodeGenFileType FileType,
+    const CGPassBuilderOption &Opt, MCContext &Ctx,
+    PassInstrumentationCallbacks *PIC) {
+  auto CGPB = NVPTXCodeGenPassBuilder(*this, Opt, PIC);
+  return CGPB.buildPipeline(MPM, MAM, Out, DwoOut, FileType, Ctx);
+}
diff --git a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
index ccbb23e0bf758..af393964fe846 100644
--- a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
@@ -44,17 +44,16 @@ using namespace llvm;
 
 // LSV is still relatively new; this switch lets us turn it off in case we
 // encounter (or suspect) a bug.
-static cl::opt<bool>
+cl::opt<bool>
     DisableLoadStoreVectorizer("disable-nvptx-load-store-vectorizer",
                                cl::desc("Disable load/store vectorizer"),
                                cl::init(false), cl::Hidden);
 
 // NVPTX IR Peephole is a new pass; this option will lets us turn it off in case
 // we encounter some issues.
-static cl::opt<bool>
-    DisableNVPTXIRPeephole("disable-nvptx-ir-peephole",
-                           cl::desc("Disable NVPTX IR Peephole"),
-                           cl::init(false), cl::Hidden);
+cl::opt<bool> DisableNVPTXIRPeephole("disable-nvptx-ir-peephole",
+                                     cl::desc("Disable NVPTX IR Peephole"),
+                                     cl::init(false), cl::Hidden);
 
 // TODO: Remove this flag when we are confident with no regressions.
 static cl::opt<bool> DisableRequireStructuredCFG(
@@ -137,6 +136,9 @@ NVPTXTargetMachine::NVPTXTargetMachine(const Target &T, const Triple &TT,
       Subtarget(TT, CPU, FS, *this), StrPool(StrAlloc) {
   if (!DisableRequireStructuredCFG)
     setRequiresStructuredCFG(true);
+  // NVPTX does not produce verifier-clean MIR yet; see isMachineVerifierClean()
+  // for the legacy pass manager equivalent.
+  setEnableDefaultMachineVerifier(false);
   initAsmInfo();
 }
 
diff --git a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h
index d15ed1ebddc95..5fab5d970b7ef 100644
--- a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h
+++ b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.h
@@ -66,6 +66,12 @@ class NVPTXTargetMachine : public CodeGenTargetMachineImpl {
 
   void registerPassBuilderCallbacks(PassBuilder &PB) override;
 
+  Error buildCodeGenPipeline(ModulePassManager &MPM, ModuleAnalysisManager &MAM,
+                             raw_pwrite_stream &Out, raw_pwrite_stream *DwoOut,
+                             CodeGenFileType FileType,
+                             const CGPassBuilderOption &Opt, MCContext &Ctx,
+                             PassInstrumentationCallbacks *PIC) override;
+
   TargetTransformInfo getTargetTransformInfo(const Function &F) const override;
 
   bool isMachineVerifierClean() const override { return false; }
diff --git a/llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll b/llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll
new file mode 100644
index 0000000000000..697c73d5ffde4
--- /dev/null
+++ b/llvm/test/CodeGen/NVPTX/llc-pipeline-npm.ll
@@ -0,0 +1,177 @@
+; RUN: llc -enable-new-pm -mtriple=nvptx64-nvidia-cuda -mcpu=sm_90 -O0 \
+; RUN:     -print-pipeline-passes=tree < %s 2>&1 | FileCheck %s --check-prefix=O0
+; RUN: llc -enable-new-pm -mtriple=nvptx64-nvidia-cuda -mcpu=sm_90 -O2 \
+; RUN:     -print-pipeline-passes=tree < %s 2>&1 | FileCheck %s --check-prefix=O2
+
+; O0: require<MachineModuleAnalysis>
+; O0-NEXT: require<profile-summary>
+; O0-NEXT: require<collector-metadata>
+; O0-NEXT: require<runtime-libcall-info>
+; O0-NEXT: require<libcall-lowering-info>
+; O0-NEXT: pre-isel-intrinsic-lowering
+; O0-NEXT: function
+; O0-NEXT:   expand-ir-insts<O0>
+; O0-NEXT: nvvm-reflect
+; O0-NEXT: nvptx-assign-valid-global-names
+; O0-NEXT: generic-to-nvvm
+; O0-NEXT: expand-variadics
+; O0-NEXT: function
+; O0-NEXT:   nvptx-mark-kernel-ptrs-global
+; O0-NEXT: nvptx-promote-param-align
+; O0-NEXT: nvptx-lower-args
+; O0-NEXT: function
+; O0-NEXT:   nvptx-lower-alloca
+; O0-NEXT:   atomic-expand
+; O0-NEXT: nvptx-lower-ctor-dtor
+; O0-NEXT: function
+; O0-NEXT:   verify
+; O0-NEXT:   gc-lowering
+; O0-NEXT: shadow-stack-gc-lowering
+; O0-NEXT: function
+; O0-NEXT:   unreachableblockelim
+; O0-NEXT:   ee-instrument<post-inline>
+; O0-NEXT:   scalarize-masked-mem-intrin
+; O0-NEXT:   expand-reductions
+; O0-NEXT:   nvptx-lower-unreachable
+; O0-NEXT:   lower-invoke
+; O0-NEXT:   unreachableblockelim
+; O0-NEXT:   inline-asm-prepare
+; O0-NEXT:   safe-stack
+; O0-NEXT:   stack-protector
+; O0-NEXT:   verify
+; O0-NEXT: nvptx-asm-printer-begin
+; O0-NEXT: function
+; O0-NEXT:   nvptx-lower-aggr-copies
+; O0-NEXT:   nvptx-alloca-hoisting
+; O0-NEXT:   machine-function
+; O0-NEXT:     nvptx-isel
+; O0-NEXT:     nvptx-replace-image-handles
+; O0-NEXT:     finalize-isel
+; O0-NEXT:     localstackalloc
+; O0-NEXT:     nvptx-forward-params
+; O0-NEXT:     nvptx-proxyreg-erasure
+; O0-NEXT:     phi-node-elimination
+; O0-NEXT:     two-address-instruction
+; O0-NEXT:     nvptx-prolog-epilog
+; O0-NEXT:     remove-redundant-debug-values
+; O0-NEXT:     fixup-statepoint-caller-saved
+; O0-NEXT:     post-ra-pseudos
+; O0-NEXT:     fentry-insert
+; O0-NEXT:     xray-instrumentation
+; O0-NEXT:     live-debug-values
+; O0-NEXT:     machine-sanmd
+; O0-NEXT:     stack-frame-layout
+; O0-NEXT:     nvptx-asm-printer
+; O0-NEXT:   free-machine-function
+; O0-NEXT: nvptx-asm-printer-end
+
+; O2: require<MachineModuleAnalysis>
+; O2-NEXT: require<profile-summary>
+; O2-NEXT: require<collector-metadata>
+; O2-NEXT: require<runtime-libcall-info>
+; O2-NEXT: require<libcall-lowering-info>
+; O2-NEXT: function
+; O2-NEXT:   objc-arc-contract
+; O2-NEXT: pre-isel-intrinsic-lowering
+; O2-NEXT: function
+; O2-NEXT:   expand-ir-insts<O2>
+; O2-NEXT: nvvm-reflect
+; O2-NEXT: function
+; O2-NEXT:   nvptx-image-optimizer
+; O2-NEXT: nvptx-assign-valid-global-names
+; O2-NEXT: generic-to-nvvm
+; O2-NEXT: expand-variadics
+; O2-NEXT: function
+; O2-NEXT:   nvptx-mark-kernel-ptrs-global
+; O2-NEXT: nvptx-promote-param-align
+; O2-NEXT: nvptx-lower-args
+; O2-NEXT: function
+; O2-NEXT:   sroa<preserve-cfg;aggregate-to-vector>
+; O2-NEXT:   nvptx-lower-alloca
+; O2-NEXT:   infer-address-spaces
+; O2-NEXT:   nvptx-atomic-lower
+; O2-NEXT:   separate-const-offset-from-gep<>
+; O2-NEXT:   speculative-execution<>
+; O2-NEXT:   slsr
+; O2-NEXT:   early-cse<>
+; O2-NEXT:   nary-reassociate
+; O2-NEXT:   early-cse<>
+; O2-NEXT:   atomic-expand
+; O2-NEXT: nvptx-lower-ctor-dtor
+; O2-NEXT: function
+; O2-NEXT:   verify
+; O2-NEXT:   loop
+; O2-NEXT:     canon-freeze
+; O2-NEXT:     loop-reduce
+; O2-NEXT:   gc-lowering
+; O2-NEXT: shadow-stack-gc-lowering
+; O2-NEXT: function
+; O2-NEXT:   unreachableblockelim
+; O2-NEXT:   consthoist
+; O2-NEXT:   replace-with-veclib
+; O2-NEXT:   partially-inline-libcalls
+; O2-NEXT:   ee-instrument<post-inline>
+; O2-NEXT:   scalarize-masked-mem-intrin
+; O2-NEXT:   expand-reductions
+; O2-NEXT:   early-cse<>
+; O2-NEXT:   load-store-vectorizer
+; O2-NEXT:   sroa<preserve-cfg;aggregate-to-vector>
+; O2-NEXT:   nvptx-tag-invariant-loads
+; O2-NEXT:   nvptx-ir-peephole
+; O2-NEXT:   nvptx-lower-unreachable
+; O2-NEXT:   codegenprepare
+; O2-NEXT:   lower-invoke
+; O2-NEXT:   unreachableblockelim
+; O2-NEXT:   inline-asm-prepare
+; O2-NEXT:   safe-stack
+; O2-NEXT:   stack-protector
+; O2-NEXT:   verify
+; O2-NEXT: nvptx-asm-printer-begin
+; O2-NEXT: function
+; O2-NEXT:   nvptx-lower-aggr-copies
+; O2-NEXT:   nvptx-alloca-hoisting
+; O2-NEXT:   machine-function
+; O2-NEXT:     nvptx-isel
+; O2-NEXT:     nvptx-replace-image-handles
+; O2-NEXT:     finalize-isel
+; O2-NEXT:     early-tailduplication
+; O2-NEXT:     opt-phis
+; O2-NEXT:     stack-coloring
+; O2-NEXT:     localstackalloc
+; O2-NEXT:     dead-mi-elimination
+; O2-NEXT:     early-machinelicm
+; O2-NEXT:     machine-cse
+; O2-NEXT:     machine-sink
+; O2-NEXT:     peephole-opt
+; O2-NEXT:     dead-mi-elimination
+; O2-NEXT:     nvptx-forward-params
+; O2-NEXT:     nvptx-address-folder
+; O2-NEXT:     nvptx-proxyreg-erasure
+; O2-NEXT:     process-imp-defs
+; O2-NEXT:     unreachable-mbb-elimination
+; O2-NEXT:     require<live-vars>
+; O2-NEXT:     require<machine-loops>
+; O2-NEXT:     phi-node-elimination
+; O2-NEXT:     two-address-instruction
+; O2-NEXT:     register-coalescer
+; O2-NEXT:     machine-scheduler
+; O2-NEXT:     stack-slot-coloring
+; O2-NEXT:     nvptx-prolog-epilog
+; O2-NEXT:     nvptx-peephole
+; O2-NEXT:     remove-redundant-debug-values
+; O2-NEXT:     fixup-statepoint-caller-saved
+; O2-NEXT:     branch-folder
+; O2-NEXT:     post-ra-pseudos
+; O2-NEXT:     block-placement
+; O2-NEXT:     fentry-insert
+; O2-NEXT:     xray-instrumentation
+; O2-NEXT:     live-debug-values
+; O2-NEXT:     machine-sanmd
+; O2-NEXT:     stack-frame-layout
+; O2-NEXT:     nvptx-asm-printer
+; O2-NEXT:   free-machine-function
+; O2-NEXT: nvptx-asm-printer-end
+
+define void @empty() {
+  ret void
+}

>From 41b35c15d6cc1d57937af48bce1be9d83f210fd2 Mon Sep 17 00:00:00 2001
From: Alex Maclean <amaclean at nvidia.com>
Date: Wed, 12 Aug 2026 14:52:50 -0700
Subject: [PATCH 2/2] address comments

---
 .../Target/NVPTX/NVPTXCodeGenPassBuilder.cpp  | 76 +++++++++++++++++--
 llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp  | 65 ----------------
 2 files changed, 70 insertions(+), 71 deletions(-)

diff --git a/llvm/lib/Target/NVPTX/NVPTXCodeGenPassBuilder.cpp b/llvm/lib/Target/NVPTX/NVPTXCodeGenPassBuilder.cpp
index 81419d193f092..bced96a1d3981 100644
--- a/llvm/lib/Target/NVPTX/NVPTXCodeGenPassBuilder.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXCodeGenPassBuilder.cpp
@@ -1,22 +1,23 @@
-//===-- NVPTXCodeGenPassBuilder.cpp - Build NVPTX CodeGen pipeline --------===//
+//===----------------------------------------------------------------------===//
 //
 // 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
 //
 //===----------------------------------------------------------------------===//
-//
-// New pass manager version of the NVPTX codegen pipeline. This mirrors
-// NVPTXPassConfig in NVPTXTargetMachine.cpp; the two must be kept in sync
-// until the legacy pass manager path is removed.
-//
+/// \file
+/// This file contains the NVPTX CodeGen pipeline builder. It mirrors
+/// NVPTXPassConfig in NVPTXTargetMachine.cpp; the two must be kept in sync
+/// until the legacy pass manager path is removed.
 //===----------------------------------------------------------------------===//
 
 #include "NVPTX.h"
+#include "NVPTXAliasAnalysis.h"
 #include "NVPTXAsmPrinter.h"
 #include "NVPTXCtorDtorLowering.h"
 #include "NVPTXSubtarget.h"
 #include "NVPTXTargetMachine.h"
+#include "llvm/Analysis/KernelInfo.h"
 #include "llvm/CodeGen/AtomicExpand.h"
 #include "llvm/CodeGen/DeadMachineInstructionElim.h"
 #include "llvm/CodeGen/MachineCopyPropagation.h"
@@ -57,6 +58,29 @@ using namespace llvm;
 extern cl::opt<bool> DisableLoadStoreVectorizer;
 extern cl::opt<bool> DisableNVPTXIRPeephole;
 
+// byval arguments in NVPTX are special. We're only allowed to read from them
+// using a special instruction, and if we ever need to write to them or take an
+// address, we must make a local copy and use it, instead.
+//
+// The problem is that local copies are very expensive, and we create them very
+// late in the compilation pipeline, so LLVM does not have much of a chance to
+// eliminate them, if they turn out to be unnecessary.
+//
+// One way around that is to create such copies early on, and let them percolate
+// through the optimizations. The copying itself will never trigger creation of
+// another copy later on, as the reads are allowed. If LLVM can eliminate it,
+// it's a win. It the full optimization pipeline can't remove the copy, that's
+// as good as it gets in terms of the effort we could've done, and it's
+// certainly a much better effort than what we do now.
+//
+// This early injection of the copies has potential to create undesireable
+// side-effects, so it's disabled by default, for now, until it sees more
+// testing.
+static cl::opt<bool> EarlyByValArgsCopy(
+    "nvptx-early-byval-copy",
+    cl::desc("Create a copy of byval function arguments early."),
+    cl::init(false), cl::Hidden);
+
 namespace {
 
 class NVPTXCodeGenPassBuilder
@@ -294,6 +318,46 @@ void NVPTXCodeGenPassBuilder::addAsmPrinterEnd(PassManagerWrapper &PMW) const {
 
 } // namespace
 
+void NVPTXTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) {
+#define GET_PASS_REGISTRY "NVPTXPassRegistry.def"
+#include "llvm/Passes/TargetPassRegistry.inc"
+
+  // TODO: Move this into the base CodeGenPassBuilder once all targets that
+  // currently implement it have a ported asm-printer pass.
+  if (PIC) {
+    PIC->addClassToPassName(NVPTXAsmPrinterBeginPass::name(),
+                            "nvptx-asm-printer-begin");
+    PIC->addClassToPassName(NVPTXAsmPrinterPass::name(), "nvptx-asm-printer");
+    PIC->addClassToPassName(NVPTXAsmPrinterEndPass::name(),
+                            "nvptx-asm-printer-end");
+  }
+
+  PB.registerPipelineStartEPCallback(
+      [this](ModulePassManager &PM, OptimizationLevel Level) {
+        // We do not want to fold out calls to nvvm.reflect early if the user
+        // has not provided a target architecture just yet.
+        if (Subtarget.hasTargetName())
+          PM.addPass(NVVMReflectPass(Subtarget.getSmVersion()));
+
+        FunctionPassManager FPM;
+        // Note: NVVMIntrRangePass was causing numerical discrepancies at one
+        // point, if issues crop up, consider disabling.
+        FPM.addPass(NVVMIntrRangePass());
+        if (EarlyByValArgsCopy)
+          FPM.addPass(NVPTXCopyByValArgsPass());
+        PM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+      });
+
+  if (!NoKernelInfoEndLTO) {
+    PB.registerFullLinkTimeOptimizationLastEPCallback(
+        [this](ModulePassManager &PM, OptimizationLevel Level) {
+          FunctionPassManager FPM;
+          FPM.addPass(KernelInfoPrinter(this));
+          PM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
+        });
+  }
+}
+
 Error NVPTXTargetMachine::buildCodeGenPipeline(
     ModulePassManager &MPM, ModuleAnalysisManager &MAM, raw_pwrite_stream &Out,
     raw_pwrite_stream *DwoOut, CodeGenFileType FileType,
diff --git a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
index af393964fe846..d6248622c5eed 100644
--- a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
@@ -19,14 +19,12 @@
 #include "NVPTXTargetObjectFile.h"
 #include "NVPTXTargetTransformInfo.h"
 #include "TargetInfo/NVPTXTargetInfo.h"
-#include "llvm/Analysis/KernelInfo.h"
 #include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/CodeGen/Passes.h"
 #include "llvm/CodeGen/TargetPassConfig.h"
 #include "llvm/IR/IntrinsicsNVPTX.h"
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Pass.h"
-#include "llvm/Passes/PassBuilder.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Target/TargetMachine.h"
@@ -63,29 +61,6 @@ static cl::opt<bool> DisableRequireStructuredCFG(
              "unexpected regressions happen."),
     cl::init(false), cl::Hidden);
 
-// byval arguments in NVPTX are special. We're only allowed to read from them
-// using a special instruction, and if we ever need to write to them or take an
-// address, we must make a local copy and use it, instead.
-//
-// The problem is that local copies are very expensive, and we create them very
-// late in the compilation pipeline, so LLVM does not have much of a chance to
-// eliminate them, if they turn out to be unnecessary.
-//
-// One way around that is to create such copies early on, and let them percolate
-// through the optimizations. The copying itself will never trigger creation of
-// another copy later on, as the reads are allowed. If LLVM can eliminate it,
-// it's a win. It the full optimization pipeline can't remove the copy, that's
-// as good as it gets in terms of the effort we could've done, and it's
-// certainly a much better effort than what we do now.
-//
-// This early injection of the copies has potential to create undesireable
-// side-effects, so it's disabled by default, for now, until it sees more
-// testing.
-static cl::opt<bool> EarlyByValArgsCopy(
-    "nvptx-early-byval-copy",
-    cl::desc("Create a copy of byval function arguments early."),
-    cl::init(false), cl::Hidden);
-
 extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeNVPTXTarget() {
   // Register the target.
   RegisterTargetMachine<NVPTXTargetMachine> X(getTheNVPTXTarget32());
@@ -201,46 +176,6 @@ void NVPTXTargetMachine::registerEarlyDefaultAliasAnalyses(AAManager &AAM) {
   AAM.registerFunctionAnalysis<NVPTXAA>();
 }
 
-void NVPTXTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) {
-#define GET_PASS_REGISTRY "NVPTXPassRegistry.def"
-#include "llvm/Passes/TargetPassRegistry.inc"
-
-  // TODO: Move this into the base CodeGenPassBuilder once all targets that
-  // currently implement it have a ported asm-printer pass.
-  if (PIC) {
-    PIC->addClassToPassName(NVPTXAsmPrinterBeginPass::name(),
-                            "nvptx-asm-printer-begin");
-    PIC->addClassToPassName(NVPTXAsmPrinterPass::name(), "nvptx-asm-printer");
-    PIC->addClassToPassName(NVPTXAsmPrinterEndPass::name(),
-                            "nvptx-asm-printer-end");
-  }
-
-  PB.registerPipelineStartEPCallback(
-      [this](ModulePassManager &PM, OptimizationLevel Level) {
-        // We do not want to fold out calls to nvvm.reflect early if the user
-        // has not provided a target architecture just yet.
-        if (Subtarget.hasTargetName())
-          PM.addPass(NVVMReflectPass(Subtarget.getSmVersion()));
-
-        FunctionPassManager FPM;
-        // Note: NVVMIntrRangePass was causing numerical discrepancies at one
-        // point, if issues crop up, consider disabling.
-        FPM.addPass(NVVMIntrRangePass());
-        if (EarlyByValArgsCopy)
-          FPM.addPass(NVPTXCopyByValArgsPass());
-        PM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
-      });
-
-  if (!NoKernelInfoEndLTO) {
-    PB.registerFullLinkTimeOptimizationLastEPCallback(
-        [this](ModulePassManager &PM, OptimizationLevel Level) {
-          FunctionPassManager FPM;
-          FPM.addPass(KernelInfoPrinter(this));
-          PM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM)));
-        });
-  }
-}
-
 TargetTransformInfo
 NVPTXTargetMachine::getTargetTransformInfo(const Function &F) const {
   return TargetTransformInfo(std::make_unique<NVPTXTTIImpl>(this, F));



More information about the llvm-commits mailing list