From 80ec474a65a29a740b2edf7cc77d493ab4013a6b Mon Sep 17 00:00:00 2001 From: Mahesh Ravishankar Date: Mon, 16 Dec 2019 15:05:21 -0800 Subject: [PATCH] Add atomic operations to SPIR-V dialect. Some changes to the dialect generation script to allow specification of different base class to derive from in ODS. PiperOrigin-RevId: 285859230 --- mlir/include/mlir/Dialect/SPIRV/SPIRVAtomicOps.td | 493 ++++++++++++++++++++- mlir/include/mlir/Dialect/SPIRV/SPIRVBase.td | 24 +- .../Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp | 5 +- mlir/lib/Dialect/SPIRV/SPIRVLowering.cpp | 2 +- mlir/lib/Dialect/SPIRV/SPIRVOps.cpp | 65 +++ .../Dialect/SPIRV/Serialization/atomic-ops.mlir | 24 +- mlir/test/Dialect/SPIRV/atomic-ops.mlir | 133 +++++- mlir/utils/spirv/define_inst.sh | 4 +- mlir/utils/spirv/gen_spirv_dialect.py | 7 +- 9 files changed, 739 insertions(+), 18 deletions(-) diff --git a/mlir/include/mlir/Dialect/SPIRV/SPIRVAtomicOps.td b/mlir/include/mlir/Dialect/SPIRV/SPIRVAtomicOps.td index d8e62bb..15b6ab0 100644 --- a/mlir/include/mlir/Dialect/SPIRV/SPIRVAtomicOps.td +++ b/mlir/include/mlir/Dialect/SPIRV/SPIRVAtomicOps.td @@ -23,13 +23,60 @@ #ifndef SPIRV_ATOMIC_OPS #define SPIRV_ATOMIC_OPS +class SPV_AtomicUpdateOp traits = []> : + SPV_Op { + let parser = [{ return ::parseAtomicUpdateOp(parser, result, false); }]; + let printer = [{ return ::printAtomicUpdateOp(getOperation(), p); }]; + let verifier = [{ return ::verifyAtomicUpdateOp(getOperation()); }]; + + let arguments = (ins + SPV_AnyPtr:$pointer, + SPV_ScopeAttr:$memory_scope, + SPV_MemorySemanticsAttr:$semantics + ); + let results = (outs + SPV_Integer:$result + ); +} + +class SPV_AtomicUpdateWithValueOp traits = []> : + SPV_Op { + let parser = [{ return ::parseAtomicUpdateOp(parser, result, true); }]; + let printer = [{ return ::printAtomicUpdateOp(getOperation(), p); }]; + let verifier = [{ return ::verifyAtomicUpdateOp(getOperation()); }]; + + let arguments = (ins + SPV_AnyPtr:$pointer, + SPV_ScopeAttr:$memory_scope, + SPV_MemorySemanticsAttr:$semantics, + SPV_Integer:$value + ); + let results = (outs + SPV_Integer:$result + ); +} + // ----- -def SPV_AtomicCompareExchangeWeakOp : SPV_Op<"AtomicCompareExchangeWeak", []> { - let summary = "Deprecated (use OpAtomicCompareExchange)."; +def SPV_AtomicAndOp : SPV_AtomicUpdateWithValueOp<"AtomicAnd", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; let description = [{ - Has the same semantics as OpAtomicCompareExchange. + 1) load through Pointer to get an Original Value, + + 2) get a New Value by the bitwise AND 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 an integer 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. @@ -40,6 +87,33 @@ def SPV_AtomicCompareExchangeWeakOp : SPV_Op<"AtomicCompareExchangeWeak", []> { memory-semantics ::= `"None"` | `"Acquire"` | "Release"` | ... + atomic-and-op ::= + `spv.AtomicAnd` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicAnd "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicCompareExchangeWeakOp : SPV_Op<"AtomicCompareExchangeWeak", []> { + let summary = "Deprecated (use OpAtomicCompareExchange)."; + + let description = [{ + Has the same semantics as OpAtomicCompareExchange. + + Memory must be a valid memory Scope. + + ### Custom assembly form + + ``` atomic-compare-exchange-weak-op ::= `spv.AtomicCompareExchangeWeak` scope memory-semantics memory-semantics ssa-use `,` ssa-use `,` ssa-use @@ -71,4 +145,417 @@ def SPV_AtomicCompareExchangeWeakOp : SPV_Op<"AtomicCompareExchangeWeak", []> { // ----- +def SPV_AtomicIAddOp : SPV_AtomicUpdateWithValueOp<"AtomicIAdd", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value by integer 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 an integer 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. + + ### Custom assembly form + + ``` + atomic-iadd-op ::= + `spv.AtomicIAdd` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicIAdd "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicIDecrementOp : SPV_AtomicUpdateOp<"AtomicIDecrement", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value through integer subtraction of 1 from Original Value, + and + + 3) store the New Value back through Pointer. + + The instruction’s result is the Original Value. + + Result Type must be an integer type scalar. The type of the value + pointed to by Pointer must be the same as Result Type. + + Memory must be a valid memory Scope. + + ### Custom assembly form + + ``` + atomic-idecrement-op ::= + `spv.AtomicIDecrement` scope memory-semantics ssa-use + `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicIDecrement "Device" "None" %pointer : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicIIncrementOp : SPV_AtomicUpdateOp<"AtomicIIncrement", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value through integer addition of 1 to Original Value, and + + 3) store the New Value back through Pointer. + + The instruction’s result is the Original Value. + + Result Type must be an integer type scalar. The type of the value + pointed to by Pointer must be the same as Result Type. + + Memory must be a valid memory Scope. + + ### Custom assembly form + + ``` + atomic-iincrement-op ::= + `spv.AtomicIIncrement` scope memory-semantics ssa-use + `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicIncrement "Device" "None" %pointer : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicISubOp : SPV_AtomicUpdateWithValueOp<"AtomicISub", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value by integer subtraction of Value from Original Value, + and + + 3) store the New Value back through Pointer. + + The instruction’s result is the Original Value. + + Result Type must be an integer 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. + + ### Custom assembly form + + ``` + atomic-isub-op ::= + `spv.AtomicISub` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicISub "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicOrOp : SPV_AtomicUpdateWithValueOp<"AtomicOr", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value by the bitwise OR 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 an integer 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. + + ### Custom assembly form + + ``` + atomic-or-op ::= + `spv.AtomicOr` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicOr "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicSMaxOp : SPV_AtomicUpdateWithValueOp<"AtomicSMax", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value by finding the largest signed integer 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 an integer 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. + + ### Custom assembly form + + ``` + atomic-smax-op ::= + `spv.AtomicSMax` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicSMax "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicSMinOp : SPV_AtomicUpdateWithValueOp<"AtomicSMin", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value by finding the smallest signed integer 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 an integer 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. + + ### Custom assembly form + + ``` + atomic-smin-op ::= + `spv.AtomicSMin` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicSMin "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicUMaxOp : SPV_AtomicUpdateWithValueOp<"AtomicUMax", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value by finding the largest unsigned integer 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 an integer 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. + + ### Custom assembly form + + ``` + atomic-umax-op ::= + `spv.AtomicUMax` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicUMax "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicUMinOp : SPV_AtomicUpdateWithValueOp<"AtomicUMin", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value by finding the smallest unsigned integer 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 an integer 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. + + ### Custom assembly form + + ``` + atomic-umin-op ::= + `spv.AtomicUMin` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicUMin "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + +def SPV_AtomicXorOp : SPV_AtomicUpdateWithValueOp<"AtomicXor", []> { + let summary = [{ + Perform the following steps atomically with respect to any other atomic + accesses within Scope to the same location: + }]; + + let description = [{ + 1) load through Pointer to get an Original Value, + + 2) get a New Value by the bitwise exclusive OR 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 an integer 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. + + ### Custom assembly form + + ``` + atomic-xor-op ::= + `spv.AtomicXor` scope memory-semantics + ssa-use `,` ssa-use `:` spv-pointer-type + ``` + + For example: + + ``` + %0 = spv.AtomicXor "Device" "None" %pointer, %value : + !spv.ptr + ``` + }]; +} + +// ----- + #endif // SPIRV_ATOMIC_OPS diff --git a/mlir/include/mlir/Dialect/SPIRV/SPIRVBase.td b/mlir/include/mlir/Dialect/SPIRV/SPIRVBase.td index 8368a62..8383988 100644 --- a/mlir/include/mlir/Dialect/SPIRV/SPIRVBase.td +++ b/mlir/include/mlir/Dialect/SPIRV/SPIRVBase.td @@ -1144,6 +1144,17 @@ def SPV_OC_OpBitCount : I32EnumAttrCase<"OpBitCount", 205>; def SPV_OC_OpControlBarrier : I32EnumAttrCase<"OpControlBarrier", 224>; def SPV_OC_OpMemoryBarrier : I32EnumAttrCase<"OpMemoryBarrier", 225>; def SPV_OC_OpAtomicCompareExchangeWeak : I32EnumAttrCase<"OpAtomicCompareExchangeWeak", 231>; +def SPV_OC_OpAtomicIIncrement : I32EnumAttrCase<"OpAtomicIIncrement", 232>; +def SPV_OC_OpAtomicIDecrement : I32EnumAttrCase<"OpAtomicIDecrement", 233>; +def SPV_OC_OpAtomicIAdd : I32EnumAttrCase<"OpAtomicIAdd", 234>; +def SPV_OC_OpAtomicISub : I32EnumAttrCase<"OpAtomicISub", 235>; +def SPV_OC_OpAtomicSMin : I32EnumAttrCase<"OpAtomicSMin", 236>; +def SPV_OC_OpAtomicUMin : I32EnumAttrCase<"OpAtomicUMin", 237>; +def SPV_OC_OpAtomicSMax : I32EnumAttrCase<"OpAtomicSMax", 238>; +def SPV_OC_OpAtomicUMax : I32EnumAttrCase<"OpAtomicUMax", 239>; +def SPV_OC_OpAtomicAnd : I32EnumAttrCase<"OpAtomicAnd", 240>; +def SPV_OC_OpAtomicOr : I32EnumAttrCase<"OpAtomicOr", 241>; +def SPV_OC_OpAtomicXor : I32EnumAttrCase<"OpAtomicXor", 242>; def SPV_OC_OpPhi : I32EnumAttrCase<"OpPhi", 245>; def SPV_OC_OpLoopMerge : I32EnumAttrCase<"OpLoopMerge", 246>; def SPV_OC_OpSelectionMerge : I32EnumAttrCase<"OpSelectionMerge", 247>; @@ -1194,11 +1205,14 @@ def SPV_OpcodeAttr : SPV_OC_OpBitwiseAnd, SPV_OC_OpNot, SPV_OC_OpBitFieldInsert, SPV_OC_OpBitFieldSExtract, SPV_OC_OpBitFieldUExtract, SPV_OC_OpBitReverse, SPV_OC_OpBitCount, SPV_OC_OpControlBarrier, SPV_OC_OpMemoryBarrier, - SPV_OC_OpAtomicCompareExchangeWeak, SPV_OC_OpPhi, SPV_OC_OpLoopMerge, - SPV_OC_OpSelectionMerge, SPV_OC_OpLabel, SPV_OC_OpBranch, - SPV_OC_OpBranchConditional, SPV_OC_OpReturn, SPV_OC_OpReturnValue, - SPV_OC_OpUnreachable, SPV_OC_OpModuleProcessed, SPV_OC_OpGroupNonUniformBallot, - SPV_OC_OpSubgroupBallotKHR + SPV_OC_OpAtomicCompareExchangeWeak, SPV_OC_OpAtomicIIncrement, + SPV_OC_OpAtomicIDecrement, SPV_OC_OpAtomicIAdd, SPV_OC_OpAtomicISub, + SPV_OC_OpAtomicSMin, SPV_OC_OpAtomicUMin, SPV_OC_OpAtomicSMax, + SPV_OC_OpAtomicUMax, SPV_OC_OpAtomicAnd, SPV_OC_OpAtomicOr, SPV_OC_OpAtomicXor, + SPV_OC_OpPhi, SPV_OC_OpLoopMerge, SPV_OC_OpSelectionMerge, SPV_OC_OpLabel, + SPV_OC_OpBranch, SPV_OC_OpBranchConditional, SPV_OC_OpReturn, + SPV_OC_OpReturnValue, SPV_OC_OpUnreachable, SPV_OC_OpModuleProcessed, + SPV_OC_OpGroupNonUniformBallot, SPV_OC_OpSubgroupBallotKHR ]> { let cppNamespace = "::mlir::spirv"; } diff --git a/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp b/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp index a8747a7..92cc026 100644 --- a/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp +++ b/mlir/lib/Conversion/GPUToSPIRV/ConvertGPUToSPIRV.cpp @@ -234,8 +234,9 @@ lowerAsEntryFunction(gpu::GPUFuncOp funcOp, SPIRVTypeConverter &typeConverter, "lowering as entry functions requires ABI info for all arguments"); return nullptr; } - // For entry functions need to make the signature void(void). Compute the - // replacement value for all arguments and replace all uses. + // Update the signature to valid SPIR-V types and add the ABI + // attributes. These will be "materialized" by using the + // LowerABIAttributesPass. TypeConverter::SignatureConversion signatureConverter(fnType.getNumInputs()); { for (auto argType : enumerate(funcOp.getType().getInputs())) { diff --git a/mlir/lib/Dialect/SPIRV/SPIRVLowering.cpp b/mlir/lib/Dialect/SPIRV/SPIRVLowering.cpp index 1e68b49..284fe91 100644 --- a/mlir/lib/Dialect/SPIRV/SPIRVLowering.cpp +++ b/mlir/lib/Dialect/SPIRV/SPIRVLowering.cpp @@ -246,7 +246,7 @@ Value *mlir::spirv::getBuiltinVariableValue(Operation *op, } //===----------------------------------------------------------------------===// -// Entry Function signature Conversion +// Set ABI attributes for lowering entry functions. //===----------------------------------------------------------------------===// LogicalResult diff --git a/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp b/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp index 839f134..140470b 100644 --- a/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp +++ b/mlir/lib/Dialect/SPIRV/SPIRVOps.cpp @@ -49,6 +49,7 @@ static constexpr const char kIndicesAttrName[] = "indices"; static constexpr const char kInitializerAttrName[] = "initializer"; static constexpr const char kInterfaceAttrName[] = "interface"; static constexpr const char kMemoryScopeAttrName[] = "memory_scope"; +static constexpr const char kSemanticsAttrName[] = "semantics"; static constexpr const char kSpecConstAttrName[] = "spec_const"; static constexpr const char kSpecIdAttrName[] = "spec_id"; static constexpr const char kTypeAttrName[] = "type"; @@ -514,6 +515,70 @@ static LogicalResult verifyBitFieldExtractOp(Operation *op) { return success(); } +// Parses an atomic update op. If the update op does not take a value (like +// AtomicIIncrement) `hasValue` must be false. +static ParseResult parseAtomicUpdateOp(OpAsmParser &parser, + OperationState &state, bool hasValue) { + spirv::Scope scope; + spirv::MemorySemantics memoryScope; + SmallVector operandInfo; + OpAsmParser::OperandType ptrInfo, valueInfo; + Type type; + llvm::SMLoc loc; + if (parseEnumAttribute(scope, parser, state, kMemoryScopeAttrName) || + parseEnumAttribute(memoryScope, parser, state, kSemanticsAttrName) || + parser.parseOperandList(operandInfo, (hasValue ? 2 : 1)) || + parser.getCurrentLocation(&loc) || parser.parseColonType(type)) + return failure(); + + auto ptrType = type.dyn_cast(); + if (!ptrType) + return parser.emitError(loc, "expected pointer type"); + + SmallVector operandTypes; + operandTypes.push_back(ptrType); + if (hasValue) + operandTypes.push_back(ptrType.getPointeeType()); + if (parser.resolveOperands(operandInfo, operandTypes, parser.getNameLoc(), + state.operands)) + return failure(); + return parser.addTypeToList(ptrType.getPointeeType(), state.types); +} + +// Prints an atomic update op. +static void printAtomicUpdateOp(Operation *op, OpAsmPrinter &printer) { + printer << op->getName() << " \""; + auto scopeAttr = op->getAttrOfType(kMemoryScopeAttrName); + printer << spirv::stringifyScope( + static_cast(scopeAttr.getInt())) + << "\" \""; + auto memorySemanticsAttr = op->getAttrOfType(kSemanticsAttrName); + printer << spirv::stringifyMemorySemantics( + static_cast( + memorySemanticsAttr.getInt())) + << "\" " << op->getOperands() << " : " + << op->getOperand(0)->getType(); +} + +// Verifies an atomic update op. +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 (op->getNumOperands() > 1) { + auto valueType = op->getOperand(1)->getType(); + if (valueType != elementType) + return op->emitOpError("expected value to have the same type as the " + "pointer operand's pointee type ") + << elementType << ", but found " << valueType; + } + return success(); +} + // Parses an op that has no inputs and no outputs. static ParseResult parseNoIOOp(OpAsmParser &parser, OperationState &state) { if (parser.parseOptionalAttrDict(state.attributes)) diff --git a/mlir/test/Dialect/SPIRV/Serialization/atomic-ops.mlir b/mlir/test/Dialect/SPIRV/Serialization/atomic-ops.mlir index cead3cf..93d4af1 100644 --- a/mlir/test/Dialect/SPIRV/Serialization/atomic-ops.mlir +++ b/mlir/test/Dialect/SPIRV/Serialization/atomic-ops.mlir @@ -3,8 +3,30 @@ spv.module "Logical" "GLSL450" { // CHECK-LABEL: @atomic_compare_exchange_weak func @atomic_compare_exchange_weak(%ptr: !spv.ptr, %value: i32, %comparator: i32) -> i32 { - // CHECK: %{{.*}} = spv.AtomicCompareExchangeWeak "Workgroup" "Release" "Acquire" %{{.*}}, %{{.*}}, %{{.*}} : !spv.ptr + // 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 + %1 = spv.AtomicAnd "Device" "None" %ptr, %value : !spv.ptr + // CHECK: spv.AtomicIAdd "Workgroup" "Acquire" %{{.*}}, %{{.*}} : !spv.ptr + %2 = spv.AtomicIAdd "Workgroup" "Acquire" %ptr, %value : !spv.ptr + // CHECK: spv.AtomicIDecrement "Workgroup" "Acquire" %{{.*}} : !spv.ptr + %3 = spv.AtomicIDecrement "Workgroup" "Acquire" %ptr : !spv.ptr + // CHECK: spv.AtomicIIncrement "Device" "Release" %{{.*}} : !spv.ptr + %4 = spv.AtomicIIncrement "Device" "Release" %ptr : !spv.ptr + // CHECK: spv.AtomicISub "Workgroup" "Acquire" %{{.*}}, %{{.*}} : !spv.ptr + %5 = spv.AtomicISub "Workgroup" "Acquire" %ptr, %value : !spv.ptr + // CHECK: spv.AtomicOr "Workgroup" "AcquireRelease" %{{.*}}, %{{.*}} : !spv.ptr + %6 = spv.AtomicOr "Workgroup" "AcquireRelease" %ptr, %value : !spv.ptr + // CHECK: spv.AtomicSMax "Subgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %7 = spv.AtomicSMax "Subgroup" "None" %ptr, %value : !spv.ptr + // CHECK: spv.AtomicSMin "Device" "Release" %{{.*}}, %{{.*}} : !spv.ptr + %8 = spv.AtomicSMin "Device" "Release" %ptr, %value : !spv.ptr + // CHECK: spv.AtomicUMax "Subgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %9 = spv.AtomicUMax "Subgroup" "None" %ptr, %value : !spv.ptr + // CHECK: spv.AtomicUMin "Device" "Release" %{{.*}}, %{{.*}} : !spv.ptr + %10 = spv.AtomicUMin "Device" "Release" %ptr, %value : !spv.ptr + // CHECK: spv.AtomicXor "Workgroup" "AcquireRelease" %{{.*}}, %{{.*}} : !spv.ptr + %11 = spv.AtomicXor "Workgroup" "AcquireRelease" %ptr, %value : !spv.ptr spv.ReturnValue %0: i32 } } diff --git a/mlir/test/Dialect/SPIRV/atomic-ops.mlir b/mlir/test/Dialect/SPIRV/atomic-ops.mlir index bb8ac54..67529f2 100644 --- a/mlir/test/Dialect/SPIRV/atomic-ops.mlir +++ b/mlir/test/Dialect/SPIRV/atomic-ops.mlir @@ -1,11 +1,40 @@ // RUN: mlir-opt -split-input-file -verify-diagnostics %s | FileCheck %s //===----------------------------------------------------------------------===// +// spv.AtomicAnd +//===----------------------------------------------------------------------===// + +func @atomic_and(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicAnd "Device" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicAnd "Device" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} + +// ----- + +func @atomic_and(%ptr : !spv.ptr, %value : i32) -> i32 { + // expected-error @+1 {{pointer operand must point to an integer value, found 'f32'}} + %0 = "spv.AtomicAnd"(%ptr, %value) {memory_scope = 4: i32, semantics = 0x4 : i32} : (!spv.ptr, i32) -> (i32) + return %0 : i32 +} + + +// ----- + +func @atomic_and(%ptr : !spv.ptr, %value : i64) -> i64 { + // expected-error @+1 {{expected value to have the same type as the pointer operand's pointee type 'i32', but found 'i64'}} + %0 = "spv.AtomicAnd"(%ptr, %value) {memory_scope = 2: i32, semantics = 0x8 : i32} : (!spv.ptr, i64) -> (i64) + return %0 : i64 +} + +// ----- + +//===----------------------------------------------------------------------===// // spv.AtomicCompareExchangeWeak //===----------------------------------------------------------------------===// func @atomic_compare_exchange_weak(%ptr: !spv.ptr, %value: i32, %comparator: i32) -> i32 { - // CHECK: %{{.*}} = spv.AtomicCompareExchangeWeak "Workgroup" "Release" "Acquire" %{{.*}}, %{{.*}}, %{{.*}} : !spv.ptr + // CHECK: spv.AtomicCompareExchangeWeak "Workgroup" "Release" "Acquire" %{{.*}}, %{{.*}}, %{{.*}} : !spv.ptr %0 = spv.AtomicCompareExchangeWeak "Workgroup" "Release" "Acquire" %ptr, %value, %comparator: !spv.ptr return %0: i32 } @@ -33,3 +62,105 @@ func @atomic_compare_exchange_weak(%ptr: !spv.ptr, %value: i32, %0 = "spv.AtomicCompareExchangeWeak"(%ptr, %value, %comparator) {memory_scope = 4: i32, equal_semantics = 0x4: i32, unequal_semantics = 0x2:i32} : (!spv.ptr, i32, i32) -> (i32) return %0: i32 } + +// ----- + +//===----------------------------------------------------------------------===// +// spv.AtomicIAdd +//===----------------------------------------------------------------------===// + +func @atomic_iadd(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicIAdd "Workgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicIAdd "Workgroup" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicIDecrement +//===----------------------------------------------------------------------===// + +func @atomic_idecrement(%ptr : !spv.ptr) -> i32 { + // CHECK: spv.AtomicIDecrement "Workgroup" "None" %{{.*}} : !spv.ptr + %0 = spv.AtomicIDecrement "Workgroup" "None" %ptr : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicIIncrement +//===----------------------------------------------------------------------===// + +func @atomic_iincrement(%ptr : !spv.ptr) -> i32 { + // CHECK: spv.AtomicIIncrement "Workgroup" "None" %{{.*}} : !spv.ptr + %0 = spv.AtomicIIncrement "Workgroup" "None" %ptr : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicISub +//===----------------------------------------------------------------------===// + +func @atomic_isub(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicISub "Workgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicISub "Workgroup" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicOr +//===----------------------------------------------------------------------===// + +func @atomic_or(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicOr "Workgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicOr "Workgroup" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicSMax +//===----------------------------------------------------------------------===// + +func @atomic_smax(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicSMax "Workgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicSMax "Workgroup" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicSMin +//===----------------------------------------------------------------------===// + +func @atomic_smin(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicSMin "Workgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicSMin "Workgroup" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicUMax +//===----------------------------------------------------------------------===// + +func @atomic_umax(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicUMax "Workgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicUMax "Workgroup" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicUMin +//===----------------------------------------------------------------------===// + +func @atomic_umin(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicUMin "Workgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicUMin "Workgroup" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} + +//===----------------------------------------------------------------------===// +// spv.AtomicXor +//===----------------------------------------------------------------------===// + +func @atomic_xor(%ptr : !spv.ptr, %value : i32) -> i32 { + // CHECK: spv.AtomicXor "Workgroup" "None" %{{.*}}, %{{.*}} : !spv.ptr + %0 = spv.AtomicXor "Workgroup" "None" %ptr, %value : !spv.ptr + return %0 : i32 +} diff --git a/mlir/utils/spirv/define_inst.sh b/mlir/utils/spirv/define_inst.sh index 3508c4f..f11078a 100755 --- a/mlir/utils/spirv/define_inst.sh +++ b/mlir/utils/spirv/define_inst.sh @@ -35,13 +35,13 @@ file_name=$1 inst_category=$2 case $inst_category in - Op | ArithmeticOp | LogicalOp | CastOp | ControlFlowOp | StructureOp) + Op | ArithmeticOp | LogicalOp | CastOp | ControlFlowOp | StructureOp | AtomicUpdateOp | AtomicUpdateWithValueOp) ;; *) echo "Usage : " $0 " ()*" echo " is the file name of MLIR SPIR-V op definitions spec" echo " must be one of " \ - "(Op|ArithmeticOp|LogicalOp|CastOp|ControlFlowOp|StructureOp)" + "(Op|ArithmeticOp|LogicalOp|CastOp|ControlFlowOp|StructureOp|AtomicUpdateOp)" exit 1; ;; esac diff --git a/mlir/utils/spirv/gen_spirv_dialect.py b/mlir/utils/spirv/gen_spirv_dialect.py index bf4886d..be7116c 100755 --- a/mlir/utils/spirv/gen_spirv_dialect.py +++ b/mlir/utils/spirv/gen_spirv_dialect.py @@ -353,7 +353,7 @@ def map_spec_operand_to_ods_argument(operand): # and 'IdScope' given that they should be generated from OpConstant. assert quantifier == '', ('unexpected to have optional/variadic memory ' 'semantics or scope ') - arg_type = 'I32' + arg_type = 'SPV_' + kind[2:] + 'Attr' elif kind == 'LiteralInteger': if quantifier == '': arg_type = 'I32Attr' @@ -651,8 +651,9 @@ def update_td_op_definitions(path, instructions, docs, filter_list, instruction = next( inst for inst in instructions if inst['opname'] == opname) op_defs.append( - get_op_definition(instruction, docs[opname], - op_info_dict.get(opname, {}))) + get_op_definition( + instruction, docs[opname], + op_info_dict.get(opname, {'inst_category': inst_category}))) except StopIteration: # This is an op added by us; use the existing ODS definition. op_defs.append(name_op_map[opname]) -- 2.7.4