[llvm-branch-commits] [llvm] [AMDGPU] Warn for using <4 x i32> buffer intrinsics (PR #231301)

Krzysztof Drewniak via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Oct 13 09:59:57 PDT 2026


https://github.com/krzysz00 updated https://github.com/llvm/llvm-project/pull/231301

>From a07a47641641b273fdf5b7cd50580955931264e7 Mon Sep 17 00:00:00 2001
From: Krzysztof Drewniak <Krzysztof.Drewniak at amd.com>
Date: Fri, 9 Oct 2026 16:26:04 +0000
Subject: [PATCH 1/3] [AMDGPU] Warn for using <4 x i32> buffer intrinsics

The buffer intrinsics that take their resource as a <4 x i32> have been
documented as deprecated in IntrinsicsAMDGPU.td for some time, but only
in a TableGen comment, so nothing told users about it. They've all got
`ptr addrspace(8)` counterparts now that #209243 landed, so we can be
slightly more official about asking people to move.

The warning lives in AMDGPUAsmPrinter::doInitialization next to the
no-subarch deprecation warning.

We warn once per intrinsic used, not once per call (it'd cause
unbounded warning spam) and not once per declaration (it'd catch the
same intrinsic but with different types repeatedly). We try to be
clever to point at a specific call if we can find one with a debug
location.

Users can pass -amdgpu-warn-deprecated-buffer-intrinsics=0 to suppress
this.

AI disclosure: Claude wrote the code and tests, I wrote this message
and cleaaned up comment wording.

Co-Authored-By: Claude Opus 5 (1M context) <noreply at anthropic.com>
---
 llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp   | 60 +++++++++++++
 .../Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp    |  9 ++
 llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h |  6 ++
 .../deprecated-buffer-intrinsics-warn-loc.ll  | 35 ++++++++
 .../deprecated-buffer-intrinsics-warn.ll      | 58 +++++++++++++
 .../AMDGPU/BufferIntrinsicDeprecationTest.cpp | 86 +++++++++++++++++++
 llvm/unittests/Target/AMDGPU/CMakeLists.txt   |  1 +
 7 files changed, 255 insertions(+)
 create mode 100644 llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
 create mode 100644 llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
 create mode 100644 llvm/unittests/Target/AMDGPU/BufferIntrinsicDeprecationTest.cpp

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
index 06acc7613d6608..75aacc6c7f3e87 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
@@ -18,6 +18,7 @@
 #include "AMDGPUAsmPrinter.h"
 #include "AMDGPU.h"
 #include "AMDGPUHSAMetadataStreamer.h"
+#include "AMDGPUInstrInfo.h"
 #include "AMDGPUMCResourceInfo.h"
 #include "AMDGPUResourceUsageAnalysis.h"
 #include "AMDGPUTargetMachine.h"
@@ -32,6 +33,7 @@
 #include "Utils/AMDGPUBaseInfo.h"
 #include "Utils/AMDKernelCodeTUtils.h"
 #include "Utils/SIDefinesUtils.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/CodeGen/AsmPrinterAnalysis.h"
@@ -55,6 +57,11 @@
 using namespace llvm;
 using namespace llvm::AMDGPU;
 
+static cl::opt<bool> WarnDeprecatedBufferIntrinsics(
+    "amdgpu-warn-deprecated-buffer-intrinsics", cl::Hidden, cl::init(true),
+    cl::desc("Warn about AMDGPU buffer intrinsics that take their resource as "
+             "a <4 x i32> instead of a ptr addrspace(8)"));
+
 // This should get the default rounding mode from the kernel. We just set the
 // default here, but this could change if the OpenCL rounding mode pragmas are
 // used.
@@ -384,6 +391,56 @@ void AMDGPUAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
   AsmPrinter::emitGlobalVariable(GV);
 }
 
