Skip to content

[mlir][spirv] Add Element Binary operators to TOSA Ext Inst Set#179627

Open
davidegrohmann wants to merge 1 commit intollvm:mainfrom
davidegrohmann:mlir-spv-tosa-ext-inst-set-001000-part5
Open

[mlir][spirv] Add Element Binary operators to TOSA Ext Inst Set#179627
davidegrohmann wants to merge 1 commit intollvm:mainfrom
davidegrohmann:mlir-spv-tosa-ext-inst-set-001000-part5

Conversation

@davidegrohmann
Copy link
Contributor

This patch introduces the following element binary operators:

  • spirv.Tosa.Add
  • spirv.Tosa.ArithmeticRightShift
  • spirv.Tosa.BitwiseAnd
  • spirv.Tosa.BitwiseOr
  • spirv.Tosa.BitwiseXor
  • spirv.Tosa.IntDiv
  • spirv.Tosa.LogicalAnd
  • spirv.Tosa.LogicalLeftShift
  • spirv.Tosa.LogicalRightShift
  • spirv.Tosa.LogicalOr
  • spirv.Tosa.LogicalXor
  • spirv.Tosa.Maximum
  • spirv.Tosa.Minimum
  • spirv.Tosa.Mul
  • spirv.Tosa.Pow
  • spirv.Tosa.Sub
  • spirv.Tosa.Table

Also dialect and serialization round-trip tests have been added.

Change-Id: I477dec54212d4201230ba63753c4138fdcb83915

@llvmbot
Copy link
Member

llvmbot commented Feb 4, 2026

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-spirv

Author: Davide Grohmann (davidegrohmann)

Changes

This patch introduces the following element binary operators:

  • spirv.Tosa.Add
  • spirv.Tosa.ArithmeticRightShift
  • spirv.Tosa.BitwiseAnd
  • spirv.Tosa.BitwiseOr
  • spirv.Tosa.BitwiseXor
  • spirv.Tosa.IntDiv
  • spirv.Tosa.LogicalAnd
  • spirv.Tosa.LogicalLeftShift
  • spirv.Tosa.LogicalRightShift
  • spirv.Tosa.LogicalOr
  • spirv.Tosa.LogicalXor
  • spirv.Tosa.Maximum
  • spirv.Tosa.Minimum
  • spirv.Tosa.Mul
  • spirv.Tosa.Pow
  • spirv.Tosa.Sub
  • spirv.Tosa.Table

Also dialect and serialization round-trip tests have been added.

Change-Id: I477dec54212d4201230ba63753c4138fdcb83915


Patch is 92.84 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/179627.diff

