[Mlir-commits] [mlir] [mlir][SPIR-V] Add EXT.AtomicFMin and EXT.AtomicFMax ops (PR #213596)

Arseniy Obolenskiy llvmlistbot at llvm.org
Sun Aug 2 22:36:35 PDT 2026


https://github.com/aobolensk created https://github.com/llvm/llvm-project/pull/213596

None

>From 100a2873cf72388130a81981cf854d1fc068437d Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 3 Aug 2026 07:34:56 +0200
Subject: [PATCH] [mlir][SPIR-V] Add EXT.AtomicFMin and EXT.AtomicFMax ops

---
 .../mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td   | 124 ++++++++++++++++++
 .../mlir/Dialect/SPIRV/IR/SPIRVBase.td        |   3 +
 mlir/lib/Dialect/SPIRV/IR/AtomicOps.cpp       |  16 +++
 mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir    |  88 +++++++++++++
 mlir/test/Target/SPIRV/atomic-ops.mlir        |   8 +-
 5 files changed, 237 insertions(+), 2 deletions(-)

diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td
index d756a923b744f..56121376a65eb 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td
@@ -438,6 +438,130 @@ def SPIRV_EXTAtomicFAddOp : SPIRV_ExtVendorOp<"AtomicFAdd", [
 
 // -----
 
+def SPIRV_EXTAtomicFMinOp : SPIRV_ExtVendorOp<"AtomicFMin", [
+  PointeeTypeMatchTrait<"pointer", "result">,
+  PointeeTypeMatchTrait<"pointer", "value">,
+]> {
+  let summary = "TBD";
+
+  let description = [{
+
+    <!-- End of AutoGen section -->
+
+    Perform the following steps atomically with respect to any other atomic
+    accesses within Scope to the same location:
+
+    1) load through Pointer to get an Original Value,
+
+    2) get a New Value by finding the smallest floating-point number of
+    Original Value and Value, and
+
+    3) store the New Value back through Pointer.
+
+    The instruction's result is the Original Value.
+
+    Result Type must be a floating-point type scalar.
+
+    The type of Value must be the same as Result Type. The type of the value
+    pointed to by Pointer must be the same as Result Type.
+
+    Memory must be a valid memory Scope.
+
+    #### Example:
+
+    ```mlir
+    %0 = spirv.EXT.AtomicFMin <Device> <None> %pointer, %value :
+                           !spirv.ptr<f32, StorageBuffer>
+    ```
+  }];
+
+  let availability = [
+    MinVersion<SPIRV_V_1_0>,
+    MaxVersion<SPIRV_V_1_6>,
+    Extension<[SPV_EXT_shader_atomic_float_min_max]>,
+    Capability<[SPIRV_C_AtomicFloat16MinMaxEXT, SPIRV_C_AtomicFloat32MinMaxEXT, SPIRV_C_AtomicFloat64MinMaxEXT]>
+  ];
+
+  let arguments = (ins
+    SPIRV_AnyPtr:$pointer,
+    SPIRV_ScopeAttr:$memory_scope,
+    SPIRV_MemorySemanticsAttr:$semantics,
+    SPIRV_Float:$value
+  );
+
+  let results = (outs
+    SPIRV_Float:$result
+  );
+
+  let assemblyFormat = [{
+    $memory_scope $semantics operands attr-dict `:` type($pointer)
+  }];
+}
+
+// -----
+
+def SPIRV_EXTAtomicFMaxOp : SPIRV_ExtVendorOp<"AtomicFMax", [
+  PointeeTypeMatchTrait<"pointer", "result">,
+  PointeeTypeMatchTrait<"pointer", "value">,
+]> {
+  let summary = "TBD";
+
+  let description = [{
+
+    <!-- End of AutoGen section -->
+
+    Perform the following steps atomically with respect to any other atomic
+    accesses within Scope to the same location:
+
+    1) load through Pointer to get an Original Value,
+
+    2) get a New Value by finding the largest floating-point number of
+    Original Value and Value, and
+
+    3) store the New Value back through Pointer.
+
+    The instruction's result is the Original Value.
+
+    Result Type must be a floating-point type scalar.
+
+    The type of Value must be the same as Result Type. The type of the value
+    pointed to by Pointer must be the same as Result Type.
+
+    Memory must be a valid memory Scope.
+
+    #### Example:
+
+    ```mlir
+    %0 = spirv.EXT.AtomicFMax <Device> <None> %pointer, %value :
+                           !spirv.ptr<f32, StorageBuffer>
+    ```
+  }];
+
+  let availability = [
+    MinVersion<SPIRV_V_1_0>,
+    MaxVersion<SPIRV_V_1_6>,
+    Extension<[SPV_EXT_shader_atomic_float_min_max]>,
+    Capability<[SPIRV_C_AtomicFloat16MinMaxEXT, SPIRV_C_AtomicFloat32MinMaxEXT, SPIRV_C_AtomicFloat64MinMaxEXT]>
+  ];
+
+  let arguments = (ins
+    SPIRV_AnyPtr:$pointer,
+    SPIRV_ScopeAttr:$memory_scope,
+    SPIRV_MemorySemanticsAttr:$semantics,
+    SPIRV_Float:$value
+  );
+
+  let results = (outs
+    SPIRV_Float:$result
+  );
+
+  let assemblyFormat = [{
+    $memory_scope $semantics operands attr-dict `:` type($pointer)
+  }];
+}
+
+// -----
+
 def SPIRV_AtomicIAddOp : SPIRV_AtomicUpdateWithValueOp<"AtomicIAdd", []> {
   let summary = [{
     Perform the following steps atomically with respect to any other atomic
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
index 68a0ee470709d..9ad4f5308e247 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td
@@ -4706,6 +4706,8 @@ def SPIRV_OC_OpEmitMeshTasksEXT               : I32EnumAttrCase<"OpEmitMeshTasks
 def SPIRV_OC_OpSetMeshOutputsEXT              : I32EnumAttrCase<"OpSetMeshOutputsEXT", 5295>;
 def SPIRV_OC_OpSubgroupBlockReadINTEL         : I32EnumAttrCase<"OpSubgroupBlockReadINTEL", 5575>;
 def SPIRV_OC_OpSubgroupBlockWriteINTEL        : I32EnumAttrCase<"OpSubgroupBlockWriteINTEL", 5576>;
+def SPIRV_OC_OpAtomicFMinEXT                  : I32EnumAttrCase<"OpAtomicFMinEXT", 5614>;
+def SPIRV_OC_OpAtomicFMaxEXT                  : I32EnumAttrCase<"OpAtomicFMaxEXT", 5615>;
 def SPIRV_OC_OpAssumeTrueKHR                  : I32EnumAttrCase<"OpAssumeTrueKHR", 5630>;
 def SPIRV_OC_OpExpectKHR                      : I32EnumAttrCase<"OpExpectKHR", 5631>;
 def SPIRV_OC_OpAtomicFAddEXT                  : I32EnumAttrCase<"OpAtomicFAddEXT", 6035>;
@@ -4836,6 +4838,7 @@ def SPIRV_OpcodeAttr :
       SPIRV_OC_OpSpecConstantCompositeReplicateEXT,
       SPIRV_OC_OpEmitMeshTasksEXT, SPIRV_OC_OpSetMeshOutputsEXT,
       SPIRV_OC_OpSubgroupBlockReadINTEL, SPIRV_OC_OpSubgroupBlockWriteINTEL,
+      SPIRV_OC_OpAtomicFMinEXT, SPIRV_OC_OpAtomicFMaxEXT,
       SPIRV_OC_OpAssumeTrueKHR, SPIRV_OC_OpExpectKHR, SPIRV_OC_OpAtomicFAddEXT,
       SPIRV_OC_OpTypeStructContinuedINTEL,
       SPIRV_OC_OpConstantCompositeContinuedINTEL,
diff --git a/mlir/lib/Dialect/SPIRV/IR/AtomicOps.cpp b/mlir/lib/Dialect/SPIRV/IR/AtomicOps.cpp
index 7029268177128..605e9bb00f8d4 100644
--- a/mlir/lib/Dialect/SPIRV/IR/AtomicOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/AtomicOps.cpp
@@ -77,6 +77,22 @@ LogicalResult EXTAtomicFAddOp::verify() {
   return verifyAtomicUpdateOp<EXTAtomicFAddOp, FloatType>(getOperation());
 }
 
+//===----------------------------------------------------------------------===//
+// spirv.EXT.AtomicFMinOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult EXTAtomicFMinOp::verify() {
+  return verifyAtomicUpdateOp<EXTAtomicFMinOp, FloatType>(getOperation());
+}
+
+//===----------------------------------------------------------------------===//
+// spirv.EXT.AtomicFMaxOp
+//===----------------------------------------------------------------------===//
+
+LogicalResult EXTAtomicFMaxOp::verify() {
+  return verifyAtomicUpdateOp<EXTAtomicFMaxOp, FloatType>(getOperation());
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.AtomicIDecrementOp
 //===----------------------------------------------------------------------===//
diff --git a/mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir b/mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir
index feaa9123fd715..9e88a7a388a83 100644
--- a/mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir
@@ -356,3 +356,91 @@ func.func @atomic_bf16_fadd(%ptr : !spirv.ptr<bf16, StorageBuffer>, %value : bf1
   %0 = spirv.EXT.AtomicFAdd <Device> <None> %ptr, %value : !spirv.ptr<bf16, StorageBuffer>
   return %0 : bf16
 }
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv.EXT.AtomicFMin
+//===----------------------------------------------------------------------===//
+
+func.func @atomic_fmin(%ptr : !spirv.ptr<f32, StorageBuffer>, %value : f32) -> f32 {
+  // CHECK: spirv.EXT.AtomicFMin <Device> <None> %{{.*}}, %{{.*}} : !spirv.ptr<f32, StorageBuffer>
+  %0 = spirv.EXT.AtomicFMin <Device> <None> %ptr, %value : !spirv.ptr<f32, StorageBuffer>
+  return %0 : f32
+}
+
+// -----
+
+func.func @atomic_fmin(%ptr : !spirv.ptr<i32, StorageBuffer>, %value : f32) -> f32 {
+  // expected-error @+1 {{'spirv.EXT.AtomicFMin' op failed to verify that `result` type matches pointee type of `pointer`}}
+  %0 = "spirv.EXT.AtomicFMin"(%ptr, %value) {memory_scope = #spirv.scope<Workgroup>, semantics = #spirv.memory_semantics<AcquireRelease>} : (!spirv.ptr<i32, StorageBuffer>, f32) -> (f32)
+  return %0 : f32
+}
+
+// -----
+
+func.func @atomic_fmin(%ptr : !spirv.ptr<f32, StorageBuffer>, %value : f64) -> f64 {
+  // expected-error @+1 {{'spirv.EXT.AtomicFMin' op failed to verify that `result` type matches pointee type of `pointer`}}
+  %0 = "spirv.EXT.AtomicFMin"(%ptr, %value) {memory_scope = #spirv.scope<Device>, semantics = #spirv.memory_semantics<AcquireRelease>} : (!spirv.ptr<f32, StorageBuffer>, f64) -> (f64)
+  return %0 : f64
+}
+
+// -----
+
+func.func @atomic_fmin(%ptr : !spirv.ptr<f32, StorageBuffer>, %value : f32) -> f32 {
+  // expected-error @+1 {{expected at most one of these four memory constraints to be set: `Acquire`, `Release`,`AcquireRelease` or `SequentiallyConsistent`}}
+  %0 = spirv.EXT.AtomicFMin <Device> <Acquire|Release> %ptr, %value : !spirv.ptr<f32, StorageBuffer>
+  return %0 : f32
+}
+
+// -----
+
+func.func @atomic_bf16_fmin(%ptr : !spirv.ptr<bf16, StorageBuffer>, %value : bf16) -> bf16 {
+  // expected-error @+1 {{op operand #1 must be 16/32/64-bit float, but got 'bf16'}}
+  %0 = spirv.EXT.AtomicFMin <Device> <None> %ptr, %value : !spirv.ptr<bf16, StorageBuffer>
+  return %0 : bf16
+}
+
+// -----
+
+//===----------------------------------------------------------------------===//
+// spirv.EXT.AtomicFMax
+//===----------------------------------------------------------------------===//
+
+func.func @atomic_fmax(%ptr : !spirv.ptr<f32, StorageBuffer>, %value : f32) -> f32 {
+  // CHECK: spirv.EXT.AtomicFMax <Device> <None> %{{.*}}, %{{.*}} : !spirv.ptr<f32, StorageBuffer>
+  %0 = spirv.EXT.AtomicFMax <Device> <None> %ptr, %value : !spirv.ptr<f32, StorageBuffer>
+  return %0 : f32
+}
+
+// -----
+
+func.func @atomic_fmax(%ptr : !spirv.ptr<i32, StorageBuffer>, %value : f32) -> f32 {
+  // expected-error @+1 {{'spirv.EXT.AtomicFMax' op failed to verify that `result` type matches pointee type of `pointer`}}
+  %0 = "spirv.EXT.AtomicFMax"(%ptr, %value) {memory_scope = #spirv.scope<Workgroup>, semantics = #spirv.memory_semantics<AcquireRelease>} : (!spirv.ptr<i32, StorageBuffer>, f32) -> (f32)
+  return %0 : f32
+}
+
+// -----
+
+func.func @atomic_fmax(%ptr : !spirv.ptr<f32, StorageBuffer>, %value : f64) -> f64 {
+  // expected-error @+1 {{'spirv.EXT.AtomicFMax' op failed to verify that `result` type matches pointee type of `pointer`}}
+  %0 = "spirv.EXT.AtomicFMax"(%ptr, %value) {memory_scope = #spirv.scope<Device>, semantics = #spirv.memory_semantics<AcquireRelease>} : (!spirv.ptr<f32, StorageBuffer>, f64) -> (f64)
+  return %0 : f64
+}
+
+// -----
+
+func.func @atomic_fmax(%ptr : !spirv.ptr<f32, StorageBuffer>, %value : f32) -> f32 {
+  // expected-error @+1 {{expected at most one of these four memory constraints to be set: `Acquire`, `Release`,`AcquireRelease` or `SequentiallyConsistent`}}
+  %0 = spirv.EXT.AtomicFMax <Device> <Acquire|Release> %ptr, %value : !spirv.ptr<f32, StorageBuffer>
+  return %0 : f32
+}
+
+// -----
+
+func.func @atomic_bf16_fmax(%ptr : !spirv.ptr<bf16, StorageBuffer>, %value : bf16) -> bf16 {
+  // expected-error @+1 {{op operand #1 must be 16/32/64-bit float, but got 'bf16'}}
+  %0 = spirv.EXT.AtomicFMax <Device> <None> %ptr, %value : !spirv.ptr<bf16, StorageBuffer>
+  return %0 : bf16
+}
diff --git a/mlir/test/Target/SPIRV/atomic-ops.mlir b/mlir/test/Target/SPIRV/atomic-ops.mlir
index aecc60cbbc71f..fceea9e73decc 100644
--- a/mlir/test/Target/SPIRV/atomic-ops.mlir
+++ b/mlir/test/Target/SPIRV/atomic-ops.mlir
@@ -5,7 +5,7 @@
 // RUN: %if spirv-tools %{ mlir-translate --no-implicit-module --serialize-spirv --split-input-file --spirv-save-validation-files-with-prefix=%t/module %s %}
 // RUN: %if spirv-tools %{ spirv-val %t %}
 
-spirv.module Physical64 OpenCL requires #spirv.vce<v1.0, [Kernel, Linkage, Addresses, AtomicFloat32AddEXT], [SPV_EXT_shader_atomic_float_add]> {
+spirv.module Physical64 OpenCL requires #spirv.vce<v1.0, [Kernel, Linkage, Addresses, AtomicFloat32AddEXT, AtomicFloat32MinMaxEXT], [SPV_EXT_shader_atomic_float_add, SPV_EXT_shader_atomic_float_min_max]> {
   // CHECK-LABEL: @test_int_atomics
   spirv.func @test_int_atomics(%ptr: !spirv.ptr<i32, Workgroup>, %value: i32, %comparator: i32) -> i32 "None" {
     // CHECK: spirv.AtomicCompareExchangeWeak <Workgroup> <Release> <Acquire> %{{.*}}, %{{.*}}, %{{.*}} : !spirv.ptr<i32, Workgroup>
@@ -47,8 +47,12 @@ spirv.module Physical64 OpenCL requires #spirv.vce<v1.0, [Kernel, Linkage, Addre
   spirv.func @test_float_atomics(%ptr: !spirv.ptr<f32, Workgroup>, %value: f32) -> f32 "None" {
     // CHECK: spirv.EXT.AtomicFAdd <Workgroup> <Acquire> %{{.*}}, %{{.*}} : !spirv.ptr<f32, Workgroup>
     %0 = spirv.EXT.AtomicFAdd <Workgroup> <Acquire> %ptr, %value : !spirv.ptr<f32, Workgroup>
+    // CHECK: spirv.EXT.AtomicFMin <Workgroup> <Acquire> %{{.*}}, %{{.*}} : !spirv.ptr<f32, Workgroup>
+    %1 = spirv.EXT.AtomicFMin <Workgroup> <Acquire> %ptr, %value : !spirv.ptr<f32, Workgroup>
+    // CHECK: spirv.EXT.AtomicFMax <Workgroup> <Acquire> %{{.*}}, %{{.*}} : !spirv.ptr<f32, Workgroup>
+    %2 = spirv.EXT.AtomicFMax <Workgroup> <Acquire> %ptr, %value : !spirv.ptr<f32, Workgroup>
     // CHECK: spirv.AtomicLoad <Workgroup> <Acquire> %{{.*}} : !spirv.ptr<f32, Workgroup>
-    %1 = spirv.AtomicLoad <Workgroup> <Acquire> %ptr : !spirv.ptr<f32, Workgroup>
+    %3 = spirv.AtomicLoad <Workgroup> <Acquire> %ptr : !spirv.ptr<f32, Workgroup>
     // CHECK: spirv.AtomicStore <Workgroup> <Release> %{{.*}}, %{{.*}} : !spirv.ptr<f32, Workgroup>
     spirv.AtomicStore <Workgroup> <Release> %ptr, %value : !spirv.ptr<f32, Workgroup>
     spirv.ReturnValue %0: f32



More information about the Mlir-commits mailing list