+/// Warn about the buffer intrinsics in \p M that take their resource as a
+/// <4 x i32> rather than a ptr addrspace(8), pointing at the replacement.
+/// Diagnose once per intrinsic, ignoring types, to avoid flooding the user with
+/// warnings.
+static void warnOnDeprecatedBufferIntrinsics(Module &M) {
+  SmallSet<Intrinsic::ID, 8> Reported;
+  for (const Function &F : M) {
+    if (!F.isDeclaration() || F.use_empty())
+      continue;
+    Intrinsic::ID ID = F.getIntrinsicID();
+    if (ID == Intrinsic::not_intrinsic)
+      continue;
+
+    const AMDGPU::RsrcIntrinsic *RI = AMDGPU::lookupRsrcIntrinsic(ID);
+    // TODO: Add smart pointers for images
+    if (!RI || RI->IsImage)
+      continue;
+    FunctionType *FT = F.getFunctionType();
+    if (FT->getParamType(RI->RsrcArg)->isPointerTy())
+      continue;
+
+    if (!Reported.insert(ID).second)
+      continue;
+
+    StringRef Name = Intrinsic::getBaseName(ID);
+    std::string Repl = AMDGPU::getPtrBufferIntrinsicName(Name);
+    std::string Msg = ("'" + Name +
+                       "' is deprecated and will be removed in a future LLVM "
+                       "release; use '" +
+                       Repl +
+                       "' with a 'ptr addrspace(8)' resource created by "
+                       "'llvm.amdgcn.make.buffer.rsrc' instead")
+                          .str();
+
+    const CallBase *Call = nullptr;
+    for (const User *U : F.users()) {
+      const auto *CB = dyn_cast<CallBase>(U);
+      if (CB && CB->getFunction() && CB->getDebugLoc()) {
+        Call = CB;
+        break;
+      }
+    }
+    if (Call)
+      M.getContext().diagnose(DiagnosticInfoGenericWithLoc(
+          Msg, *Call->getFunction(), Call->getDebugLoc(), DS_Warning));
+    else
+      M.getContext().diagnose(DiagnosticInfoGeneric(Msg, DS_Warning));
+  }
+}
+
 bool AMDGPUAsmPrinter::doInitialization(Module &M) {
   const Triple &TT = M.getTargetTriple();
   if (TT.getSubArch() == Triple::NoSubArch) {
@@ -405,6 +462,9 @@ bool AMDGPUAsmPrinter::doInitialization(Module &M) {
     }
   }
 
+  if (WarnDeprecatedBufferIntrinsics)
+    warnOnDeprecatedBufferIntrinsics(M);
+
   CodeObjectVersion = AMDGPU::getAMDHSACodeObjectVersion(M);
 
   if (TT.getOS() == Triple::AMDHSA) {
diff --git a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
index 4a464dc3c36660..fcab6c58a554cd 100644
--- a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
+++ b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.cpp
@@ -3366,6 +3366,15 @@ bool isIntrinsicAlwaysUniform(unsigned IntrID) {
   return lookupAlwaysUniform(IntrID);
 }
 
+std::string getPtrBufferIntrinsicName(StringRef BaseName) {
+  if (BaseName == "llvm.amdgcn.s.buffer.load")
+    return "llvm.amdgcn.ptr.s.buffer.load";
+  for (StringRef Prefix : {"llvm.amdgcn.raw.", "llvm.amdgcn.struct."})
+    if (BaseName.starts_with(Prefix))
+      return (Prefix + "ptr." + BaseName.drop_front(Prefix.size())).str();
+  return "";
+}
+
 const GcnBufferFormatInfo *getGcnBufferFormatInfo(uint8_t BitsPerComp,
                                                   uint8_t NumComponents,
                                                   uint8_t NumFormat,
diff --git a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h
index dfee7bbdbf2518..bcf6abbde8d429 100644
--- a/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h
+++ b/llvm/lib/Target/AMDGPU/Utils/AMDGPUBaseInfo.h
@@ -1783,6 +1783,12 @@ bool isIntrinsicSourceOfDivergence(unsigned IntrID);
 /// \returns true if the intrinsic is uniform
 bool isIntrinsicAlwaysUniform(unsigned IntrID);
 
+/// Given \p BaseName, the base name of a buffer intrinsic that takes its
+/// resource as a <4 x i32>, \returns the base name of the deprecation
+/// replacement that takes a ptr addrspace(8), or "" if \p BaseName does not
+/// look like such an intrinsic.
+std::string getPtrBufferIntrinsicName(StringRef BaseName);
+
 /// \returns a register class for the physical register \p Reg if it is a VGPR
 /// or nullptr otherwise.
 const MCRegisterClass *getVGPRPhysRegClass(MCRegister Reg,
diff --git a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
new file mode 100644
index 00000000000000..0b7e7e1aa9986b
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
@@ -0,0 +1,35 @@
+; When a call to a deprecated buffer intrinsic carries a debug location, the
+; deprecation warning reports it, so a user of a large module has somewhere to
+; go. The diagnostic is still emitted once per intrinsic, not once per call.
+
+; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null %s 2>&1 | \
+; RUN:   FileCheck --implicit-check-not=warning: %s
+
+; Reported against the first call that has a location, not the one without, and
+; not twice even though there are three calls.
+; CHECK: warning: shader.cl:42:7: 'llvm.amdgcn.raw.buffer.load' is deprecated {{.*}} use 'llvm.amdgcn.raw.ptr.buffer.load' {{.*}}
+
+; An intrinsic whose calls carry no location still warns, without picking up a
+; useless "<unknown>:0:0" prefix.
+; CHECK: warning: 'llvm.amdgcn.s.buffer.load' is deprecated {{.*}} use 'llvm.amdgcn.ptr.s.buffer.load' {{.*}}
+
+define amdgpu_ps float @no_loc_first(<4 x i32> inreg %rsrc, i32 %off) !dbg !4 {
+  %a = call float @llvm.amdgcn.raw.buffer.load.f32(<4 x i32> %rsrc, i32 %off, i32 0, i32 0)
+  %b = call float @llvm.amdgcn.raw.buffer.load.f32(<4 x i32> %rsrc, i32 %off, i32 4, i32 0), !dbg !7
+  %c = call i32 @llvm.amdgcn.s.buffer.load.i32(<4 x i32> %rsrc, i32 0, i32 0)
+  %cf = bitcast i32 %c to float
+  %s0 = fadd float %a, %b
+  %s1 = fadd float %s0, %cf
+  ret float %s1
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
+!1 = !DIFile(filename: "shader.cl", directory: "/src")
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = distinct !DISubprogram(name: "no_loc_first", scope: !1, file: !1, line: 10, type: !5, scopeLine: 10, spFlags: DISPFlagDefinition, unit: !0)
+!5 = !DISubroutineType(types: !6)
+!6 = !{}
+!7 = !DILocation(line: 42, column: 7, scope: !4)
diff --git a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
new file mode 100644
index 00000000000000..93b2ebebacf8b0
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
@@ -0,0 +1,58 @@
+; Buffer intrinsics that take their resource as a <4 x i32> are deprecated.
+; Check that codegen warns once per intrinsic and names the replacement.
+;
+; The --implicit-check-not=warning: ensure we don't have extra warnings.
+
+; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null %s 2>&1 | \
+; RUN:   FileCheck --implicit-check-not=warning: %s
+
+; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null \
+; RUN:   -amdgpu-warn-deprecated-buffer-intrinsics=0 %s 2>&1 | \
+; RUN:   FileCheck -check-prefix=OFF --allow-empty --implicit-check-not=warning: %s
+
+; CHECK: warning: 'llvm.amdgcn.raw.buffer.atomic.add' is deprecated and will be removed in a future LLVM release; use 'llvm.amdgcn.raw.ptr.buffer.atomic.add' with a 'ptr addrspace(8)' resource created by 'llvm.amdgcn.make.buffer.rsrc' instead
+; CHECK: warning: 'llvm.amdgcn.raw.buffer.load' is deprecated {{.*}} use 'llvm.amdgcn.raw.ptr.buffer.load' {{.*}}
+; CHECK: warning: 'llvm.amdgcn.raw.tbuffer.load' is deprecated {{.*}} use 'llvm.amdgcn.raw.ptr.tbuffer.load' {{.*}}
+
+; CHECK: warning: 'llvm.amdgcn.s.buffer.load' is deprecated {{.*}} use 'llvm.amdgcn.ptr.s.buffer.load' {{.*}}
+
+; CHECK: warning: 'llvm.amdgcn.struct.buffer.store' is deprecated {{.*}} use 'llvm.amdgcn.struct.ptr.buffer.store' {{.*}}
+
+; OFF-NOT: {{.}}
+
+; Two manglings of llvm.amdgcn.s.buffer.load, to check that the warning is
+; emitted per intrinsic rather than per declaration.
+define amdgpu_ps float @s_buffer_load_two_manglings(<4 x i32> inreg %rsrc) {
+  %a = call i32 @llvm.amdgcn.s.buffer.load.i32(<4 x i32> %rsrc, i32 0, i32 0)
+  %b = call <4 x i32> @llvm.amdgcn.s.buffer.load.v4i32(<4 x i32> %rsrc, i32 16, i32 0)
+  %b0 = extractelement <4 x i32> %b, i32 0
+  %sum = add i32 %a, %b0
+  %r = bitcast i32 %sum to float
+  ret float %r
+}
+
+define amdgpu_ps float @deprecated_buffer_ops(<4 x i32> inreg %rsrc, i32 %off) {
+  %a = call float @llvm.amdgcn.raw.buffer.load.f32(<4 x i32> %rsrc, i32 %off, i32 0, i32 0)
+  %b = call float @llvm.amdgcn.raw.tbuffer.load.f32(<4 x i32> %rsrc, i32 %off, i32 0, i32 78, i32 0)
+  %c = call i32 @llvm.amdgcn.raw.buffer.atomic.add.i32(i32 1, <4 x i32> %rsrc, i32 %off, i32 0, i32 0)
+  %cf = bitcast i32 %c to float
+  call void @llvm.amdgcn.struct.buffer.store.f32(float %a, <4 x i32> %rsrc, i32 0, i32 %off, i32 0, i32 0)
+  %s0 = fadd float %a, %b
+  %s1 = fadd float %s0, %cf
+  ret float %s1
+}
+
+define amdgpu_ps float @ptr_buffer_load(ptr addrspace(8) inreg %rsrc, i32 %off) {
+  %v = call float @llvm.amdgcn.raw.ptr.buffer.load.f32(ptr addrspace(8) %rsrc, i32 %off, i32 0, i32 0)
+  ret float %v
+}
+
+; An image resource is a <8 x i32>, which must not be mistaken for a deprecated
+; buffer resource.
+define amdgpu_ps <4 x float> @image_load(<8 x i32> inreg %rsrc, i32 %s) {
+  %v = call <4 x float> @llvm.amdgcn.image.load.1d.v4f32.i32(i32 15, i32 %s, <8 x i32> %rsrc, i32 0, i32 0)
+  ret <4 x float> %v
+}
+
+; Declared but never called.
+declare void @llvm.amdgcn.struct.tbuffer.store.f32(float, <4 x i32>, i32, i32, i32, i32, i32)
diff --git a/llvm/unittests/Target/AMDGPU/BufferIntrinsicDeprecationTest.cpp b/llvm/unittests/Target/AMDGPU/BufferIntrinsicDeprecationTest.cpp
new file mode 100644
index 00000000000000..7d3cf7d912b2bc
--- /dev/null
+++ b/llvm/unittests/Target/AMDGPU/BufferIntrinsicDeprecationTest.cpp
@@ -0,0 +1,86 @@
+//===- BufferIntrinsicDeprecationTest.cpp ---------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Test to ensure we don't direct users to an intrinsic that doesn't exist when
+// warning about deprecated <4 x i32> intrinsics in AMDGPUAsmPrinter
+//
+//===----------------------------------------------------------------------===//
+
+#include "AMDGPUInstrInfo.h"
+#include "Utils/AMDGPUBaseInfo.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsAMDGPU.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+static bool isPtrBufferIntrinsic(Intrinsic::ID ID, StringRef Name) {
+  if (ID == Intrinsic::amdgcn_s_buffer_prefetch_data)
+    return true;
+  return Name.contains(".ptr.");
+}
+
+namespace {
+
+TEST(AMDGPUBufferIntrinsicDeprecation,
+     PtrReplacementNamesAreTotalAndInjective) {
+  unsigned NumDeprecated = 0, NumPtr = 0;
+  DenseSet<unsigned> Replacements;
+
+  for (unsigned ID = 1; ID < Intrinsic::num_intrinsics; ++ID) {
+    const AMDGPU::RsrcIntrinsic *RI = AMDGPU::lookupRsrcIntrinsic(ID);
+    // No image fat pointers yet.
+    if (!RI || RI->IsImage)
+      continue;
+
+    StringRef Name = Intrinsic::getBaseName(ID);
+    if (isPtrBufferIntrinsic(ID, Name)) {
+      ++NumPtr;
+      continue;
+    }
+    ++NumDeprecated;
+
+    std::string Repl = AMDGPU::getPtrBufferIntrinsicName(Name);
+    ASSERT_FALSE(Repl.empty()) << "no replacement derived for " << Name;
+
+    unsigned ReplID = Intrinsic::lookupIntrinsicID(Repl);
+    ASSERT_NE(ReplID, Intrinsic::not_intrinsic)
+        << Name << " -> " << Repl << " is not an intrinsic";
+
+    const AMDGPU::RsrcIntrinsic *ReplRI = AMDGPU::lookupRsrcIntrinsic(ReplID);
+    ASSERT_TRUE(ReplRI) << Repl << " is not a resource intrinsic";
+    EXPECT_FALSE(ReplRI->IsImage) << Repl << " is an image intrinsic";
+    EXPECT_EQ(ReplRI->RsrcArg, RI->RsrcArg)
+        << Name << " -> " << Repl << " moves the resource operand";
+
+    EXPECT_TRUE(Replacements.insert(ReplID).second)
+        << Repl << " is the replacement for more than one intrinsic";
+  }
+
+  EXPECT_EQ(NumDeprecated, 55u);
+  EXPECT_EQ(NumPtr, 56u);
+}
+
+TEST(AMDGPUBufferIntrinsicDeprecation, ReplacementNameSpellings) {
+  EXPECT_EQ(AMDGPU::getPtrBufferIntrinsicName("llvm.amdgcn.s.buffer.load"),
+            "llvm.amdgcn.ptr.s.buffer.load");
+  EXPECT_EQ(AMDGPU::getPtrBufferIntrinsicName("llvm.amdgcn.raw.buffer.load"),
+            "llvm.amdgcn.raw.ptr.buffer.load");
+  EXPECT_EQ(
+      AMDGPU::getPtrBufferIntrinsicName("llvm.amdgcn.struct.buffer.store"),
+      "llvm.amdgcn.struct.ptr.buffer.store");
+
+  EXPECT_EQ(
+      AMDGPU::getPtrBufferIntrinsicName("llvm.amdgcn.raw.atomic.buffer.load"),
+      "llvm.amdgcn.raw.ptr.atomic.buffer.load");
+
+  EXPECT_EQ(AMDGPU::getPtrBufferIntrinsicName("llvm.amdgcn.image.load.1d"), "");
+}
+
+} // end anonymous namespace
diff --git a/llvm/unittests/Target/AMDGPU/CMakeLists.txt b/llvm/unittests/Target/AMDGPU/CMakeLists.txt
index 2bb9b6dedba13e..3214d93e8fd66d 100644
--- a/llvm/unittests/Target/AMDGPU/CMakeLists.txt
+++ b/llvm/unittests/Target/AMDGPU/CMakeLists.txt
@@ -25,6 +25,7 @@ set(LLVM_LINK_COMPONENTS
 add_llvm_target_unittest(AMDGPUTests
   AMDGPUMCExprTest.cpp
   AMDGPUUnitTests.cpp
+  BufferIntrinsicDeprecationTest.cpp
   CSETest.cpp
   DwarfRegMappings.cpp
   ExecMayBeModifiedBeforeAnyUse.cpp

>From 1c85fb83d897e6d17508846a0eba8d1781096070 Mon Sep 17 00:00:00 2001
From: Krzysztof Drewniak <Krzysztof.Drewniak at amd.com>
Date: Mon, 12 Oct 2026 22:44:14 +0000
Subject: [PATCH 2/3] [AI fixup] Test nits

Address review nits from PR #231308 on the deprecation warning tests:
- Use `< %s` instead of passing the file as an argument.
- Keep the RUN lines contiguous and the CHECK lines in one block
  instead of splitting them up with blank lines.
- Fix subject/verb agreement in the header comment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply at anthropic.com>
---
 .../AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll      | 2 +-
 .../CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll  | 9 +++------
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
index 0b7e7e1aa9986b..c4ba4805ffc7d6 100644
--- a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
+++ b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
@@ -2,7 +2,7 @@
 ; deprecation warning reports it, so a user of a large module has somewhere to
 ; go. The diagnostic is still emitted once per intrinsic, not once per call.
 
-; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null %s 2>&1 | \
+; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null < %s 2>&1 | \
 ; RUN:   FileCheck --implicit-check-not=warning: %s
 
 ; Reported against the first call that has a location, not the one without, and
diff --git a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
index 93b2ebebacf8b0..fb123e2a72d602 100644
--- a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
+++ b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
@@ -1,21 +1,18 @@
 ; Buffer intrinsics that take their resource as a <4 x i32> are deprecated.
 ; Check that codegen warns once per intrinsic and names the replacement.
 ;
-; The --implicit-check-not=warning: ensure we don't have extra warnings.
+; The --implicit-check-not=warning: ensures we don't have extra warnings.
 
-; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null %s 2>&1 | \
+; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null < %s 2>&1 | \
 ; RUN:   FileCheck --implicit-check-not=warning: %s
-
 ; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null \
-; RUN:   -amdgpu-warn-deprecated-buffer-intrinsics=0 %s 2>&1 | \
+; RUN:   -amdgpu-warn-deprecated-buffer-intrinsics=0 < %s 2>&1 | \
 ; RUN:   FileCheck -check-prefix=OFF --allow-empty --implicit-check-not=warning: %s
 
 ; CHECK: warning: 'llvm.amdgcn.raw.buffer.atomic.add' is deprecated and will be removed in a future LLVM release; use 'llvm.amdgcn.raw.ptr.buffer.atomic.add' with a 'ptr addrspace(8)' resource created by 'llvm.amdgcn.make.buffer.rsrc' instead
 ; CHECK: warning: 'llvm.amdgcn.raw.buffer.load' is deprecated {{.*}} use 'llvm.amdgcn.raw.ptr.buffer.load' {{.*}}
 ; CHECK: warning: 'llvm.amdgcn.raw.tbuffer.load' is deprecated {{.*}} use 'llvm.amdgcn.raw.ptr.tbuffer.load' {{.*}}
-
 ; CHECK: warning: 'llvm.amdgcn.s.buffer.load' is deprecated {{.*}} use 'llvm.amdgcn.ptr.s.buffer.load' {{.*}}
-
 ; CHECK: warning: 'llvm.amdgcn.struct.buffer.store' is deprecated {{.*}} use 'llvm.amdgcn.struct.ptr.buffer.store' {{.*}}
 
 ; OFF-NOT: {{.}}

>From 518c1caf780431a2778f5891d2f608f1c027b693 Mon Sep 17 00:00:00 2001
From: Krzysztof Drewniak <Krzysztof.Drewniak at amd.com>
Date: Tue, 13 Oct 2026 16:43:48 +0000
Subject: [PATCH 3/3] [AI fixup] Move the deprecation warning to
 amdgpu-lower-intrinsics

Address rampitec's review on PR #231301: the AsmPrinter is too late a
place to diagnose an IR-level deprecation.

AMDGPULowerIntrinsics is the natural home. It is already a ModulePass
that walks intrinsic declarations and emits diagnostics, and its stated
purpose is handling intrinsics "that would otherwise require separate
handling in both SelectionDAG and GlobalISel" - which is exactly the
situation here. The warning now runs at the start of that pass, before
any lowering, so it describes the module the user wrote.

This is a safe place to look for the deprecated intrinsics: no IR pass
creates them (AMDGPULowerBufferFatPointers, which runs immediately
before, only ever creates the .ptr. forms), so there are no false
positives, and nothing has rewritten them away by this point either.

As a side effect the diagnostic is now reachable from opt, so the tests
cover the pass directly in addition to going through llc. They also
now cover GlobalISel and -O0, since the pass is in addCodeGenPrepare
and runs at every optimization level.

AMDGPUAsmPrinter.cpp is left untouched by this commit as a result.

Co-Authored-By: Claude Opus 5 (1M context) <noreply at anthropic.com>
---
 llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp   | 60 ------------------
 .../Target/AMDGPU/AMDGPULowerIntrinsics.cpp   | 62 +++++++++++++++++++
 .../deprecated-buffer-intrinsics-warn-loc.ll  |  3 +
 .../deprecated-buffer-intrinsics-warn.ll      |  3 +
 .../AMDGPU/BufferIntrinsicDeprecationTest.cpp |  2 +-
 5 files changed, 69 insertions(+), 61 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
index 75aacc6c7f3e87..06acc7613d6608 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp
@@ -18,7 +18,6 @@
 #include "AMDGPUAsmPrinter.h"
 #include "AMDGPU.h"
 #include "AMDGPUHSAMetadataStreamer.h"
-#include "AMDGPUInstrInfo.h"
 #include "AMDGPUMCResourceInfo.h"
 #include "AMDGPUResourceUsageAnalysis.h"
 #include "AMDGPUTargetMachine.h"
@@ -33,7 +32,6 @@
 #include "Utils/AMDGPUBaseInfo.h"
 #include "Utils/AMDKernelCodeTUtils.h"
 #include "Utils/SIDefinesUtils.h"
-#include "llvm/ADT/SmallSet.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/CodeGen/AsmPrinterAnalysis.h"
@@ -57,11 +55,6 @@
 using namespace llvm;
 using namespace llvm::AMDGPU;
 
-static cl::opt<bool> WarnDeprecatedBufferIntrinsics(
-    "amdgpu-warn-deprecated-buffer-intrinsics", cl::Hidden, cl::init(true),
-    cl::desc("Warn about AMDGPU buffer intrinsics that take their resource as "
-             "a <4 x i32> instead of a ptr addrspace(8)"));
-
 // This should get the default rounding mode from the kernel. We just set the
 // default here, but this could change if the OpenCL rounding mode pragmas are
 // used.
@@ -391,56 +384,6 @@ void AMDGPUAsmPrinter::emitGlobalVariable(const GlobalVariable *GV) {
   AsmPrinter::emitGlobalVariable(GV);
 }
 
-/// Warn about the buffer intrinsics in \p M that take their resource as a
-/// <4 x i32> rather than a ptr addrspace(8), pointing at the replacement.
-/// Diagnose once per intrinsic, ignoring types, to avoid flooding the user with
-/// warnings.
-static void warnOnDeprecatedBufferIntrinsics(Module &M) {
-  SmallSet<Intrinsic::ID, 8> Reported;
-  for (const Function &F : M) {
-    if (!F.isDeclaration() || F.use_empty())
-      continue;
-    Intrinsic::ID ID = F.getIntrinsicID();
-    if (ID == Intrinsic::not_intrinsic)
-      continue;
-
-    const AMDGPU::RsrcIntrinsic *RI = AMDGPU::lookupRsrcIntrinsic(ID);
-    // TODO: Add smart pointers for images
-    if (!RI || RI->IsImage)
-      continue;
-    FunctionType *FT = F.getFunctionType();
-    if (FT->getParamType(RI->RsrcArg)->isPointerTy())
-      continue;
-
-    if (!Reported.insert(ID).second)
-      continue;
-
-    StringRef Name = Intrinsic::getBaseName(ID);
-    std::string Repl = AMDGPU::getPtrBufferIntrinsicName(Name);
-    std::string Msg = ("'" + Name +
-                       "' is deprecated and will be removed in a future LLVM "
-                       "release; use '" +
-                       Repl +
-                       "' with a 'ptr addrspace(8)' resource created by "
-                       "'llvm.amdgcn.make.buffer.rsrc' instead")
-                          .str();
-
-    const CallBase *Call = nullptr;
-    for (const User *U : F.users()) {
-      const auto *CB = dyn_cast<CallBase>(U);
-      if (CB && CB->getFunction() && CB->getDebugLoc()) {
-        Call = CB;
-        break;
-      }
-    }
-    if (Call)
-      M.getContext().diagnose(DiagnosticInfoGenericWithLoc(
-          Msg, *Call->getFunction(), Call->getDebugLoc(), DS_Warning));
-    else
-      M.getContext().diagnose(DiagnosticInfoGeneric(Msg, DS_Warning));
-  }
-}
-
 bool AMDGPUAsmPrinter::doInitialization(Module &M) {
   const Triple &TT = M.getTargetTriple();
   if (TT.getSubArch() == Triple::NoSubArch) {
@@ -462,9 +405,6 @@ bool AMDGPUAsmPrinter::doInitialization(Module &M) {
     }
   }
 
-  if (WarnDeprecatedBufferIntrinsics)
-    warnOnDeprecatedBufferIntrinsics(M);
-
   CodeObjectVersion = AMDGPU::getAMDHSACodeObjectVersion(M);
 
   if (TT.getOS() == Triple::AMDHSA) {
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerIntrinsics.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerIntrinsics.cpp
index e8c42bd05753d1..dbdc8aa336181e 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerIntrinsics.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerIntrinsics.cpp
@@ -12,19 +12,28 @@
 //===----------------------------------------------------------------------===//
 
 #include "AMDGPU.h"
+#include "AMDGPUInstrInfo.h"
 #include "AMDGPUTargetMachine.h"
 #include "GCNSubtarget.h"
+#include "Utils/AMDGPUBaseInfo.h"
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/IntrinsicsAMDGPU.h"
 #include "llvm/InitializePasses.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Transforms/Utils/BasicBlockUtils.h"
 
 #define DEBUG_TYPE "amdgpu-lower-intrinsics"
 
 using namespace llvm;
 
+static cl::opt<bool> WarnDeprecatedBufferIntrinsics(
+    "amdgpu-warn-deprecated-buffer-intrinsics", cl::Hidden, cl::init(true),
+    cl::desc("Warn about AMDGPU buffer intrinsics that take their resource as "
+             "a <4 x i32> instead of a ptr addrspace(8)"));
+
 namespace {
 
 class AMDGPULowerIntrinsicsImpl {
@@ -67,9 +76,62 @@ template <class T> static void forEachCall(Function &Intrin, T Callback) {
 
 } // anonymous namespace
 
+/// Warn about the buffer intrinsics in \p M that take their resource as a
+/// <4 x i32> rather than a ptr addrspace(8), pointing at the replacement.
+/// Diagnose once per intrinsic, ignoring types, to avoid flooding the user with
+/// warnings.
+static void warnOnDeprecatedBufferIntrinsics(Module &M) {
+  SmallSet<Intrinsic::ID, 8> Reported;
+  for (const Function &F : M) {
+    if (!F.isDeclaration() || F.use_empty())
+      continue;
+    Intrinsic::ID ID = F.getIntrinsicID();
+    if (ID == Intrinsic::not_intrinsic)
+      continue;
+
+    const AMDGPU::RsrcIntrinsic *RI = AMDGPU::lookupRsrcIntrinsic(ID);
+    // TODO: Add smart pointers for images
+    if (!RI || RI->IsImage)
+      continue;
+    FunctionType *FT = F.getFunctionType();
+    if (FT->getParamType(RI->RsrcArg)->isPointerTy())
+      continue;
+
+    if (!Reported.insert(ID).second)
+      continue;
+
+    StringRef Name = Intrinsic::getBaseName(ID);
+    std::string Repl = AMDGPU::getPtrBufferIntrinsicName(Name);
+    std::string Msg = ("'" + Name +
+                       "' is deprecated and will be removed in a future LLVM "
+                       "release; use '" +
+                       Repl +
+                       "' with a 'ptr addrspace(8)' resource created by "
+                       "'llvm.amdgcn.make.buffer.rsrc' instead")
+                          .str();
+
+    const CallBase *Call = nullptr;
+    for (const User *U : F.users()) {
+      const auto *CB = dyn_cast<CallBase>(U);
+      if (CB && CB->getFunction() && CB->getDebugLoc()) {
+        Call = CB;
+        break;
+      }
+    }
+    if (Call)
+      M.getContext().diagnose(DiagnosticInfoGenericWithLoc(
+          Msg, *Call->getFunction(), Call->getDebugLoc(), DS_Warning));
+    else
+      M.getContext().diagnose(DiagnosticInfoGeneric(Msg, DS_Warning));
+  }
+}
+
 bool AMDGPULowerIntrinsicsImpl::run() {
   bool Changed = false;
 
+  if (WarnDeprecatedBufferIntrinsics)
+    warnOnDeprecatedBufferIntrinsics(M);
+
   for (Function &F : M) {
     switch (F.getIntrinsicID()) {
     default:
diff --git a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
index c4ba4805ffc7d6..5aba7c03a2b06c 100644
--- a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
+++ b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn-loc.ll
@@ -4,6 +4,9 @@
 
 ; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null < %s 2>&1 | \
 ; RUN:   FileCheck --implicit-check-not=warning: %s
+; RUN: opt -mtriple=amdgpu9.00-amd-mesa3d -passes=amdgpu-lower-intrinsics \
+; RUN:   -disable-output < %s 2>&1 | \
+; RUN:   FileCheck --implicit-check-not=warning: %s
 
 ; Reported against the first call that has a location, not the one without, and
 ; not twice even though there are three calls.
diff --git a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
index fb123e2a72d602..e55711905ec698 100644
--- a/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
+++ b/llvm/test/CodeGen/AMDGPU/deprecated-buffer-intrinsics-warn.ll
@@ -5,6 +5,9 @@
 
 ; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null < %s 2>&1 | \
 ; RUN:   FileCheck --implicit-check-not=warning: %s
+; RUN: opt -mtriple=amdgpu9.00-amd-mesa3d -passes=amdgpu-lower-intrinsics \
+; RUN:   -disable-output < %s 2>&1 | \
+; RUN:   FileCheck --implicit-check-not=warning: %s
 ; RUN: llc -mtriple=amdgpu9.00-amd-mesa3d -filetype=null \
 ; RUN:   -amdgpu-warn-deprecated-buffer-intrinsics=0 < %s 2>&1 | \
 ; RUN:   FileCheck -check-prefix=OFF --allow-empty --implicit-check-not=warning: %s
diff --git a/llvm/unittests/Target/AMDGPU/BufferIntrinsicDeprecationTest.cpp b/llvm/unittests/Target/AMDGPU/BufferIntrinsicDeprecationTest.cpp
index 7d3cf7d912b2bc..c6394a21c1bddb 100644
--- a/llvm/unittests/Target/AMDGPU/BufferIntrinsicDeprecationTest.cpp
+++ b/llvm/unittests/Target/AMDGPU/BufferIntrinsicDeprecationTest.cpp
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 //
 // Test to ensure we don't direct users to an intrinsic that doesn't exist when
-// warning about deprecated <4 x i32> intrinsics in AMDGPUAsmPrinter
+// warning about deprecated <4 x i32> intrinsics in AMDGPULowerIntrinsics
 //
 //===----------------------------------------------------------------------===//
 



More information about the llvm-branch-commits mailing list