//===-- SPIRVBitOps.td - MLIR SPIR-V Bit 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 bit ops for the SPIR-V dialect. It corresponds
// to "3.32.13. Bit Instructions" of the SPIR-V specification.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_DIALECT_SPIRV_IR_BIT_OPS
#define MLIR_DIALECT_SPIRV_IR_BIT_OPS
include "mlir/Dialect/SPIRV/IR/SPIRVBase.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
class SPIRV_BitBinaryOp<string mnemonic, list<Trait> traits = []> :
// All the operands type used in bit instructions are SPIRV_Integer.
SPIRV_BinaryOp<mnemonic, SPIRV_Integer, SPIRV_Integer,
!listconcat(traits,
[Pure, SameOperandsAndResultType])> {
let assemblyFormat = "operands attr-dict `:` type($result)";
}
class SPIRV_BitFieldExtractOp<string mnemonic, list<Trait> traits = []> :
SPIRV_Op<mnemonic, !listconcat(traits,
[Pure, AllTypesMatch<["base", "result"]>])> {
let arguments = (ins
SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$base,
SPIRV_Integer:$offset,
SPIRV_Integer:$count
);
let results = (outs
SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$result
);
let hasVerifier = 0;
let assemblyFormat = [{
operands attr-dict `:` type($base) `,` type($offset) `,` type($count)
}];
}
class SPIRV_BitUnaryOp<string mnemonic, list<Trait> traits = []> :
SPIRV_UnaryOp<mnemonic, SPIRV_Integer, SPIRV_Integer,
!listconcat(traits,
[Pure, SameOperandsAndResultType])>;
class SPIRV_ShiftOp<string mnemonic, list<Trait> traits = []> :
SPIRV_BinaryOp<mnemonic, SPIRV_Integer, SPIRV_Integer,
!listconcat(traits,
[Pure, SameOperandsAndResultShape,
AllTypesMatch<["operand1", "result"]>])> {
let assemblyFormat = [{
operands attr-dict `:` type($operand1) `,` type($operand2)
}];
let hasVerifier = 1;
}
// -----
def SPIRV_BitCountOp : SPIRV_BitUnaryOp<"BitCount", []> {
let summary = "Count the number of set bits in an object.";
let description = [{
Results are computed per component.
Result Type must be a scalar or vector of integer type. The components
must be wide enough to hold the unsigned Width of Base as an unsigned
value. That is, no sign bit is needed or counted when checking for a
wide enough result width.
Base must be a scalar or vector of integer type. It must have the same
number of components as Result Type.
The result is the unsigned value that is the number of bits in Base that
are 1.
#### Example:
```mlir
%2 = spirv.BitCount %0: i32
%3 = spirv.BitCount %1: vector<4xi32>
```
}];
}
// -----
def SPIRV_BitFieldInsertOp : SPIRV_Op<"BitFieldInsert",
[Pure, AllTypesMatch<["base", "insert", "result"]>]> {
let summary = [{
Make a copy of an object, with a modified bit field that comes from
another object.
}];
let description = [{
Results are computed per component.
Result Type must be a scalar or vector of integer type.
The type of Base and Insert must be the same as Result Type.
Any result bits numbered outside [Offset, Offset + Count - 1]
(inclusive) will come from the corresponding bits in Base.
Any result bits numbered in [Offset, Offset + Count - 1] come, in
order, from the bits numbered [0, Count - 1] of Insert.
Count must be an integer type scalar. Count is the number of bits taken
from Insert. It will be consumed as an unsigned value. Count can be 0,
in which case the result will be Base.
Offset must be an integer type scalar. Offset is the lowest-order bit
of the bit field. It will be consumed as an unsigned value.
The resulting value is undefined if Count or Offset or their sum is
greater than the number of bits in the result.
#### Example:
```mlir
%0 = spirv.BitFieldInsert %base, %insert, %offset, %count : vector<3xi32>, i8, i8
```
}];
let availability = [
MinVersion<SPIRV_V_1_0>,
MaxVersion<SPIRV_V_1_6>,
Extension<[]>,
Capability<[SPIRV_C_BitInstructions, SPIRV_C_Shader]>
];
let arguments = (ins
SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$base,
SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$insert,
SPIRV_Integer:$offset,
SPIRV_Integer:$count
);
let results = (outs
SPIRV_ScalarOrVectorOf<SPIRV_Integer>:$result
);
let hasVerifier = 0;
let assemblyFormat = [{
operands attr-dict `:` type($base) `,` type($offset) `,` type($count)
}];
}
// -----
def SPIRV_BitFieldSExtractOp : SPIRV_BitFieldExtractOp<"BitFieldSExtract",
[SignedOp]> {
let summary = "Extract a bit field from an object, with sign extension.";
let description = [{
Results are computed per component.
Result Type must be a scalar or vector of integer type.
The type of Base must be the same as Result Type.
If Count is greater than 0: The bits of Base numbered in [Offset, Offset
+ Count - 1] (inclusive) become the bits numbered [0, Count - 1] of the
result. The remaining bits of the result will all be the same as bit
Offset + Count - 1 of Base.
Count must be an integer type scalar. Count is the number of bits
extracted from Base. It will be consumed as an unsigned value. Count can
be 0, in which case the result will be 0.
Offset must be an integer type scalar. Offset is the lowest-order bit
of the bit field to extract from Base. It will be consumed as an
unsigned value.
The resulting value is undefined if Count or Offset or their sum is
greater than the number of bits in the result.
#### Example:
```mlir
%0 = spirv.BitFieldSExtract %base, %offset, %count : vector<3xi32>, i8, i8
```
}];
let availability = [
MinVersion<SPIRV_V_1_0>,
MaxVersion<SPIRV_V_1_6>,
Extension<[]>,
Capability<[SPIRV_C_BitInstructions, SPIRV_C_Shader]>
];
}
// -----
def SPIRV_BitFieldUExtractOp : SPIRV_BitFieldExtractOp<"BitFieldUExtract",
[UnsignedOp]> {
let summary = "Extract a bit field from an object, without sign extension.";
let description = [{
The semantics are the same as with OpBitFieldSExtract with the exception
that there is no sign extension. The remaining bits of the result will
all be 0.
#### Example:
```mlir
%0 = spirv.BitFieldUExtract %base, %offset, %count : vector<3xi32>, i8, i8
```
}];
let availability = [
MinVersion<SPIRV_V_1_0>,
MaxVersion<SPIRV_V_1_6>,
Extension<[]>,
Capability<[SPIRV_C_BitInstructions, SPIRV_C_Shader]>
];
}
// -----
def SPIRV_BitReverseOp : SPIRV_BitUnaryOp<"BitReverse", []> {
let summary = "Reverse the bits in an object.";
let description = [{
Results are computed per component.
Result Type must be a scalar or vector of integer type.
The type of Base must be the same as Result Type.
The bit-number n of the result will be taken from bit-number Width - 1 -
n of Base, where Width is the OpTypeInt operand of the Result Type.
#### Example:
```mlir
%2 = spirv.BitReverse %0 : i32
%3 = spirv.BitReverse %1 : vector<4xi32>
```
}];
let availability = [
MinVersion<SPIRV_V_1_0>,
MaxVersion<SPIRV_V_1_6>,
Extension<[]>,
Capability<[SPIRV_C_BitInstructions, SPIRV_C_Shader]>
];
}
// -----
def SPIRV_BitwiseAndOp : SPIRV_BitBinaryOp<"BitwiseAnd",
[Commutative, UsableInSpecConstantOp]> {
let summary = [{
Result is 1 if both Operand 1 and Operand 2 are 1. Result is 0 if either
Operand 1 or Operand 2 are 0.
}];
let description = [{
Results are computed per component, and within each component, per bit.
Result Type must be a scalar or vector of integer type. The type of
Operand 1 and Operand 2 must be a scalar or vector of integer type.
They must have the same number of components as Result Type. They must
have the same component width as Result Type.
#### Example:
```mlir
%2 = spirv.BitwiseAnd %0, %1 : i32
%2 = spirv.BitwiseAnd %0, %1 : vector<4xi32>
```
}];
let hasFolder = 1;
}
// -----
def SPIRV_BitwiseOrOp : SPIRV_BitBinaryOp<"BitwiseOr",
[Commutative, UsableInSpecConstantOp]> {
let summary = [{
Result is 1 if either Operand 1 or Operand 2 is 1. Result is 0 if both
Operand 1 and Operand 2 are 0.
}];
let description = [{
Results are computed per component, and within each component, per bit.
Result Type must be a scalar or vector of integer type. The type of
Operand 1 and Operand 2 must be a scalar or vector of integer type.
They must have the same number of components as Result Type. They must
have the same component width as Result Type.
#### Example:
```mlir
%2 = spirv.BitwiseOr %0, %1 : i32
%2 = spirv.BitwiseOr %0, %1 : vector<4xi32>
```
}];
let hasFolder = 1;
}
// -----
def SPIRV_BitwiseXorOp : SPIRV_BitBinaryOp<"BitwiseXor",
[Commutative, UsableInSpecConstantOp]> {
let summary = [{
Result is 1 if exactly one of Operand 1 or Operand 2 is 1. Result is 0
if Operand 1 and Operand 2 have the same value.
}];
let description = [{
Results are computed per component, and within each component, per bit.
Result Type must be a scalar or vector of integer type. The type of
Operand 1 and Operand 2 must be a scalar or vector of integer type.
They must have the same number of components as Result Type. They must
have the same component width as Result Type.
#### Example:
```mlir
%2 = spirv.BitwiseXor %0, %1 : i32
%2 = spirv.BitwiseXor %0, %1 : vector<4xi32>
```
}];
let hasFolder = 1;
}
// -----
def SPIRV_ShiftLeftLogicalOp : SPIRV_ShiftOp<"ShiftLeftLogical",
[UsableInSpecConstantOp]> {
let summary = [{
Shift the bits in Base left by the number of bits specified in Shift.
The least-significant bits are zero filled.
}];
let description = [{
Result Type must be a scalar or vector of integer type.
The type of each Base and Shift must be a scalar or vector of integer
type. Base and Shift must have the same number of components. The
number of components and bit width of the type of Base must be the same
as in Result Type.
Shift is treated as unsigned. The result is undefined if Shift is
greater than or equal to the bit width of the components of Base.
The number of components and bit width of Result Type must match those
Base type. All types must be integer types.
Results are computed per component.
#### Example:
```mlir
%2 = spirv.ShiftLeftLogical %0, %1 : i32, i16
%5 = spirv.ShiftLeftLogical %3, %4 : vector<3xi32>, vector<3xi16>
```
}];
let hasFolder = 1;
}
// -----
def SPIRV_ShiftRightArithmeticOp : SPIRV_ShiftOp<"ShiftRightArithmetic",
[UsableInSpecConstantOp]> {
let summary = [{
Shift the bits in Base right by the number of bits specified in Shift.
The most-significant bits are filled with the sign bit from Base.
}];
let description = [{
Result Type must be a scalar or vector of integer type.
The type of each Base and Shift must be a scalar or vector of integer
type. Base and Shift must have the same number of components. The
number of components and bit width of the type of Base must be the same
as in Result Type.
Shift is treated as unsigned. The result is undefined if Shift is
greater than or equal to the bit width of the components of Base.
Results are computed per component.
#### Example:
```mlir
%2 = spirv.ShiftRightArithmetic %0, %1 : i32, i16
%5 = spirv.ShiftRightArithmetic %3, %4 : vector<3xi32>, vector<3xi16>
```
}];
let hasFolder = 1;
}
// -----
def SPIRV_ShiftRightLogicalOp : SPIRV_ShiftOp<"ShiftRightLogical",
[UsableInSpecConstantOp]> {
let summary = [{
Shift the bits in Base right by the number of bits specified in Shift.
The most-significant bits are zero filled.
}];
let description = [{
Result Type must be a scalar or vector of integer type.
The type of each Base and Shift must be a scalar or vector of integer
type. Base and Shift must have the same number of components. The
number of components and bit width of the type of Base must be the same
as in Result Type.
Shift is consumed as an unsigned integer. The result is undefined if
Shift is greater than or equal to the bit width of the components of
Base.
Results are computed per component.
#### Example:
```mlir
%2 = spirv.ShiftRightLogical %0, %1 : i32, i16
%5 = spirv.ShiftRightLogical %3, %4 : vector<3xi32>, vector<3xi16>
```
}];
let hasFolder = 1;
}
// -----
def SPIRV_NotOp : SPIRV_BitUnaryOp<"Not", [UsableInSpecConstantOp]> {
let summary = "Complement the bits of Operand.";
let description = [{
Results are computed per component, and within each component, per bit.
Result Type must be a scalar or vector of integer type.
Operand's type must be a scalar or vector of integer type. It must
have the same number of components as Result Type. The component width
must equal the component width in Result Type.
#### Example:
```mlir
%2 = spirv.Not %0 : i32
%3 = spirv.Not %1 : vector<4xi32>
```
}];
let hasFolder = 1;
}
#endif // MLIR_DIALECT_SPIRV_IR_BIT_OPS