[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