diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td --- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td +++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVAtomicOps.td @@ -18,7 +18,7 @@ SPV_Op { let parser = [{ return ::parseAtomicUpdateOp(parser, result, false); }]; let printer = [{ return ::printAtomicUpdateOp(getOperation(), p); }]; - let verifier = [{ return ::verifyAtomicUpdateOp(getOperation()); }]; + let verifier = [{ return ::verifyAtomicUpdateOp(getOperation()); }]; let arguments = (ins SPV_AnyPtr:$pointer, @@ -35,7 +35,7 @@ SPV_Op { let parser = [{ return ::parseAtomicUpdateOp(parser, result, true); }]; let printer = [{ return ::printAtomicUpdateOp(getOperation(), p); }]; - let verifier = [{ return ::verifyAtomicUpdateOp(getOperation()); }]; + let verifier = [{ return ::verifyAtomicUpdateOp(getOperation()); }]; let arguments = (ins SPV_AnyPtr:$pointer, @@ -278,6 +278,71 @@ // ----- +def SPV_AtomicFAddEXTOp : SPV_Op<"AtomicFAddEXT", []> { + let summary = "TBD"; + + let description = [{ + + + + + 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 float addition 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. + + ``` + atomic-fadd-op ::= + `spv.AtomicFAddEXT` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + #### Example: + + ```mlir + %0 = spv.AtomicFAddEXT "Device" "None" %pointer, %value : + !spv.ptr + ```mlir + }]; + + let availability = [ + MinVersion, + MaxVersion, + Extension<[SPV_EXT_shader_atomic_float_add]>, + Capability<[SPV_C_AtomicFloat16AddEXT, SPV_C_AtomicFloat32AddEXT, SPV_C_AtomicFloat64AddEXT]> + ]; + + let arguments = (ins + SPV_AnyPtr:$pointer, + SPV_ScopeAttr:$memory_scope, + SPV_MemorySemanticsAttr:$semantics, + SPV_Float:$value + ); + + let results = (outs + SPV_Float:$result + ); + + let parser = [{ return ::parseAtomicUpdateOp(parser, result, true); }]; + let printer = [{ return ::printAtomicUpdateOp(getOperation(), p); }]; + let verifier = [{ return ::verifyAtomicUpdateOp(getOperation()); }]; +} + +// ----- + def SPV_AtomicIAddOp : SPV_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 --- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td +++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVBase.td @@ -4169,6 +4169,7 @@ def SPV_OC_OpCooperativeMatrixLengthNV : I32EnumAttrCase<"OpCooperativeMatrixLengthNV", 5362>; def SPV_OC_OpSubgroupBlockReadINTEL : I32EnumAttrCase<"OpSubgroupBlockReadINTEL", 5575>; def SPV_OC_OpSubgroupBlockWriteINTEL : I32EnumAttrCase<"OpSubgroupBlockWriteINTEL", 5576>; +def SPV_OC_OpAtomicFAddEXT : I32EnumAttrCase<"OpAtomicFAddEXT", 6035>; def SPV_OpcodeAttr : SPV_I32EnumAttr<"Opcode", "valid SPIR-V instructions", [ @@ -4232,7 +4233,8 @@ SPV_OC_OpSubgroupBallotKHR, SPV_OC_OpTypeCooperativeMatrixNV, SPV_OC_OpCooperativeMatrixLoadNV, SPV_OC_OpCooperativeMatrixStoreNV, SPV_OC_OpCooperativeMatrixMulAddNV, SPV_OC_OpCooperativeMatrixLengthNV, - SPV_OC_OpSubgroupBlockReadINTEL, SPV_OC_OpSubgroupBlockWriteINTEL + SPV_OC_OpSubgroupBlockReadINTEL, SPV_OC_OpSubgroupBlockWriteINTEL, + SPV_OC_OpAtomicFAddEXT ]>; // End opcode section. Generated from SPIR-V spec; DO NOT MODIFY! diff --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp --- a/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp +++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVOps.cpp @@ -756,14 +756,28 @@ << "\" " << op->getOperands() << " : " << op->getOperand(0).getType(); } +template +static StringRef stringifyTypeName(); + +template <> +StringRef stringifyTypeName() { + return "integer"; +} + +template <> +StringRef stringifyTypeName() { + return "float"; +} + // Verifies an atomic update op. +template static LogicalResult verifyAtomicUpdateOp(Operation *op) { auto ptrType = op->getOperand(0).getType().cast(); auto elementType = ptrType.getPointeeType(); - if (!elementType.isa()) - return op->emitOpError( - "pointer operand must point to an integer value, found ") - << elementType; + if (!elementType.isa()) + return op->emitOpError() << "pointer operand must point to an " + << stringifyTypeName() + << " value, found " << elementType; if (op->getNumOperands() > 1) { auto valueType = op->getOperand(1).getType(); diff --git a/mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir b/mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir --- a/mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir +++ b/mlir/test/Dialect/SPIRV/IR/atomic-ops.mlir @@ -236,3 +236,39 @@ %0 = spv.AtomicXor "Workgroup" "None" %ptr, %value : !spv.ptr return %0 : i32 } + +// ----- + +//===----------------------------------------------------------------------===// +// spv.AtomicFAddEXT +//===----------------------------------------------------------------------===// + +func @atomic_fadd(%ptr : !spv.ptr, %value : f32) -> f32 { + // CHECK: spv.AtomicFAddEXT "Device" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicFAddEXT "Device" "None" %ptr, %value : !spv.ptr + return %0 : f32 +} + +// ----- + +func @atomic_fadd(%ptr : !spv.ptr, %value : f32) -> f32 { + // expected-error @+1 {{pointer operand must point to an float value, found 'i32'}} + %0 = "spv.AtomicFAddEXT"(%ptr, %value) {memory_scope = 4: i32, semantics = 0x4 : i32} : (!spv.ptr, f32) -> (f32) + return %0 : f32 +} + +// ----- + +func @atomic_fadd(%ptr : !spv.ptr, %value : f64) -> f64 { + // expected-error @+1 {{expected value to have the same type as the pointer operand's pointee type 'f32', but found 'f64'}} + %0 = "spv.AtomicFAddEXT"(%ptr, %value) {memory_scope = 2: i32, semantics = 0x8 : i32} : (!spv.ptr, f64) -> (f64) + return %0 : f64 +} + +// ----- + +func @atomic_fadd(%ptr : !spv.ptr, %value : f32) -> f32 { + // expected-error @+1 {{expected at most one of these four memory constraints to be set: `Acquire`, `Release`,`AcquireRelease` or `SequentiallyConsistent`}} + %0 = spv.AtomicFAddEXT "Device" "Acquire|Release" %ptr, %value : !spv.ptr + return %0 : f32 +} diff --git a/mlir/test/Target/SPIRV/atomic-ops.mlir b/mlir/test/Target/SPIRV/atomic-ops.mlir --- a/mlir/test/Target/SPIRV/atomic-ops.mlir +++ b/mlir/test/Target/SPIRV/atomic-ops.mlir @@ -1,8 +1,8 @@ // RUN: mlir-translate -test-spirv-roundtrip -split-input-file %s | FileCheck %s spv.module Logical GLSL450 requires #spv.vce { - // CHECK-LABEL: @atomic_compare_exchange_weak - spv.func @atomic_compare_exchange_weak(%ptr: !spv.ptr, %value: i32, %comparator: i32) -> i32 "None" { + // CHECK-LABEL: @test_int_atomics + spv.func @test_int_atomics(%ptr: !spv.ptr, %value: i32, %comparator: i32) -> i32 "None" { // CHECK: spv.AtomicCompareExchangeWeak "Workgroup" "Release" "Acquire" %{{.*}}, %{{.*}}, %{{.*}} : !spv.ptr %0 = spv.AtomicCompareExchangeWeak "Workgroup" "Release" "Acquire" %ptr, %value, %comparator: !spv.ptr // CHECK: spv.AtomicAnd "Device" "None" %{{.*}}, %{{.*}} : !spv.ptr @@ -33,4 +33,11 @@ %13 = spv.AtomicExchange "Workgroup" "Release" %ptr, %value: !spv.ptr spv.ReturnValue %0: i32 } + + // CHECK-LABEL: @test_float_atomics + spv.func @test_float_atomics(%ptr: !spv.ptr, %value: f32) -> f32 "None" { + // CHECK: spv.AtomicFAddEXT "Workgroup" "Acquire" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicFAddEXT "Workgroup" "Acquire" %ptr, %value : !spv.ptr + spv.ReturnValue %0: f32 + } }