//===-- SPIRVGroupOps.td - MLIR SPIR-V (Sub)Group 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 group and subgroup ops for the SPIR-V dialect. It
// corresponds to "3.32.21. Group and Subgroup Instructions" of the SPIR-V
// specification.
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_DIALECT_SPIRV_IR_GROUP_OPS
#define MLIR_DIALECT_SPIRV_IR_GROUP_OPS

// -----

def SPV_GroupBroadcastOp : SPV_Op<"GroupBroadcast",
  [NoSideEffect, AllTypesMatch<["value", "result"]>]> {
  let summary = [{
    Broadcast the Value of the invocation identified by the local id LocalId
    to the result of all invocations in the group.
  }];

  let description = [{
    All invocations of this module within Execution must reach this point of
    execution.

    Behavior is undefined if this instruction is used in control flow that
    is non-uniform within Execution.

    Result Type  must be a scalar or vector of floating-point type, integer
    type, or Boolean type.

    Execution must be Workgroup or Subgroup Scope.

     The type of Value must be the same as Result Type.

    LocalId must be an integer datatype. It can be a scalar, or a vector
    with 2 components or a vector with 3 components. LocalId must be the
    same for all invocations in the group.

    <!-- End of AutoGen section -->

    ```
    scope ::= `"Workgroup"` | `"Subgroup"`
    integer-float-scalar-vector-type ::= integer-type | float-type |
                               `vector<` integer-literal `x` integer-type `>` |
                               `vector<` integer-literal `x` float-type `>`
    localid-type ::= integer-type |
                   `vector<` integer-literal `x` integer-type `>`
    group-broadcast-op ::= ssa-id `=` `spv.GroupBroadcast` scope ssa_use,
                   ssa_use `:` integer-float-scalar-vector-type `,` localid-type
    ```mlir

    #### Example:

    ```
    %scalar_value = ... : f32
    %vector_value = ... : vector<4xf32>
    %scalar_localid = ... : i32
    %vector_localid = ... : vector<3xi32>
    %0 = spv.GroupBroadcast "Subgroup" %scalar_value, %scalar_localid : f32, i32
    %1 = spv.GroupBroadcast "Workgroup" %vector_value, %vector_localid :
      vector<4xf32>, vector<3xi32>
    ```
  }];

  let availability = [
    MinVersion<SPV_V_1_0>,
    MaxVersion<SPV_V_1_5>,
    Extension<[]>,
    Capability<[SPV_C_Groups]>
  ];

  let arguments = (ins
    SPV_ScopeAttr:$execution_scope,
    SPV_Type:$value,
    SPV_ScalarOrVectorOf<SPV_Integer>:$localid
  );

  let results = (outs
    SPV_Type:$result
  );

  let assemblyFormat = [{
    $execution_scope operands attr-dict `:` type($value) `,` type($localid)
  }];
}

// -----

def SPV_SubgroupBallotKHROp : SPV_Op<"SubgroupBallotKHR", []> {
  let summary = "See extension SPV_KHR_shader_ballot";

  let description = [{
    Computes a bitfield value combining the Predicate value from all invocations
    in the current Subgroup that execute the same dynamic instance of this
    instruction. The bit is set to one if the corresponding invocation is active
    and the predicate is evaluated to true; otherwise, it is set to zero.

    Predicate must be a Boolean type.

    Result Type must be a 4 component vector of 32 bit integer types.

    Result is a set of bitfields where the first invocation is represented in bit
    0 of the first vector component and the last (up to SubgroupSize) is the
    higher bit number of the last bitmask needed to represent all bits of the
    subgroup invocations.

    <!-- End of AutoGen section -->

    ```
    subgroup-ballot-op ::= ssa-id `=` `spv.SubgroupBallotKHR`
                                ssa-use `:` `vector` `<` 4 `x` `i32` `>`
    ```

    #### Example:

    ```mlir
    %0 = spv.SubgroupBallotKHR %predicate : vector<4xi32>
    ```
  }];

  let availability = [
    MinVersion<SPV_V_1_0>,
    MaxVersion<SPV_V_1_5>,
    Extension<[SPV_KHR_shader_ballot]>,
    Capability<[SPV_C_SubgroupBallotKHR]>
  ];

  let arguments = (ins
    SPV_Bool:$predicate
  );

  let results = (outs
    SPV_Int32Vec4:$result
  );

  let hasVerifier = 0;

  let assemblyFormat = "$predicate attr-dict `:` type($result)";
}

// -----

def SPV_SubgroupBlockReadINTELOp : SPV_Op<"SubgroupBlockReadINTEL", []> {
  let summary = "See extension SPV_INTEL_subgroups";

  let description = [{
    Reads one or more components of Result data for each invocation in the
    subgroup from the specified Ptr as a block operation.

    The data is read strided, so the first value read is:
    Ptr[ SubgroupLocalInvocationId ]

    and the second value read is:
    Ptr[ SubgroupLocalInvocationId + SubgroupMaxSize ]
    etc.

    Result Type may be a scalar or vector type, and its component type must be
    equal to the type pointed to by Ptr.

    The type of Ptr must be a pointer type, and must point to a scalar type.

    <!-- End of AutoGen section -->

    ```
    subgroup-block-read-INTEL-op ::= ssa-id `=` `spv.SubgroupBlockReadINTEL`
                                storage-class ssa_use `:` spirv-element-type
    ```mlir

    #### Example:

    ```
    %0 = spv.SubgroupBlockReadINTEL "StorageBuffer" %ptr : i32
    ```
  }];

  let availability = [
    MinVersion<SPV_V_1_0>,
    MaxVersion<SPV_V_1_5>,
    Extension<[SPV_INTEL_subgroups]>,
    Capability<[SPV_C_SubgroupBufferBlockIOINTEL]>
  ];

  let arguments = (ins
    SPV_AnyPtr:$ptr
  );

  let results = (outs
    SPV_Type:$value
  );
}

// -----

def SPV_SubgroupBlockWriteINTELOp : SPV_Op<"SubgroupBlockWriteINTEL", []> {
  let summary = "See extension SPV_INTEL_subgroups";

  let description = [{
    Writes one or more components of Data for each invocation in the subgroup
    from the specified Ptr as a block operation.

    The data is written strided, so the first value is written to:
    Ptr[ SubgroupLocalInvocationId ]

    and the second value written is:
    Ptr[ SubgroupLocalInvocationId + SubgroupMaxSize ]
    etc.

    The type of Ptr must be a pointer type, and must point to a scalar type.

    The component type of Data must be equal to the type pointed to by Ptr.

    <!-- End of AutoGen section -->

    ```
    subgroup-block-write-INTEL-op ::= ssa-id `=` `spv.SubgroupBlockWriteINTEL`
                      storage-class ssa_use `,` ssa-use `:` spirv-element-type
    ```mlir

    #### Example:

    ```
    spv.SubgroupBlockWriteINTEL "StorageBuffer" %ptr, %value : i32
    ```
  }];

  let availability = [
    MinVersion<SPV_V_1_0>,
    MaxVersion<SPV_V_1_5>,
    Extension<[SPV_INTEL_subgroups]>,
    Capability<[SPV_C_SubgroupBufferBlockIOINTEL]>
  ];

  let arguments = (ins
    SPV_AnyPtr:$ptr,
    SPV_Type:$value
  );

  let results = (outs);
}

// -----

#endif // MLIR_DIALECT_SPIRV_IR_GROUP_OPS
