[llvm] d5a24ef - [AMDGPU] Replace relaxed-buffer-oob-mode feature with module flag (#160922)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 1 05:53:06 PDT 2026


Author: Piotr Sobczak
Date: 2026-06-01T14:53:00+02:00
New Revision: d5a24ef8d093708141b03ef1da809363841065b1

URL: https://github.com/llvm/llvm-project/commit/d5a24ef8d093708141b03ef1da809363841065b1
DIFF: https://github.com/llvm/llvm-project/commit/d5a24ef8d093708141b03ef1da809363841065b1.diff

LOG: [AMDGPU] Replace relaxed-buffer-oob-mode feature with module flag (#160922)

Remove AMDGPU subtarget feature toggle for relaxed buffer OOB handling
and replace it with two explicit LLVM module flags:
- amdgpu.buffer.oob.relaxed for untyped buffer instructions
- amdgpu.tbuffer.oob.relaxed for typed buffer instructions

Each flag is modeled as i32 with Max merge behavior and validated as a
tri-state value, where 0 means Any/default, 1 means Relaxed and 2 means Strict. 
The absence of the module flag implies the default mode, which is currently treated
as Strict by the backend.

Added: 
    llvm/test/Verifier/AMDGPU/module-flag-oob-mode.ll

Modified: 
    llvm/docs/AMDGPUUsage.rst
    llvm/docs/ReleaseNotes.md
    llvm/lib/IR/Verifier.cpp
    llvm/lib/Target/AMDGPU/AMDGPU.td
    llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp
    llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
    llvm/lib/Target/AMDGPU/GCNSubtarget.h
    llvm/lib/Target/AMDGPU/SIISelLowering.cpp
    llvm/test/CodeGen/AMDGPU/unaligned-buffer.ll
    llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-vectors.ll
    llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/unaligned-buffer.ll

Removed: 
    


################################################################################
diff  --git a/llvm/docs/AMDGPUUsage.rst b/llvm/docs/AMDGPUUsage.rst
index 8dd743a995a9d..61841716792ef 100644
--- a/llvm/docs/AMDGPUUsage.rst
+++ b/llvm/docs/AMDGPUUsage.rst
@@ -830,6 +830,55 @@ For example:
 
      =============== ============================ ==================================================
 
+.. _amdgpu-module-flags:
+
+Module Flags
+------------
+
+AMDGPU-specific behaviour can be controlled via LLVM module flags (see
+`Module Flags Metadata
+<https://llvm.org/docs/LangRef.html#module-flags-metadata>`_ in the language
+reference). These flags are set by frontends and are
+consumed by the AMDGPU backend during code generation.
+
+.. list-table:: AMDGPU Module Flags
+   :name: amdgpu-module-flags-table
+   :header-rows: 1
+
+   * - Flag Name
+     - Type
+     - Merge
+     - Description
+   * - ``amdgpu.buffer.oob.mode``
+     - ``i32``
+     - Max
+     - Controls out-of-bounds semantics for untyped buffer
+       instructions (``buffer_load`` / ``buffer_store``).
+
+       - ``0`` (or absent): **any**. The module does not care about OOB
+         semantics. This is an alias of **strict** that is allowed to link
+         with any other module. Code generation is identical to **strict**.
+       - ``1``: **relaxed**. The backend may merge misaligned buffer
+         accesses for performance, even if that changes OOB behaviour.
+       - ``2``: **strict**. The backend preserves per-byte OOB guarantees
+         by preventing merging of misaligned buffer accesses that could
+         straddle an OOB boundary (e.g. as required by Vulkan
+         ``robustBufferAccess2``).
+
+   * - ``amdgpu.tbuffer.oob.mode``
+     - ``i32``
+     - Max
+     - Same as above, but for typed buffer instructions (``tbuffer_load`` /
+       ``tbuffer_store``).
+
+.. note::
+
+   Frontends that require misaligned-access merging for performance should
+   set both flags to ``1`` (relaxed).  Frontends that require strict
+   per-byte OOB guarantees should set the flags to ``2`` (strict) as needed.
+   Modules that do not use buffer operations or are in
diff erent to OOB semantics
+   (e.g. device libraries) should leave the flags absent.
+
 .. _amdgpu-target-id:
 
 Target ID

diff  --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 013b5d0a52067..a222c5a146964 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -174,6 +174,12 @@ Makes programs 10x faster by doing Special New Thing.
   are now deprecated. Use `"amdgpu-waves-per-eu"` instead. The backend still
   honors the attributes; Clang emits a `-Wdeprecated-declarations` warning when
   the source attributes are used.
+* The `relaxed-buffer-oob-mode` subtarget feature has been replaced by two
+  module flags, `amdgpu.buffer.oob.mode` and `amdgpu.tbuffer.oob.mode`, which
+  control out-of-bounds semantics (see the AMDGPU User Guide). Frontends that
+  previously relied on the subtarget feature to enable misaligned buffer merging
+  must now set the corresponding module flag to `1` (relaxed). An absent flag is
+  treated as strict by the backend.
 
 ### Changes to the ARM Backend
 

diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index e58209a2499b4..b76e532a6f3e4 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2144,6 +2144,21 @@ Verifier::visitModuleFlag(const MDNode *Op,
           "SemanticInterposition metadata requires constant integer argument");
   }
 
+  if (ID->getString() == "amdgpu.buffer.oob.mode" ||
+      ID->getString() == "amdgpu.tbuffer.oob.mode") {
+    Check(MFB == Module::Max,
+          "'" + ID->getString() +
+              "' module flag must use 'max' merge behaviour");
+    ConstantInt *Value =
+        mdconst::dyn_extract_or_null<ConstantInt>(Op->getOperand(2));
+    Check(Value, "'" + ID->getString() +
+                     "' module flag must have a constant integer value");
+    if (Value) {
+      Check(Value->getZExtValue() <= 2,
+            "'" + ID->getString() + "' module flag must be 0, 1, or 2");
+    }
+  }
+
   if (ID->getString() == "CG Profile") {
     for (const MDOperand &MDO : cast<MDNode>(Op->getOperand(2))->operands())
       visitModuleFlagCGProfileEntry(MDO);

diff  --git a/llvm/lib/Target/AMDGPU/AMDGPU.td b/llvm/lib/Target/AMDGPU/AMDGPU.td
index 2cf998b218412..73321dabf4af2 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPU.td
+++ b/llvm/lib/Target/AMDGPU/AMDGPU.td
@@ -155,11 +155,6 @@ defm UnalignedDSAccess : AMDGPUSubtargetFeature<"unaligned-ds-access",
   "Hardware supports unaligned local and region loads and stores"
 >;
 
-defm RelaxedBufferOOBMode : AMDGPUSubtargetFeature<"relaxed-buffer-oob-mode",
-  "Disable strict out-of-bounds buffer guarantees. An OOB access may potentially"
-  "cause an adjacent access to be treated as if it were also OOB"
->;
-
 defm DX10ClampAndIEEEMode : AMDGPUSubtargetFeature<"dx10-clamp-and-ieee-mode",
   "Target has DX10_CLAMP and IEEE_MODE kernel descriptor bits"
 >;

diff  --git a/llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp b/llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp
index 65317016c6390..b572e47ce0f21 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp
@@ -93,6 +93,7 @@
 #include "llvm/CodeGen/TargetPassConfig.h"
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/IntrinsicsAMDGPU.h"
+#include "llvm/IR/Module.h"
 #include "llvm/IR/PassManager.h"
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/InitializePasses.h"
@@ -1245,13 +1246,38 @@ GCNTargetMachine::GCNTargetMachine(const Target &T, const Triple &TT,
                                    CodeGenOptLevel OL, bool JIT)
     : AMDGPUTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL) {}
 
+enum class OOBFlagValue {
+  Any = 0,
+  Relaxed = 1,
+  Strict = 2,
+};
+
+/// Returns the OOB mode encoded by a module flag.
+/// An absent flag defaults to Any.
+static OOBFlagValue getOOBFlagValue(const Module &M, StringRef FlagName) {
+  const auto *Flag =
+      mdconst::dyn_extract_or_null<ConstantInt>(M.getModuleFlag(FlagName));
+  if (!Flag)
+    return OOBFlagValue::Any;
+  return static_cast<OOBFlagValue>(Flag->getZExtValue());
+}
+
 const TargetSubtargetInfo *
 GCNTargetMachine::getSubtargetImpl(const Function &F) const {
   StringRef GPU = getGPUName(F);
   StringRef FS = getFeatureString(F);
 
+  const Module &M = *F.getParent();
+  OOBFlagValue BufOOB = getOOBFlagValue(M, AMDGPUOOBMode::BufferFlag);
+  OOBFlagValue TBufOOB = getOOBFlagValue(M, AMDGPUOOBMode::TBufferFlag);
+  bool BufRelaxed = BufOOB == OOBFlagValue::Relaxed;
+  bool TBufRelaxed = TBufOOB == OOBFlagValue::Relaxed;
   SmallString<128> SubtargetKey(GPU);
   SubtargetKey.append(FS);
+  if (BufRelaxed)
+    SubtargetKey.append(",buf-oob=1");
+  if (TBufRelaxed)
+    SubtargetKey.append(",tbuf-oob=1");
 
   auto &I = SubtargetMap[SubtargetKey];
   if (!I) {
@@ -1259,7 +1285,8 @@ GCNTargetMachine::getSubtargetImpl(const Function &F) const {
     // creation will depend on the TM and the code generation flags on the
     // function that reside in TargetOptions.
     resetTargetOptions(F);
-    I = std::make_unique<GCNSubtarget>(TargetTriple, GPU, FS, *this);
+    I = std::make_unique<GCNSubtarget>(TargetTriple, GPU, FS, *this, BufRelaxed,
+                                       TBufRelaxed);
   }
 
   I->setScalarizeGlobalBehavior(ScalarizeGlobal);

diff  --git a/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp b/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
index c952d5191ea62..f47cb4ee50970 100644
--- a/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
+++ b/llvm/lib/Target/AMDGPU/GCNSubtarget.cpp
@@ -181,12 +181,15 @@ void GCNSubtarget::checkSubtargetFeatures(const Function &F) const {
 }
 
 GCNSubtarget::GCNSubtarget(const Triple &TT, StringRef GPU, StringRef FS,
-                           const GCNTargetMachine &TM)
+                           const GCNTargetMachine &TM, bool BufferOOBRelaxed,
+                           bool TBufferOOBRelaxed)
     : // clang-format off
     AMDGPUGenSubtargetInfo(TT, GPU, /*TuneCPU*/ GPU, FS),
     AMDGPUSubtarget(TT),
     TargetID(*this),
     InstrItins(getInstrItineraryForCPU(GPU)),
+    BufferOOBRelaxed(BufferOOBRelaxed),
+    TBufferOOBRelaxed(TBufferOOBRelaxed),
     InstrInfo(initializeSubtargetDependencies(TT, GPU, FS)),
     TLInfo(TM, *this),
     // Frame index expansion sometimes assumes the low bit of SP is 0

diff  --git a/llvm/lib/Target/AMDGPU/GCNSubtarget.h b/llvm/lib/Target/AMDGPU/GCNSubtarget.h
index c4ea04df5264b..dd89db3419c47 100644
--- a/llvm/lib/Target/AMDGPU/GCNSubtarget.h
+++ b/llvm/lib/Target/AMDGPU/GCNSubtarget.h
@@ -31,6 +31,16 @@ namespace llvm {
 
 class GCNTargetMachine;
 
+/// Module flag names controlling out-of-bounds buffer access semantics.
+/// Each flag is an i32 with Module::Max merge behaviour and tri-state values:
+///   0 = any (absent/default - backend currently treats as strict)
+///   1 = relaxed
+///   2 = strict
+namespace AMDGPUOOBMode {
+inline constexpr StringLiteral BufferFlag("amdgpu.buffer.oob.mode");
+inline constexpr StringLiteral TBufferFlag("amdgpu.tbuffer.oob.mode");
+} // namespace AMDGPUOOBMode
+
 class GCNSubtarget final : public AMDGPUGenSubtargetInfo,
                            public AMDGPUSubtarget {
 public:
@@ -74,6 +84,8 @@ class GCNSubtarget final : public AMDGPUGenSubtargetInfo,
   bool DynamicVGPR = false;
   bool DynamicVGPRBlockSize32 = false;
   bool ScalarizeGlobal = false;
+  const bool BufferOOBRelaxed;
+  const bool TBufferOOBRelaxed;
 
   /// The maximum number of instructions that may be placed within an S_CLAUSE,
   /// which is one greater than the maximum argument to S_CLAUSE. A value of 0
@@ -100,7 +112,8 @@ class GCNSubtarget final : public AMDGPUGenSubtargetInfo,
 
 public:
   GCNSubtarget(const Triple &TT, StringRef GPU, StringRef FS,
-               const GCNTargetMachine &TM);
+               const GCNTargetMachine &TM, bool BufferOOBRelaxed = false,
+               bool TBufferOOBRelaxed = false);
   ~GCNSubtarget() override;
 
   GCNSubtarget &initializeSubtargetDependencies(const Triple &TT, StringRef GPU,
@@ -336,6 +349,9 @@ class GCNSubtarget final : public AMDGPUGenSubtargetInfo,
 
   bool isTgSplitEnabled() const { return EnableTgSplit; }
 
+  bool hasRelaxedBufferOOBMode() const { return BufferOOBRelaxed; }
+  bool hasRelaxedTBufferOOBMode() const { return TBufferOOBRelaxed; }
+
   bool isCuModeEnabled() const { return EnableCuMode; }
 
   bool isPreciseMemoryEnabled() const { return EnablePreciseMemory; }

diff  --git a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
index f03c5748455ee..86f2479490c29 100644
--- a/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIISelLowering.cpp
@@ -2257,10 +2257,11 @@ bool SITargetLowering::allowsMisalignedMemoryAccessesImpl(
            Subtarget->hasUnalignedBufferAccessEnabled();
   }
 
-  // Ensure robust out-of-bounds guarantees for buffer accesses are met if
-  // RelaxedBufferOOBMode is disabled. Normally hardware will ensure proper
+  // Ensure robust out-of-bounds guarantees for buffer accesses are met when the
+  // "amdgpu.buffer.oob.mode" module flag has not enabled relaxed untyped-buffer
+  // OOB semantics. Normally hardware will ensure proper
   // out-of-bounds behavior, but in the edge case where an access starts
-  // out-of-bounds and then enter in-bounds, the entire access would be treated
+  // out-of-bounds and then enters in-bounds, the entire access would be treated
   // as out-of-bounds. Prevent misaligned memory accesses by requiring the
   // natural alignment of buffer accesses.
   if (AddrSpace == AMDGPUAS::BUFFER_FAT_POINTER ||

diff  --git a/llvm/test/CodeGen/AMDGPU/unaligned-buffer.ll b/llvm/test/CodeGen/AMDGPU/unaligned-buffer.ll
index b518d5e738031..36415c80660d6 100644
--- a/llvm/test/CodeGen/AMDGPU/unaligned-buffer.ll
+++ b/llvm/test/CodeGen/AMDGPU/unaligned-buffer.ll
@@ -2,7 +2,7 @@
 ; RUN: llc -global-isel=0 -mtriple=amdgcn -mcpu=gfx1100 < %s | FileCheck -check-prefix=SDAG %s
 ; RUN: llc -global-isel=1 -new-reg-bank-select -mtriple=amdgcn -mcpu=gfx1100 < %s | FileCheck -check-prefix=GISEL %s
 
-; Check that in strict OOB mode for buffers (relaxed-buffer-oob-mode attribute not set) the underaligned loads and stores get split.
+; Check that in strict OOB mode for buffers ("amdgpu.buffer.oob.mode" module flag not set) the underaligned loads and stores get split.
 ; FIXME: The loads/stores do not get split (extend amdgpu-lower-buffer-fat-pointers?).
 
 define amdgpu_ps void @split_underaligned_load(ptr addrspace(7) inreg %p, ptr addrspace(7) inreg %p2) #0 {
@@ -52,7 +52,7 @@ entry:
   ret void
 }
 
-; Check that in strict OOB mode for buffers (relaxed-buffer-oob-mode attribute not set) the naturally aligned loads and stores do not get split.
+; Check that in strict OOB mode for buffers ("amdgpu.buffer.oob.mode" module flag not set) the naturally aligned loads and stores do not get split.
 
 define amdgpu_ps void @do_not_split_aligned_load(ptr addrspace(7) inreg %p, ptr addrspace(7) inreg %p2) #0 {
 ; CHECK-LABEL: do_not_split_aligned_load:

diff  --git a/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-vectors.ll b/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-vectors.ll
index d6b51039d5b44..f2099cee2ed69 100644
--- a/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-vectors.ll
+++ b/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/merge-vectors.ll
@@ -1,6 +1,10 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
-; RUN: opt -mtriple=amdgcn-amd-amdhsa -passes=load-store-vectorizer -mattr=+relaxed-buffer-oob-mode -S -o - %s | FileCheck --check-prefixes=CHECK,CHECK-OOB-RELAXED %s
-; RUN: opt -mtriple=amdgcn-amd-amdhsa -passes=load-store-vectorizer -S -o - %s | FileCheck --check-prefixes=CHECK,CHECK-OOB-STRICT %s
+; RUN: opt -mtriple=amdgcn-amd-amdhsa -passes=load-store-vectorizer -S -o - %s \
+; RUN:   | FileCheck --check-prefixes=CHECK,CHECK-OOB-STRICT %s
+; RUN: cp %s %t.relaxed.ll
+; RUN: printf '\n!llvm.module.flags = !{!0}\n!0 = !{i32 7, !"amdgpu.buffer.oob.mode", i32 1}\n' >> %t.relaxed.ll
+; RUN: opt -mtriple=amdgcn-amd-amdhsa -passes=load-store-vectorizer -S -o - %t.relaxed.ll \
+; RUN:   | FileCheck --check-prefixes=CHECK,CHECK-OOB-RELAXED %s
 
 define amdgpu_kernel void @merge_v2i32_v2i32(ptr addrspace(1) nocapture %a, ptr addrspace(1) nocapture readonly %b) #0 {
 ; CHECK-LABEL: define amdgpu_kernel void @merge_v2i32_v2i32(

diff  --git a/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/unaligned-buffer.ll b/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/unaligned-buffer.ll
index d590a4a403fb7..74a247b702caf 100644
--- a/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/unaligned-buffer.ll
+++ b/llvm/test/Transforms/LoadStoreVectorizer/AMDGPU/unaligned-buffer.ll
@@ -1,8 +1,12 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt -mtriple=amdgcn--amdpal -passes=load-store-vectorizer -S -o - %s | FileCheck --check-prefix=OOB-STRICT %s
-; RUN: opt -mtriple=amdgcn--amdpal -passes=load-store-vectorizer -mattr=+relaxed-buffer-oob-mode -S -o - %s | FileCheck --check-prefixes=OOB-RELAXED %s
+; RUN: cp %s %t.relaxed.ll
+; RUN: printf '\n!llvm.module.flags = !{!0}\n!0 = !{i32 7, !"amdgpu.buffer.oob.mode", i32 1}\n' >> %t.relaxed.ll
+; RUN: opt -mtriple=amdgcn--amdpal -passes=load-store-vectorizer -S -o - %t.relaxed.ll \
+; RUN:   | FileCheck --check-prefixes=OOB-RELAXED %s
 
-; The test checks that relaxed-buffer-oob-mode allows merging loads even if the target load is not naturally aligned.
+; The test checks that the "amdgpu.buffer.oob.mode" module flag
+; allows merging loads even if the target load is not naturally aligned.
 
 define amdgpu_kernel void @merge_align_4(ptr addrspace(7) captures(none) %p) #0 {
 ;
@@ -20,7 +24,7 @@ define amdgpu_kernel void @merge_align_4(ptr addrspace(7) captures(none) %p) #0
 ; OOB-STRICT-NEXT:    ret void
 ;
 ; OOB-RELAXED-LABEL: define amdgpu_kernel void @merge_align_4(
-; OOB-RELAXED-SAME: ptr addrspace(7) captures(none) [[P:%.*]]) #[[ATTR0:[0-9]+]] {
+; OOB-RELAXED-SAME: ptr addrspace(7) captures(none) [[P:%.*]]) {
 ; OOB-RELAXED-NEXT:  [[ENTRY:.*:]]
 ; OOB-RELAXED-NEXT:    [[GEP_M8:%.*]] = getelementptr i8, ptr addrspace(7) [[P]], i32 -8
 ; OOB-RELAXED-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr addrspace(7) [[GEP_M8]], align 4
@@ -42,7 +46,8 @@ entry:
   ret void
 }
 
-; The test checks that strict OOB mode (relaxed-buffer-oob-mode not set) allows merging loads if the target load is naturally aligned.
+; The test checks that strict OOB mode ("amdgpu.buffer.oob.mode" absent or 0)
+; allows merging loads when the target load is naturally aligned.
 
 define amdgpu_kernel void @merge_align_16(ptr addrspace(7) captures(none) %p) #0 {
 ; OOB-STRICT-LABEL: define amdgpu_kernel void @merge_align_16(
@@ -57,7 +62,7 @@ define amdgpu_kernel void @merge_align_16(ptr addrspace(7) captures(none) %p) #0
 ; OOB-STRICT-NEXT:    ret void
 ;
 ; OOB-RELAXED-LABEL: define amdgpu_kernel void @merge_align_16(
-; OOB-RELAXED-SAME: ptr addrspace(7) captures(none) [[P:%.*]]) #[[ATTR0]] {
+; OOB-RELAXED-SAME: ptr addrspace(7) captures(none) [[P:%.*]]) {
 ; OOB-RELAXED-NEXT:  [[ENTRY:.*:]]
 ; OOB-RELAXED-NEXT:    [[GEP_M8:%.*]] = getelementptr i8, ptr addrspace(7) [[P]], i32 -8
 ; OOB-RELAXED-NEXT:    [[TMP0:%.*]] = load <4 x i32>, ptr addrspace(7) [[GEP_M8]], align 16

diff  --git a/llvm/test/Verifier/AMDGPU/module-flag-oob-mode.ll b/llvm/test/Verifier/AMDGPU/module-flag-oob-mode.ll
new file mode 100644
index 0000000000000..f471d8e2f9bfe
--- /dev/null
+++ b/llvm/test/Verifier/AMDGPU/module-flag-oob-mode.ll
@@ -0,0 +1,98 @@
+; Tests for IR verifier enforcement of the "amdgpu.buffer.oob.mode" and
+; "amdgpu.tbuffer.oob.mode" module flags.  Each flag must use Module::Max
+; (i32 7) merge behaviour, carry a constant integer value, and be 0, 1, or 2.
+
+; RUN: split-file %s %t
+
+; --- Negative: wrong merge behaviour (Override=4 instead of Max=7) ---
+; RUN: not llvm-as %t/wrong-behavior-buffer.ll --disable-output 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=WRONG-BUF
+; RUN: not llvm-as %t/wrong-behavior-tbuffer.ll --disable-output 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=WRONG-TBUF
+
+; --- Negative: non-integer value ---
+; RUN: not llvm-as %t/non-integer-buffer.ll --disable-output 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=NON-INT-BUF
+; RUN: not llvm-as %t/non-integer-tbuffer.ll --disable-output 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=NON-INT-TBUF
+
+; --- Negative: value out of range (3 is not 0, 1, or 2) ---
+; RUN: not llvm-as %t/out-of-range-buffer.ll --disable-output 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=RANGE-BUF
+; RUN: not llvm-as %t/out-of-range-tbuffer.ll --disable-output 2>&1 \
+; RUN:   | FileCheck %s --check-prefix=RANGE-TBUF
+
+; --- Positive: absent flags (no error expected) ---
+; RUN: llvm-as %t/absent.ll --disable-output 2>&1 | count 0
+
+; --- Positive: valid any value 0 ---
+; RUN: llvm-as %t/valid-both-0.ll --disable-output 2>&1 | count 0
+
+; --- Positive: valid relaxed value 1 for buffer ---
+; RUN: llvm-as %t/valid-buffer-1.ll --disable-output 2>&1 | count 0
+
+; --- Positive: valid relaxed value 1 for tbuffer ---
+; RUN: llvm-as %t/valid-tbuffer-1.ll --disable-output 2>&1 | count 0
+
+; --- Positive: valid strict value 2 ---
+; RUN: llvm-as %t/valid-both-2.ll --disable-output 2>&1 | count 0
+
+; WRONG-BUF:    'amdgpu.buffer.oob.mode' module flag must use 'max' merge behaviour
+; WRONG-TBUF:   'amdgpu.tbuffer.oob.mode' module flag must use 'max' merge behaviour
+; NON-INT-BUF:  invalid value for 'max' module flag (expected constant integer)
+; NON-INT-TBUF: invalid value for 'max' module flag (expected constant integer)
+; RANGE-BUF:    'amdgpu.buffer.oob.mode' module flag must be 0, 1, or 2
+; RANGE-TBUF:   'amdgpu.tbuffer.oob.mode' module flag must be 0, 1, or 2
+
+;--- wrong-behavior-buffer.ll
+; Override (i32 4) is not Max (i32 7).
+!0 = !{i32 4, !"amdgpu.buffer.oob.mode", i32 1}
+!llvm.module.flags = !{!0}
+
+;--- wrong-behavior-tbuffer.ll
+!0 = !{i32 4, !"amdgpu.tbuffer.oob.mode", i32 1}
+!llvm.module.flags = !{!0}
+
+;--- non-integer-buffer.ll
+; Max behaviour but float value instead of integer.
+!0 = !{i32 7, !"amdgpu.buffer.oob.mode", float 1.0}
+!llvm.module.flags = !{!0}
+
+;--- non-integer-tbuffer.ll
+!0 = !{i32 7, !"amdgpu.tbuffer.oob.mode", float 1.0}
+!llvm.module.flags = !{!0}
+
+;--- out-of-range-buffer.ll
+; Value 3 is out of range (must be 0, 1, or 2).
+!0 = !{i32 7, !"amdgpu.buffer.oob.mode", i32 3}
+!llvm.module.flags = !{!0}
+
+;--- out-of-range-tbuffer.ll
+!0 = !{i32 7, !"amdgpu.tbuffer.oob.mode", i32 3}
+!llvm.module.flags = !{!0}
+
+;--- absent.ll
+; No OOB flags at all - should be accepted.
+define void @f() { ret void }
+
+;--- valid-both-0.ll
+; Both flags explicitly any.
+!0 = !{i32 7, !"amdgpu.buffer.oob.mode", i32 0}
+!1 = !{i32 7, !"amdgpu.tbuffer.oob.mode", i32 0}
+!llvm.module.flags = !{!0, !1}
+
+;--- valid-buffer-1.ll
+; Relaxed untyped buffer OOB.
+!0 = !{i32 7, !"amdgpu.buffer.oob.mode", i32 1}
+!llvm.module.flags = !{!0}
+
+;--- valid-tbuffer-1.ll
+; Relaxed typed buffer OOB.
+!0 = !{i32 7, !"amdgpu.tbuffer.oob.mode", i32 1}
+!llvm.module.flags = !{!0}
+
+;--- valid-both-2.ll
+; Both flags strict.
+!0 = !{i32 7, !"amdgpu.buffer.oob.mode", i32 2}
+!1 = !{i32 7, !"amdgpu.tbuffer.oob.mode", i32 2}
+!llvm.module.flags = !{!0, !1}


        


More information about the llvm-commits mailing list