[llvm-branch-commits] [mlir] [mlir][AMDGPU][NFC] Pre-commit tests for incorrect version checks (PR #220104)

Krzysztof Drewniak via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Sep 10 15:53:27 PDT 2026


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

>From 88eae6abe289d845673aefdd6c6f8f1d6de6b693 Mon Sep 17 00:00:00 2001
From: Krzysztof Drewniak <Krzysztof.Drewniak at amd.com>
Date: Thu, 27 Aug 2026 19:48:44 +0000
Subject: [PATCH] [mlir][AMDGPU][NFC] Pre-commit tests for incorrect version
 checks

There'll be a refactoring from `amdgpu::Chipset` to
`ROCDL::TargetInfo`, thus also moving from chip version checks to
features checks. This commit adds tests for incorrect lowerings that
were allowed by the current code.

- gfx90c is >= gfx90a but stil needs atomic emulation (it doesn't
  have buffer fmax and so on).
- gfx90c is also >= gfx90a but has no barrier back-off, so it needs
  the inline asm workaround around `s_barrier` that it isn't getting
- gfx908 doesn't have a packed fp16 atomic add but we thought it did
- gfx950 is mistakenly allowing xf32 MFMAs
- gfx1200 is allowing permlane_swap instructions that it doesn't have
- gfx11.7 should be allowing OCP FP8 conversions but isn't on the list

This also cleans up some redundant tests with a --check-prefixes

AI disclosure: Claude found these and wrote the tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply at anthropic.com>
---
 .../8-bit-floats-ocp-gfx1170.mlir             | 28 ++++++++
 .../AMDGPUToROCDL/lds-barrier-gfx90c.mlir     | 15 ++++
 .../Conversion/AMDGPUToROCDL/mfma-gfx950.mlir | 14 ++++
 .../Conversion/AMDGPUToROCDL/permlane.mlir    |  4 ++
 .../AMDGPU/amdgpu-emulate-atomics.mlir        | 69 ++++++++++---------
 5 files changed, 97 insertions(+), 33 deletions(-)
 create mode 100644 mlir/test/Conversion/AMDGPUToROCDL/8-bit-floats-ocp-gfx1170.mlir
 create mode 100644 mlir/test/Conversion/AMDGPUToROCDL/lds-barrier-gfx90c.mlir

diff --git a/mlir/test/Conversion/AMDGPUToROCDL/8-bit-floats-ocp-gfx1170.mlir b/mlir/test/Conversion/AMDGPUToROCDL/8-bit-floats-ocp-gfx1170.mlir
new file mode 100644
index 0000000000000..27692d540e5d4
--- /dev/null
+++ b/mlir/test/Conversion/AMDGPUToROCDL/8-bit-floats-ocp-gfx1170.mlir
@@ -0,0 +1,28 @@
+// RUN: mlir-opt %s --convert-amdgpu-to-rocdl=chipset=gfx1170 --split-input-file --verify-diagnostics
+
+// gfx11.7 has FeatureOCPFP8ConversionInsts, so these conversions are available
+// on it. They are rejected today because the predicate deciding whether a
+// target uses the OCP fp8 formats is written as the version range "gfx9.5+ or
+// gfx12+", which skips over gfx11.7 entirely.
+
+func.func @ext_packed_fp8(%v: vector<4xf8E4M3FN>) -> f32 {
+  // expected-error at below {{failed to legalize operation 'amdgpu.ext_packed_fp8'}}
+  %ret = amdgpu.ext_packed_fp8 %v[0] : vector<4xf8E4M3FN> to f32
+  func.return %ret : f32
+}
+
+// -----
+
+func.func @ext_packed_bf8(%v: vector<4xf8E5M2>) -> f32 {
+  // expected-error at below {{failed to legalize operation 'amdgpu.ext_packed_fp8'}}
+  %ret = amdgpu.ext_packed_fp8 %v[0] : vector<4xf8E5M2> to f32
+  func.return %ret : f32
+}
+
+// -----
+
+func.func @packed_trunc_2xfp8(%v: f32) -> vector<4xf8E4M3FN> {
+  // expected-error at below {{failed to legalize operation 'amdgpu.packed_trunc_2xfp8'}}
+  %ret = amdgpu.packed_trunc_2xfp8 %v, undef into undef[word 0] : f32 to vector<4xf8E4M3FN>
+  func.return %ret : vector<4xf8E4M3FN>
+}
diff --git a/mlir/test/Conversion/AMDGPUToROCDL/lds-barrier-gfx90c.mlir b/mlir/test/Conversion/AMDGPUToROCDL/lds-barrier-gfx90c.mlir
new file mode 100644
index 0000000000000..d1c9919eb0d59
--- /dev/null
+++ b/mlir/test/Conversion/AMDGPUToROCDL/lds-barrier-gfx90c.mlir
@@ -0,0 +1,15 @@
+// RUN: mlir-opt %s -convert-amdgpu-to-rocdl=chipset=gfx90c | FileCheck %s
+
+// gfx90c sorts after gfx90a, so the version comparison guarding the inline asm
+// workaround treats it as having the hardware barrier back-off. It does not:
+// gfx90c is a Renoir-class APU and lacks FeatureBackOffBarrier, so a bare
+// s_barrier lets waits on global memory be introduced around the barrier.
+
+// CHECK-LABEL: func @lds_barrier
+func.func @lds_barrier() {
+  // CHECK: llvm.fence syncscope("workgroup") release
+  // CHECK-NEXT: rocdl.s.barrier
+  // CHECK-NEXT: llvm.fence syncscope("workgroup") acquire
+  amdgpu.lds_barrier
+  func.return
+}
diff --git a/mlir/test/Conversion/AMDGPUToROCDL/mfma-gfx950.mlir b/mlir/test/Conversion/AMDGPUToROCDL/mfma-gfx950.mlir
index ace99fd89625a..d124c33f19144 100644
--- a/mlir/test/Conversion/AMDGPUToROCDL/mfma-gfx950.mlir
+++ b/mlir/test/Conversion/AMDGPUToROCDL/mfma-gfx950.mlir
@@ -96,3 +96,17 @@ func.func @scaled_mfma_to_rocdl(%arg0 : vector<16xf32>,
 
   func.return
 }
+
+// gfx950 does not have the xf32 MFMAs -- FeatureXF32Insts is set on gfx942
+// only -- but it compares greater than gfx942 by ISA version, so the
+// reduced-precision f32 MFMAs are currently selected for it.
+// CHECK-LABEL: func @mfma_reduce_precision_to_rocdl
+func.func @mfma_reduce_precision_to_rocdl(%arg0 : vector<2xf32>,
+                                          %arg1 : vector<16xf32>,
+                                          %arg2 : vector<4xf32>) {
+  // CHECK: rocdl.mfma.f32.32x32x4.xf32
+  amdgpu.mfma 32x32x4 %arg0 * %arg0 + %arg1 reducePrecision : vector<2xf32>, vector<2xf32>, vector<16xf32>
+  // CHECK: rocdl.mfma.f32.16x16x8.xf32
+  amdgpu.mfma 16x16x8 %arg0 * %arg0 + %arg2 reducePrecision : vector<2xf32>, vector<2xf32>, vector<4xf32>
+  func.return
+}
diff --git a/mlir/test/Conversion/AMDGPUToROCDL/permlane.mlir b/mlir/test/Conversion/AMDGPUToROCDL/permlane.mlir
index 660e34a62cc5e..a8643604abb5f 100755
--- a/mlir/test/Conversion/AMDGPUToROCDL/permlane.mlir
+++ b/mlir/test/Conversion/AMDGPUToROCDL/permlane.mlir
@@ -1,4 +1,8 @@
 // RUN: mlir-opt --convert-amdgpu-to-rocdl=chipset=gfx950 --canonicalize %s | FileCheck %s
+// The permlane swaps come from FeaturePermlane16Swap/FeaturePermlane32Swap,
+// which gfx1200 does not have -- but it compares greater than gfx950 by ISA
+// version, so `chipset < kGfx950` lets it through and it lowers identically.
+// RUN: mlir-opt --convert-amdgpu-to-rocdl=chipset=gfx1200 --canonicalize %s | FileCheck %s
 
 // CHECK-LABEL: func @test_permlane16_i32
 // CHECK-SAME: (%[[ARG0:.*]]: i32)
diff --git a/mlir/test/Dialect/AMDGPU/amdgpu-emulate-atomics.mlir b/mlir/test/Dialect/AMDGPU/amdgpu-emulate-atomics.mlir
index 08896158ac7f7..fa883bb96c1a0 100644
--- a/mlir/test/Dialect/AMDGPU/amdgpu-emulate-atomics.mlir
+++ b/mlir/test/Dialect/AMDGPU/amdgpu-emulate-atomics.mlir
@@ -1,9 +1,11 @@
-// RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx90a %s | FileCheck %s --check-prefixes=CHECK,GFX90A
+// RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx908 %s | FileCheck %s --check-prefixes=CHECK,GFX9CAS,GFX908
+// RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx90a %s | FileCheck %s --check-prefixes=CHECK,GFX9CAS,GFX90A
+// RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx90c %s | FileCheck %s --check-prefixes=CHECK,GFX9CAS,GFX90C
 // RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx1030 %s | FileCheck %s --check-prefixes=CHECK,GFX10
 // RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx1100 %s | FileCheck %s --check-prefixes=CHECK,GFX11
 // RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx1200 %s | FileCheck %s --check-prefixes=CHECK,GFX12
-// RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx942 %s | FileCheck %s --check-prefixes=CHECK,GFX942
-// RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx950 %s | FileCheck %s --check-prefixes=CHECK,GFX950
+// RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx942 %s | FileCheck %s --check-prefixes=CHECK,GFX9CAS,GFX942
+// RUN: mlir-opt -split-input-file -amdgpu-emulate-atomics=chipset=gfx950 %s | FileCheck %s --check-prefixes=CHECK,GFX9CAS,GFX950
 
 // -----
 
@@ -14,36 +16,16 @@ func.func @atomic_fmax(%val: f32, %buffer: memref<?xf32>, %idx: i32) -> f32 {
 // GFX10: amdgpu.raw_buffer_atomic_fmax boundsCheck(true) {foo} [[val]] -> [[buffer]][[[idx]]]
 // GFX11: amdgpu.raw_buffer_atomic_fmax boundsCheck(true) {foo} [[val]] -> [[buffer]][[[idx]]]
 // GFX12: amdgpu.raw_buffer_atomic_fmax boundsCheck(true) {foo} [[val]] -> [[buffer]][[[idx]]]
-// GFX90A: [[ld:%.+]] = amdgpu.raw_buffer_load boundsCheck(true) {foo} [[buffer]][[[idx]]]
-// GFX90A:  cf.br [[loop:\^.+]]([[ld]] : f32)
-// GFX90A:  [[loop]]([[arg:%.+]]: f32):
-// GFX90A:  [[operated:%.+]] = arith.maximumf [[val]], [[arg]]
-// GFX90A: [[atomicRes:%.+]] = amdgpu.raw_buffer_atomic_cmpswap boundsCheck(true) {foo} [[operated]], [[arg]] -> [[buffer]][[[idx]]]
-// GFX90A:  [[argCast:%.+]] = arith.bitcast [[arg]] : f32 to i32
-// GFX90A:  [[resCast:%.+]] = arith.bitcast [[atomicRes]] : f32 to i32
-// GFX90A:  [[test:%.+]] = arith.cmpi eq, [[resCast]], [[argCast]]
-// GFX90A:  cf.cond_br [[test]], [[post:\^.+]]([[arg]] : f32), [[loop]]([[atomicRes]] : f32)
-// GFX90A:  [[post]]([[old:%.+]]: f32):
-// GFX942: [[ld:%.+]] = amdgpu.raw_buffer_load boundsCheck(true) {foo} [[buffer]][[[idx]]]
-// GFX942:  cf.br [[loop:\^.+]]([[ld]] : f32)
-// GFX942:  [[loop]]([[arg:%.+]]: f32):
-// GFX942:  [[operated:%.+]] = arith.maximumf [[val]], [[arg]]
-// GFX942: [[atomicRes:%.+]] = amdgpu.raw_buffer_atomic_cmpswap boundsCheck(true) {foo} [[operated]], [[arg]] -> [[buffer]][[[idx]]]
-// GFX942:  [[argCast:%.+]] = arith.bitcast [[arg]] : f32 to i32
-// GFX942:  [[resCast:%.+]] = arith.bitcast [[atomicRes]] : f32 to i32
-// GFX942:  [[test:%.+]] = arith.cmpi eq, [[resCast]], [[argCast]]
-// GFX942:  cf.cond_br [[test]], [[post:\^.+]]([[arg]] : f32), [[loop]]([[atomicRes]] : f32)
-// GFX942:  [[post]]([[old:%.+]]: f32):
-// GFX950: [[ld:%.+]] = amdgpu.raw_buffer_load boundsCheck(true) {foo} [[buffer]][[[idx]]]
-// GFX950:  cf.br [[loop:\^.+]]([[ld]] : f32)
-// GFX950:  [[loop]]([[arg:%.+]]: f32):
-// GFX950:  [[operated:%.+]] = arith.maximumf [[val]], [[arg]]
-// GFX950: [[atomicRes:%.+]] = amdgpu.raw_buffer_atomic_cmpswap boundsCheck(true) {foo} [[operated]], [[arg]] -> [[buffer]][[[idx]]]
-// GFX950:  [[argCast:%.+]] = arith.bitcast [[arg]] : f32 to i32
-// GFX950:  [[resCast:%.+]] = arith.bitcast [[atomicRes]] : f32 to i32
-// GFX950:  [[test:%.+]] = arith.cmpi eq, [[resCast]], [[argCast]]
-// GFX950:  cf.cond_br [[test]], [[post:\^.+]]([[arg]] : f32), [[loop]]([[atomicRes]] : f32)
-// GFX950:  [[post]]([[old:%.+]]: f32):
+// GFX9CAS: [[ld:%.+]] = amdgpu.raw_buffer_load boundsCheck(true) {foo} [[buffer]][[[idx]]]
+// GFX9CAS:  cf.br [[loop:\^.+]]([[ld]] : f32)
+// GFX9CAS:  [[loop]]([[arg:%.+]]: f32):
+// GFX9CAS:  [[operated:%.+]] = arith.maximumf [[val]], [[arg]]
+// GFX9CAS: [[atomicRes:%.+]] = amdgpu.raw_buffer_atomic_cmpswap boundsCheck(true) {foo} [[operated]], [[arg]] -> [[buffer]][[[idx]]]
+// GFX9CAS:  [[argCast:%.+]] = arith.bitcast [[arg]] : f32 to i32
+// GFX9CAS:  [[resCast:%.+]] = arith.bitcast [[atomicRes]] : f32 to i32
+// GFX9CAS:  [[test:%.+]] = arith.cmpi eq, [[resCast]], [[argCast]]
+// GFX9CAS:  cf.cond_br [[test]], [[post:\^.+]]([[arg]] : f32), [[loop]]([[atomicRes]] : f32)
+// GFX9CAS:  [[post]]([[old:%.+]]: f32):
 // CHECK-NEXT: gpu.printf "End\0A"
 // CHECK-NEXT: return
   gpu.printf "Begin\n"
@@ -64,6 +46,19 @@ func.func @atomic_fmax_f64(%val: f64, %buffer: memref<?xf64>, %idx: i32) {
 // GFX12: amdgpu.raw_buffer_atomic_fmax boundsCheck(true) [[val]] -> [[buffer]][[[idx]]]
 // GFX942: amdgpu.raw_buffer_atomic_fmax boundsCheck(true) [[val]] -> [[buffer]][[[idx]]]
 // GFX950: amdgpu.raw_buffer_atomic_fmax boundsCheck(true) [[val]] -> [[buffer]][[[idx]]]
+// gfx908 has no f64 buffer fmin/fmax, so it is emulated.
+// GFX908:  [[ld:%.+]] = amdgpu.raw_buffer_load boundsCheck(true) [[buffer]][[[idx]]]
+// GFX908:  cf.br [[loop:\^.+]]([[ld]] : f64)
+// GFX908:  [[loop]]([[arg:%.+]]: f64):
+// GFX908:  [[operated:%.+]] = arith.maximumf [[val]], [[arg]]
+// GFX908: [[atomicRes:%.+]] = amdgpu.raw_buffer_atomic_cmpswap boundsCheck(true) [[operated]], [[arg]] -> [[buffer]][[[idx]]]
+// GFX908:  [[argCast:%.+]] = arith.bitcast [[arg]] : f64 to i64
+// GFX908:  [[resCast:%.+]] = arith.bitcast [[atomicRes]] : f64 to i64
+// GFX908:  [[test:%.+]] = arith.cmpi eq, [[resCast]], [[argCast]]
+// GFX908:  cf.cond_br [[test]], [[post:\^.+]]([[arg]] : f64), [[loop]]([[atomicRes]] : f64)
+// GFX908:  [[post]]([[old:%.+]]: f64):
+// gfx90c has none either, but sorts after gfx90a by ISA version.
+// GFX90C: amdgpu.raw_buffer_atomic_fmax boundsCheck(true) [[val]] -> [[buffer]][[[idx]]]
 // CHECK-NEXT: gpu.printf "End\0A"
   gpu.printf "Begin\n"
   %old = amdgpu.raw_buffer_atomic_fmax boundsCheck(true) %val -> %buffer[%idx] : f64 -> memref<?xf64>, i32
@@ -82,6 +77,8 @@ func.func @atomic_fadd(%val: f32, %buffer: memref<?xf32>, %idx: i32) {
 // GFX12: amdgpu.raw_buffer_atomic_fadd
 // GFX942: amdgpu.raw_buffer_atomic_fadd
 // GFX950: amdgpu.raw_buffer_atomic_fadd
+// GFX908: amdgpu.raw_buffer_atomic_fadd
+// GFX90C: amdgpu.raw_buffer_atomic_fadd
   %old = amdgpu.raw_buffer_atomic_fadd boundsCheck(true) %val -> %buffer[%idx] : f32 -> memref<?xf32>, i32
   func.return
 }
@@ -103,6 +100,8 @@ func.func @atomic_fadd_v2f16(%val: vector<2xf16>, %buffer: memref<?xf16>, %idx:
 // GFX942: amdgpu.raw_buffer_atomic_fadd
 // GFX12:  amdgpu.raw_buffer_atomic_fadd
 // GFX950:  amdgpu.raw_buffer_atomic_fadd
+// GFX908: amdgpu.raw_buffer_atomic_fadd
+// GFX90C: amdgpu.raw_buffer_atomic_fadd
   %old = amdgpu.raw_buffer_atomic_fadd boundsCheck(true) %val -> %buffer[%idx] : vector<2xf16> -> memref<?xf16>, i32
   func.return
 }
@@ -119,6 +118,10 @@ func.func @atomic_fadd_v2bf16(%val: vector<2xbf16>, %buffer: memref<?xbf16>, %id
 // GFX942: amdgpu.raw_buffer_atomic_cmpswap
 // GFX12:  amdgpu.raw_buffer_atomic_fadd
 // GFX950:  amdgpu.raw_buffer_atomic_fadd
+// GFX908: amdgpu.raw_buffer_load
+// GFX908: amdgpu.raw_buffer_atomic_cmpswap
+// GFX90C: amdgpu.raw_buffer_load
+// GFX90C: amdgpu.raw_buffer_atomic_cmpswap
   %old = amdgpu.raw_buffer_atomic_fadd boundsCheck(true) %val -> %buffer[%idx] : vector<2xbf16> -> memref<?xbf16>, i32
   func.return
 }



More information about the llvm-branch-commits mailing list