blob: 71ccee5bfea19c6eb44acbfd2150c147ce7fe58a [file] [log] [blame]
//===-- SPIRVAtomicOps.td - MLIR SPIR-V Atomic Ops ---------*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains atomic ops for the SPIR-V dialect. It corresponds to
// "3.32.18. Atomic Instructions" of the SPIR-V specification.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_DIALECT_SPIRV_IR_ATOMIC_OPS
#define MLIR_DIALECT_SPIRV_IR_ATOMIC_OPS
class SPV_AtomicUpdateOp<string mnemonic, list<OpTrait> traits = []> :
SPV_Op<mnemonic, traits> {
let parser = [{ return ::parseAtomicUpdateOp(parser, result, false); }];
let printer = [{ return ::printAtomicUpdateOp(getOperation(), p); }];
let verifier = [{ return ::verifyAtomicUpdateOp<IntegerType>(getOperation()); }];
let arguments = (ins
SPV_AnyPtr:$pointer,
SPV_ScopeAttr:$memory_scope,
SPV_MemorySemanticsAttr:$semantics
);
let results = (outs
SPV_Integer:$result
);
}
class SPV_AtomicUpdateWithValueOp<string mnemonic, list<OpTrait> traits = []> :
SPV_Op<mnemonic, traits> {
let parser = [{ return ::parseAtomicUpdateOp(parser, result, true); }];
let printer = [{ return ::printAtomicUpdateOp(getOperation(), p); }];
let verifier = [{ return ::verifyAtomicUpdateOp<IntegerType>(getOperation()); }];
let arguments = (ins
SPV_AnyPtr:$pointer,
SPV_ScopeAttr:$memory_scope,
SPV_MemorySemanticsAttr:$semantics,
SPV_Integer:$value
);
let results = (outs
SPV_Integer:$result
);
let builders = [
OpBuilder<(ins "Value":$pointer, "::mlir::spirv::Scope":$scope,
"::mlir::spirv::MemorySemantics":$memory, "Value":$value),
[{build($_builder, $_state, value.getType(), pointer, scope, memory, value);}]>
];
}
// -----
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 = [{
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 instructions 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.
<!-- End of AutoGen section -->
```
scope ::= `"CrossDevice"` | `"Device"` | `"Workgroup"` | ...
memory-semantics ::= `"None"` | `"Acquire"` | "Release"` | ...
atomic-and-op ::=
`spv.AtomicAnd` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicAnd "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
def SPV_AtomicCompareExchangeOp : SPV_Op<"AtomicCompareExchange", []> {
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 from Value only if Original Value equals Comparator,
and
3) store the New Value back through Pointer'only if 'Original Value
equaled Comparator.
The instruction's result is the Original Value.
Result Type must be an integer type scalar.
Use Equal for the memory semantics of this instruction when Value and
Original Value compare equal.
Use Unequal for the memory semantics of this instruction when Value and
Original Value compare unequal. Unequal must not be set to Release or
Acquire and Release. In addition, Unequal cannot be set to a stronger
memory-order then Equal.
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. This type
must also match the type of Comparator.
Memory is a memory Scope.
<!-- End of AutoGen section -->
```
atomic-compare-exchange-op ::=
`spv.AtomicCompareExchange` scope memory-semantics memory-semantics
ssa-use `,` ssa-use `,` ssa-use
`:` spv-pointer-type
```mlir
#### Example:
```
%0 = spv.AtomicCompareExchange "Workgroup" "Acquire" "None"
%pointer, %value, %comparator
: !spv.ptr<i32, WorkGroup>
```
}];
let arguments = (ins
SPV_AnyPtr:$pointer,
SPV_ScopeAttr:$memory_scope,
SPV_MemorySemanticsAttr:$equal_semantics,
SPV_MemorySemanticsAttr:$unequal_semantics,
SPV_Integer:$value,
SPV_Integer:$comparator
);
let results = (outs
SPV_Integer:$result
);
let parser = [{ return ::parseAtomicCompareExchangeImpl(parser, result); }];
let printer = [{ return ::printAtomicCompareExchangeImpl(*this, p); }];
let verifier = [{ return ::verifyAtomicCompareExchangeImpl(*this); }];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-compare-exchange-weak-op ::=
`spv.AtomicCompareExchangeWeak` scope memory-semantics memory-semantics
ssa-use `,` ssa-use `,` ssa-use
`:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicCompareExchangeWeak "Workgroup" "Acquire" "None"
%pointer, %value, %comparator
: !spv.ptr<i32, WorkGroup>
```
}];
let availability = [
MinVersion<SPV_V_1_0>,
MaxVersion<SPV_V_1_3>,
Extension<[]>,
Capability<[SPV_C_Kernel]>
];
let arguments = (ins
SPV_AnyPtr:$pointer,
SPV_ScopeAttr:$memory_scope,
SPV_MemorySemanticsAttr:$equal_semantics,
SPV_MemorySemanticsAttr:$unequal_semantics,
SPV_Integer:$value,
SPV_Integer:$comparator
);
let results = (outs
SPV_Integer:$result
);
let parser = [{ return ::parseAtomicCompareExchangeImpl(parser, result); }];
let printer = [{ return ::printAtomicCompareExchangeImpl(*this, p); }];
let verifier = [{ return ::verifyAtomicCompareExchangeImpl(*this); }];
}
// -----
def SPV_AtomicExchangeOp : SPV_Op<"AtomicExchange", []> {
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 from copying Value, and
3) store the New Value back through Pointer.
The instruction's result is the Original Value.
Result Type must be a scalar of integer type or floating-point type.
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 is a memory Scope.
<!-- End of AutoGen section -->
```
atomic-exchange-op ::=
`spv.AtomicCompareExchange` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```mlir
#### Example:
```
%0 = spv.AtomicExchange "Workgroup" "Acquire" %pointer, %value,
: !spv.ptr<i32, WorkGroup>
```
}];
let arguments = (ins
SPV_AnyPtr:$pointer,
SPV_ScopeAttr:$memory_scope,
SPV_MemorySemanticsAttr:$semantics,
SPV_Numerical:$value
);
let results = (outs
SPV_Numerical:$result
);
}
// -----
def SPV_AtomicFAddEXTOp : SPV_Op<"AtomicFAddEXT", []> {
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 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<f32, StorageBuffer>
```mlir
}];
let availability = [
MinVersion<SPV_V_1_0>,
MaxVersion<SPV_V_1_5>,
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<FloatType>(getOperation()); }];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-iadd-op ::=
`spv.AtomicIAdd` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicIAdd "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-idecrement-op ::=
`spv.AtomicIDecrement` scope memory-semantics ssa-use
`:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicIDecrement "Device" "None" %pointer :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-iincrement-op ::=
`spv.AtomicIIncrement` scope memory-semantics ssa-use
`:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicIncrement "Device" "None" %pointer :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-isub-op ::=
`spv.AtomicISub` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicISub "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-or-op ::=
`spv.AtomicOr` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicOr "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-smax-op ::=
`spv.AtomicSMax` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicSMax "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-smin-op ::=
`spv.AtomicSMin` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicSMin "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
def SPV_AtomicUMaxOp : SPV_AtomicUpdateWithValueOp<"AtomicUMax", [UnsignedOp]> {
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.
<!-- End of AutoGen section -->
```
atomic-umax-op ::=
`spv.AtomicUMax` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicUMax "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
def SPV_AtomicUMinOp : SPV_AtomicUpdateWithValueOp<"AtomicUMin", [UnsignedOp]> {
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.
<!-- End of AutoGen section -->
```
atomic-umin-op ::=
`spv.AtomicUMin` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicUMin "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
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.
<!-- End of AutoGen section -->
```
atomic-xor-op ::=
`spv.AtomicXor` scope memory-semantics
ssa-use `,` ssa-use `:` spv-pointer-type
```
#### Example:
```mlir
%0 = spv.AtomicXor "Device" "None" %pointer, %value :
!spv.ptr<i32, StorageBuffer>
```
}];
}
// -----
#endif // MLIR_DIALECT_SPIRV_IR_ATOMIC_OPS