[Mlir-commits] [mlir] [mlir][SPIR-V] Add EXT.AtomicFMin and EXT.AtomicFMax ops (PR #213596)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sun Aug 2 22:37:15 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: Arseniy Obolenskiy (aobolensk)
<details>
<summary>Changes</summary>
---
Full diff: https://github.com/llvm/llvm-project/pull/213596.diff
5 Files Affected:
- (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td (+124)
- (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td (+3)
- (modified) mlir/lib/Dialect/SPIRV/IR/AtomicOps.cpp (+16)
- (modified) mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir (+88)
- (modified) mlir/test/Target/SPIRV/atomic-ops.mlir (+6-2)
``````````diff
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
``````````
</details>
https://github.com/llvm/llvm-project/pull/213596
More information about the Mlir-commits
mailing list