5 Files Affected:

  • (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td (+804)
  • (modified) mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaTypes.td (+11)
  • (modified) mlir/test/Dialect/SPIRV/IR/tosa-ops-verification.mlir (+93)
  • (modified) mlir/test/Dialect/SPIRV/IR/tosa-ops.mlir (+245)
  • (modified) mlir/test/Target/SPIRV/tosa-ops.mlir (+442)
diff --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td
index 61e8ea2c9ebc8..d81985fb9a40f 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td
@@ -863,4 +863,808 @@ def SPIRV_TosaTanhOp : SPIRV_TosaOpWithResult<"Tanh", 13, [Pure,
 }
 
 
+def SPIRV_TosaAddOp : SPIRV_TosaOpWithResult<"Add", 14, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>]> {
+  let summary = "Addition operator.";
+
+  let description = [{
+    Elementwise Addition of input1 and input2. Axis of size 1 will be broadcast,
+    as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_add
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_add
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.Add %arg0, %arg1 : !spirv.arm.tensor<4x7x3x10xi32>, !spirv.arm.tensor<4x7x3x1xi32> -> !spirv.arm.tensor<4x7x3x10xi32>
+    %0 = spirv.Tosa.Add %arg0, %arg1 : !spirv.arm.tensor<26x37x18xf16>, !spirv.arm.tensor<1x37x18xf16> -> !spirv.arm.tensor<26x37x18xf16>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_TosaNumerical_TensorArm: $input1,
+    SPIRV_TosaNumerical_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaNumerical_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaArithmeticRightShiftOp : SPIRV_TosaOpWithResult<"ArithmeticRightShift", 15, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>]> {
+  let summary = "Arithmetic Right Shift.";
+
+  let description = [{
+    Elementwise Arithmetic Right Shift of input1 by the amount specified in
+    input2. Axis of size 1 will be broadcast, as necessary. Rank of input tensors
+    must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_arithmetic_right_shift
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_arithmetic_right_shift
+
+    #### Example:
+    ```mlir
+    %1 = spirv.Tosa.ArithmeticRightShift round = true, %arg0, %arg1 : !spirv.arm.tensor<1x47x22xi16>, !spirv.arm.tensor<49x47x22xi16> -> !spirv.arm.tensor<49x47x22xi16>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_BoolConstAttr: $round,
+    SPIRV_TosaInteger_TensorArm: $input1,
+    SPIRV_TosaInteger_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaInteger_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    `round` `=` $round `,`
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaBitwiseAndOp : SPIRV_TosaOpWithResult<"BitwiseAnd", 16, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>]> {
+  let summary = "Bitwise AND operator.";
+
+  let description = [{
+    Elementwise Bitwise AND of input1 and input2. Axis of size 1
+    will be broadcast as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_bitwise_and
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_bitwise_and
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.BitwiseAnd %arg0, %arg1 : !spirv.arm.tensor<4x1x7x12xi16>, !spirv.arm.tensor<4x13x7x12xi16> -> !spirv.arm.tensor<4x13x7x12xi16>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_TosaInteger_TensorArm: $input1,
+    SPIRV_TosaInteger_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaInteger_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaBitwiseOrOp : SPIRV_TosaOpWithResult<"BitwiseOr", 17, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>]> {
+  let summary = "Bitwise OR operator.";
+
+  let description = [{
+    Elementwise Bitwise OR of input1 and input2. Axis of size 1 will be
+    broadcast as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_bitwise_or
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_bitwise_or
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.BitwiseOr %arg0, %arg1 : !spirv.arm.tensor<11x30x23xi32>, !spirv.arm.tensor<1x30x23xi32> -> !spirv.arm.tensor<11x30x23xi32>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_TosaInteger_TensorArm: $input1,
+    SPIRV_TosaInteger_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaInteger_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaBitwiseXorOp : SPIRV_TosaOpWithResult<"BitwiseXor", 18, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>]> {
+  let summary = "Bitwise XOR operator.";
+
+  let description = [{
+    Elementwise Bitwise XOR of input1 and input2. Axis of size 1 will be
+    broadcast as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_bitwise_xor
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_bitwise_xor
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.BitwiseXor %arg0, %arg1 : !spirv.arm.tensor<4x8x13x9xi16>, !spirv.arm.tensor<4x8x1x9xi16> -> !spirv.arm.tensor<4x8x13x9xi16>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_TosaInteger_TensorArm: $input1,
+    SPIRV_TosaInteger_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaInteger_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaIntDivOp : SPIRV_TosaOpWithResult<"IntDiv", 19, [Pure]> {
+  let summary = "Integer Divide operator.";
+
+  let description = [{
+    Elementwise Integer Divide of input1 by input2. Axis of size 1 will be
+    broadcast as necessary. Rank of input tensors must match.
+
+    The result of the divide is truncated towards zero. Expected use is for
+    operations on non-scaled integers. Floating point divide should use
+    `spirv.Tosa.Reciprocal` and `spirv.Tosa.Mul`. Quantized integer divide
+    should use `spirv.Tosa.Table`(for $ 1/x $) and `spirv.Tosa.Mul`.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_intdiv
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_intdiv
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.IntDiv %arg0, %arg1 : !spirv.arm.tensor<1x65533x1xi32>, !spirv.arm.tensor<2x65533x1xi32> -> !spirv.arm.tensor<2x65533x1xi32>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_Int32_TensorArm: $input1,
+    SPIRV_Int32_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_Int32_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaLogicalAndOp : SPIRV_TosaOpWithResult<"LogicalAnd", 20, [Pure]> {
+  let summary = "Logical AND operator.";
+
+  let description = [{
+    Elementwise Logical AND of input1 and input2. Axis of size 1 will be
+    broadcast, as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_logical_and
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_logical_and
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.LogicalAnd %arg0, %arg1 : !spirv.arm.tensor<2x1x7x11xi1>, !spirv.arm.tensor<2x4x7x11xi1> -> !spirv.arm.tensor<2x4x7x11xi1>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_Bool_TensorArm: $input1,
+    SPIRV_Bool_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_Bool_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaLogicalLeftShiftOp : SPIRV_TosaOpWithResult<"LogicalLeftShift", 21, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>]> {
+  let summary = "Logical Left Shift operator.";
+
+  let description = [{
+    Elementwise Logical Left Shift of input1 by the amount specified in input2.
+    Axis of size 1 will be broadcast, as necessary.
+    Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_logical_left_shift
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_logical_left_shift
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.LogicalLeftShift %arg0, %arg1 : !spirv.arm.tensor<7x1x11x4xi8>, !spirv.arm.tensor<7x8x11x4xi8> -> !spirv.arm.tensor<7x8x11x4xi8>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_TosaInteger_TensorArm: $input1,
+    SPIRV_TosaInteger_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaInteger_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaLogicalRightShiftOp : SPIRV_TosaOpWithResult<"LogicalRightShift", 22, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>]> {
+  let summary = "Logical Right Shift operator.";
+
+  let description = [{
+    Elementwise Logical Right Shift of input1 by the amount specified in input2.
+    Axis of size 1 will be broadcast, as necessary. Rank of input tensors must
+    match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_logical_right_shift
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_logical_right_shift
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.LogicalRightShift %arg0, %arg1 : !spirv.arm.tensor<6x13x1x19xi8>, !spirv.arm.tensor<6x13x6x19xi8> -> !spirv.arm.tensor<6x13x6x19xi8>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_TosaInteger_TensorArm: $input1,
+    SPIRV_TosaInteger_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaInteger_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaLogicalOrOp : SPIRV_TosaOpWithResult<"LogicalOr", 23, [Pure]> {
+  let summary = "Logical OR operator.";
+
+  let description = [{
+    Elementwise logical OR of input1 and input2. Axis of size 1 will be
+    broadcast as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_logical_or
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_logical_or
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.LogicalOr %arg0, %arg1 : !spirv.arm.tensor<3x6x12x5xi1>, !spirv.arm.tensor<3x6x1x5xi1> -> !spirv.arm.tensor<3x6x12x5xi1>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_Bool_TensorArm: $input1,
+    SPIRV_Bool_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_Bool_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaLogicalXorOp : SPIRV_TosaOpWithResult<"LogicalXor", 24, [Pure]> {
+  let summary = "Logical XOR operator.";
+
+  let description = [{
+    Elementwise logical XOR of input1 and input2. Axis of size 1 will be
+    broadcast as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_logical_xor
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_logical_xor
+
+    #### Example:
+    ```mlir
+    %0 = spirv.Tosa.LogicalXor %arg0, %arg1 : !spirv.arm.tensor<11x4x9x12xi1>, !spirv.arm.tensor<11x4x9x1xi1> -> !spirv.arm.tensor<11x4x9x12xi1>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_Bool_TensorArm: $input1,
+    SPIRV_Bool_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_Bool_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaMaximumOp : SPIRV_TosaOpWithResult<"Maximum", 25, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>,
+  TypeConstraintImplicationOn<"input1", AnyInteger, "input1", [I32]>,
+  TypeConstraintImplicationOn<"input2", AnyInteger, "input2", [I32]>,
+  TypeConstraintImplicationOn<"output", AnyInteger, "output", [I32]>]> {
+  let summary = "Maximum.";
+
+  let description = [{
+    Elementwise maximum of input1 and input2. Axis of size 1 will be broadcast,
+    as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_maximum
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_maximum
+
+    #### Example:
+    ```mlir
+    %1 = spirv.Tosa.Maximum nan_mode = <Propagate>, %arg0, %arg1 : !spirv.arm.tensor<1x2x65533x1xi32>, !spirv.arm.tensor<1x2x65533x2xi32> -> !spirv.arm.tensor<1x2x65533x2xi32>
+    %1 = spirv.Tosa.Maximum nan_mode = <Ignore>, %arg0, %arg1 : !spirv.arm.tensor<1x12x14x7xf16>, !spirv.arm.tensor<11x12x14x7xf16> -> !spirv.arm.tensor<11x12x14x7xf16>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_TosaExtNaNPropagationModeAttr: $nan_mode,
+    SPIRV_TosaNumerical_TensorArm: $input1,
+    SPIRV_TosaNumerical_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaNumerical_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    `nan_mode` `=` $nan_mode `,`
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaMinimumOp : SPIRV_TosaOpWithResult<"Minimum", 26, [Pure,
+  AllElementTypesMatch<["input1", "input2", "output"]>,
+  AllRanksMatch<["input1", "input2"]>,
+  TypeConstraintImplicationOn<"input1", AnyInteger, "input1", [I32]>,
+  TypeConstraintImplicationOn<"input2", AnyInteger, "input2", [I32]>,
+  TypeConstraintImplicationOn<"output", AnyInteger, "output", [I32]>]> {
+  let summary = "Minimum.";
+
+  let description = [{
+    Elementwise minimum of input1 and input2. Axis of size 1 will be broadcast,
+    as necessary. Rank of input tensors must match.
+
+    References:
+      * https://github.khronos.org/SPIRV-Registry/extended/TOSA.001000.1.html#_minimum
+      * https://www.mlplatform.org/tosa/tosa_spec_1_0_1.html#_minimum
+
+    #### Example:
+    ```mlir
+    %1 = spirv.Tosa.Minimum nan_mode = <Propagate>, %arg0, %arg1 : !spirv.arm.tensor<15x2x10x11xi32>, !spirv.arm.tensor<15x1x10x11xi32> -> !spirv.arm.tensor<15x2x10x11xi32>
+    %1 = spirv.Tosa.Minimum nan_mode = <Propagate>, %arg0, %arg1 : !spirv.arm.tensor<1x65531x2x1xf32>, !spirv.arm.tensor<1x1x2x1xf32> -> !spirv.arm.tensor<1x65531x2x1xf32>
+    ```
+  }];
+
+  let arguments = (ins
+    SPIRV_TosaExtNaNPropagationModeAttr: $nan_mode,
+    SPIRV_TosaNumerical_TensorArm: $input1,
+    SPIRV_TosaNumerical_TensorArm: $input2
+  );
+
+  let results = (outs
+    SPIRV_TosaNumerical_TensorArm: $output
+  );
+
+  let assemblyFormat = [{
+    `nan_mode` `=` $nan_mode `,`
+    $input1 `,`
+    $input2
+    attr-dict `:` type(operands) `->` type(results)
+  }];
+
+  let extraClassDeclaration = extraBaseClassDeclaration#[{
+    ::mlir::spirv::TensorArmType getInput1Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput1().getType());
+    }
+    ::mlir::spirv::TensorArmType getInput2Type() {
+      return cast<::mlir::spirv::TensorArmType>(getInput2().getType());
+    }
+  }];
+}
+
+
+def SPIRV_TosaMulOp : SPIRV_TosaOpWithResult<"Mul", 27, [Pure,
+  AllElementTypesMatch<["input1", "input2"]>,
+  AllRanksMatch<["input1", "input2"]>,
+  TypeConstraintImplicationOn<"input1", F16, "output", [F16]>,
+  TypeConstraintImplicationOn<"input1", F32, "output", [F32]>,
+  TypeConstraintImplicationOn<"input1", BF16, "output", [BF16]>,
+  TypeConstraintImplicationOn<"input1", AnyInteger, "output", [I32]>]> {
+  let summary = "Multiplication operator.";
+
+  let description = [{
+    Elementwise Multiplication (Hadamard product) of inp...
[truncated]

@davidegrohmann davidegrohmann changed the title [mlir][spirv] Add Element Bynary operators to TOSA Ext Inst Set [mlir][spirv] Add Element Binary operators to TOSA Ext Inst Set Feb 4, 2026
This patch introduces the following element binary operators:
* spirv.Tosa.Add
* spirv.Tosa.ArithmeticRightShift
* spirv.Tosa.BitwiseAnd
* spirv.Tosa.BitwiseOr
* spirv.Tosa.BitwiseXor
* spirv.Tosa.IntDiv
* spirv.Tosa.LogicalAnd
* spirv.Tosa.LogicalLeftShift
* spirv.Tosa.LogicalRightShift
* spirv.Tosa.LogicalOr
* spirv.Tosa.LogicalXor
* spirv.Tosa.Maximum
* spirv.Tosa.Minimum
* spirv.Tosa.Mul
* spirv.Tosa.Pow
* spirv.Tosa.Sub
* spirv.Tosa.Table

Also dialect and serialization round-trip tests have been added.

Signed-off-by: Davide Grohmann <davide.grohmann@arm.com>
Change-Id: I477dec54212d4201230ba63753c4138fdcb83915
@davidegrohmann davidegrohmann force-pushed the mlir-spv-tosa-ext-inst-set-001000-part5 branch from add69f7 to c4573a5 Compare February 4, 2026 09:03
@davidegrohmann
Copy link
Contributor Author

This one a bit bigger, but many similar and trivial operators.

Copy link
Contributor

@IgWod-IMG IgWod-IMG left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to create parent ops for binary and unary operations and then use them to implement the actual new ops? Currently there is a lot of repetition, e.g., getInput1Type. It's a fairly common pattern for other types of ops, e.g.,

class SPIRV_GroupNonUniformArithmeticOp<string mnemonic, Type type,

@kuhar
Copy link
Member

kuhar commented Feb 4, 2026

Would it be possible to create parent ops for binary and unary operations and then use them to implement the actual new ops? Currently there is a lot of repetition, e.g., getInput1Type. It's a fairly common pattern for other types of ops, e.g.,

class SPIRV_GroupNonUniformArithmeticOp<string mnemonic, Type type,

+1. Can you also split this into a few smaller PRs (ideally <= 500 kLOC each)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants