[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