[Mlir-commits] [mlir] [mlir][tosa] Add row_gather operator (PR #202895)
Luke Hutton
llvmlistbot at llvm.org
Wed Jun 10 01:50:03 PDT 2026
https://github.com/lhutton1 created https://github.com/llvm/llvm-project/pull/202895
Adds support for the row_gather operator defined by the TOSA specification, see https://github.com/arm/tosa-specification/pull/60.
This includes:
- Operator definition
- Verification logic for the operator
- Output shape inference for the operator
- Validation checks to ensure compliance with the TOSA specification including profile compliance and level checks.
- Canonicalization to replace row_gather with gather when row_count is statically known to be 1.
It does not yet cover support for MXFP types. This will be added once block scaled types are supported.
>From 9cfa455e6fd126023d3cad175622f7d69a3ad8c9 Mon Sep 17 00:00:00 2001
From: Luke Hutton <luke.hutton at arm.com>
Date: Mon, 8 Jun 2026 17:59:08 +0100
Subject: [PATCH] [mlir][tosa] Add row_gather operator
Adds support for the row_gather operator defined by the TOSA
specification, see https://github.com/arm/tosa-specification/pull/60.
This includes:
- Operator definition
- Verification logic for the operator
- Output shape inference for the operator
- Validation checks to ensure compliance with the TOSA specification
including profile compliance and level checks.
- Canonicalization to replace row_gather with gather when row_count is
statically known to be 1.
It does not yet cover support for MXFP types. This will be added once
block scaled types are supported.
Change-Id: I9037f813a6c31b7dc03cfe3b5a191f5e3fce02d6
---
.../Dialect/Tosa/IR/TosaComplianceData.h.inc | 65 +++++++++++
mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td | 36 ++++++
.../mlir/Dialect/Tosa/Utils/ConversionUtils.h | 5 +
.../Dialect/Tosa/IR/TosaCanonicalizations.cpp | 20 ++++
mlir/lib/Dialect/Tosa/IR/TosaOps.cpp | 105 +++++++++++++++---
.../Tosa/Transforms/TosaProfileCompliance.cpp | 9 ++
.../Tosa/Transforms/TosaValidation.cpp | 11 ++
.../Dialect/Tosa/Utils/ConversionUtils.cpp | 16 +++
mlir/test/Dialect/Tosa/availability.mlir | 10 ++
mlir/test/Dialect/Tosa/canonicalize.mlir | 30 +++++
mlir/test/Dialect/Tosa/invalid_extension.mlir | 8 ++
mlir/test/Dialect/Tosa/level_check.mlir | 9 ++
mlir/test/Dialect/Tosa/ops.mlir | 47 ++++++++
mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir | 39 +++++++
.../tosa-validation-version-1p0-invalid.mlir | 9 ++
...a-validation-version-1p1-pro-fp-valid.mlir | 9 ++
.../tosa-validation-version-1p1-valid.mlir | 27 +++++
mlir/test/Dialect/Tosa/verifier.mlir | 45 ++++++++
18 files changed, 484 insertions(+), 16 deletions(-)
diff --git a/mlir/include/mlir/Dialect/Tosa/IR/TosaComplianceData.h.inc b/mlir/include/mlir/Dialect/Tosa/IR/TosaComplianceData.h.inc
index 50bb9f69c6242..f17475fae563d 100644
--- a/mlir/include/mlir/Dialect/Tosa/IR/TosaComplianceData.h.inc
+++ b/mlir/include/mlir/Dialect/Tosa/IR/TosaComplianceData.h.inc
@@ -396,6 +396,33 @@ profileComplianceMap = {
{{i16T, i64T, i16T}, SpecificationVersion::V_1_1_DRAFT},
{{i32T, i64T, i32T}, SpecificationVersion::V_1_1_DRAFT}},
anyOf}}},
+ {"tosa.row_gather",
+ {{{Profile::pro_int},
+ {{{boolT, i32T, boolT}, SpecificationVersion::V_1_1_DRAFT},
+ {{i8T, i32T, i8T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i16T, i32T, i16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i32T, i32T, i32T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i64T, i32T, i64T}, SpecificationVersion::V_1_1_DRAFT},
+ {{boolT, i64T, boolT}, SpecificationVersion::V_1_1_DRAFT},
+ {{i8T, i64T, i8T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i16T, i64T, i16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i32T, i64T, i32T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i64T, i64T, i64T}, SpecificationVersion::V_1_1_DRAFT}}},
+ {{Profile::pro_fp},
+ {{{boolT, i32T, boolT}, SpecificationVersion::V_1_1_DRAFT},
+ {{i8T, i32T, i8T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i16T, i32T, i16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i32T, i32T, i32T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i64T, i32T, i64T}, SpecificationVersion::V_1_1_DRAFT},
+ {{fp16T, i32T, fp16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{fp32T, i32T, fp32T}, SpecificationVersion::V_1_1_DRAFT},
+ {{boolT, i64T, boolT}, SpecificationVersion::V_1_1_DRAFT},
+ {{i8T, i64T, i8T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i16T, i64T, i16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i32T, i64T, i32T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i64T, i64T, i64T}, SpecificationVersion::V_1_1_DRAFT},
+ {{fp16T, i64T, fp16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{fp32T, i64T, fp32T}, SpecificationVersion::V_1_1_DRAFT}}}}},
{"tosa.row_gather_block_scaled",
{{{Profile::pro_int},
{{{i8T, i32T, i32T, i8T}, SpecificationVersion::V_1_1_DRAFT},
@@ -909,6 +936,44 @@ extensionComplianceMap = {
{{Extension::bf16, Extension::int64},
{{{bf16T, i64T, bf16T}, SpecificationVersion::V_1_1_DRAFT}},
allOf}}},
+ {"tosa.row_gather",
+ {
+ {{Extension::int16},
+ {{{i16T, i32T, i16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i16T, i32T, i16T}, SpecificationVersion::V_1_1_DRAFT}}},
+ {{Extension::int64},
+ {{{i64T, i32T, i64T}, SpecificationVersion::V_1_1_DRAFT},
+ {{boolT, i64T, boolT}, SpecificationVersion::V_1_1_DRAFT},
+ {{i8T, i64T, i8T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i32T, i64T, i32T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i64T, i64T, i64T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i64T, i32T, i64T}, SpecificationVersion::V_1_1_DRAFT},
+ {{boolT, i64T, boolT}, SpecificationVersion::V_1_1_DRAFT},
+ {{i8T, i64T, i8T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i32T, i64T, i32T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i64T, i64T, i64T}, SpecificationVersion::V_1_1_DRAFT},
+ {{fp16T, i64T, fp16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{fp32T, i64T, fp32T}, SpecificationVersion::V_1_1_DRAFT}}},
+ {{Extension::int16, Extension::int64},
+ {{{i16T, i64T, i16T}, SpecificationVersion::V_1_1_DRAFT},
+ {{i16T, i64T, i16T}, SpecificationVersion::V_1_1_DRAFT}},
+ allOf},
+ {{Extension::fp8e4m3},
+ {{{fp8e4m3T, i32T, fp8e4m3T}, SpecificationVersion::V_1_1_DRAFT}}},
+ {{Extension::fp8e5m2},
+ {{{fp8e5m2T, i32T, fp8e5m2T}, SpecificationVersion::V_1_1_DRAFT}}},
+ {{Extension::bf16},
+ {{{bf16T, i32T, bf16T}, SpecificationVersion::V_1_1_DRAFT}}},
+ {{Extension::fp8e4m3, Extension::int64},
+ {{{fp8e4m3T, i64T, fp8e4m3T}, SpecificationVersion::V_1_1_DRAFT}},
+ allOf},
+ {{Extension::fp8e5m2, Extension::int64},
+ {{{fp8e5m2T, i64T, fp8e5m2T}, SpecificationVersion::V_1_1_DRAFT}},
+ allOf},
+ {{Extension::bf16, Extension::int64},
+ {{{bf16T, i64T, bf16T}, SpecificationVersion::V_1_1_DRAFT}},
+ allOf},
+ }},
{"tosa.row_gather_block_scaled",
{{{Extension::fp8e4m3},
{{{fp8e4m3T, i32T, i32T, fp8e4m3T}, SpecificationVersion::V_1_1_DRAFT}}},
diff --git a/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td b/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
index e135265b99881..75fe9c4fc54b2 100644
--- a/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
+++ b/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
@@ -2532,6 +2532,42 @@ def Tosa_GatherOp : Tosa_InferShapedTypeOp<"gather", [NoMemoryEffect]> {
"operands attr-dict `:` functional-type(operands, results)";
}
+//===----------------------------------------------------------------------===//
+// Operator: row_gather
+//===----------------------------------------------------------------------===//
+def Tosa_RowGatherOp : Tosa_InferShapedTypeOp<"row_gather", [NoMemoryEffect]> {
+ let summary = "Row gather operation.";
+
+ let description = [{
+ Generate a tensor based on the input indices and row_count. The number of
+ consecutive rows gathered for each index is specified in row_count. N is
+ the number of batches, W is the number of indices in each batch, K is
+ the range of each index, and C is the number of data channels for each
+ index. The values tensor has shape [N, K, C] and the output tensor has shape
+ [N, W * row_count, C].
+
+ This operation is not pure. Undefined behaviour may occur if the specified
+ indices are out of range.
+ }];
+
+ let arguments = (ins Tosa_Tensor3D:$values, Tosa_IndexTensor2D:$indices,
+ Tosa_ScalarInt32Tensor:$row_count);
+
+ let results = (outs Tosa_Tensor3D:$output);
+
+ list<Availability> availability = [Profile<[Tosa_PRO_INT, Tosa_PRO_FP]>,
+ Extension<[Tosa_EXT_FP8E4M3,
+ Tosa_EXT_FP8E5M2, Tosa_EXT_BF16,
+ Tosa_EXT_INT64]>,
+ ];
+
+ let hasCanonicalizer = 1;
+ let hasVerifier = 1;
+
+ let assemblyFormat =
+ "operands attr-dict `:` functional-type(operands, results)";
+}
+
//===----------------------------------------------------------------------===//
// Operator: row_gather_block_scaled
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/Tosa/Utils/ConversionUtils.h b/mlir/include/mlir/Dialect/Tosa/Utils/ConversionUtils.h
index 3a48608e7fd94..df4dabc2afda8 100644
--- a/mlir/include/mlir/Dialect/Tosa/Utils/ConversionUtils.h
+++ b/mlir/include/mlir/Dialect/Tosa/Utils/ConversionUtils.h
@@ -271,6 +271,11 @@ std::optional<ArrayRef<T>> tryGetDenseResourceValues(ElementsAttr attr) {
return std::nullopt;
}
+// returns the value of a constant scalar int tensor, or failure if
+// the value cannot be extracted
+template <typename T>
+FailureOr<T> getConstantScalarIntValue(Value val);
+
} // namespace tosa
} // namespace mlir
diff --git a/mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp b/mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp
index 4af185a6e534b..ec5bd53951324 100644
--- a/mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp
+++ b/mlir/lib/Dialect/Tosa/IR/TosaCanonicalizations.cpp
@@ -1185,6 +1185,26 @@ void CastToBlockScaledOp::getCanonicalizationPatterns(
results.add<CancellingBlockScaledCastsOptimization>(context);
}
+struct RowGatherToGather : public OpRewritePattern<tosa::RowGatherOp> {
+ using OpRewritePattern<tosa::RowGatherOp>::OpRewritePattern;
+
+ LogicalResult matchAndRewrite(tosa::RowGatherOp op,
+ PatternRewriter &rewriter) const override {
+ const FailureOr<int32_t> rowCount = mlir::tosa::getConstantScalarIntValue<int32_t>(op.getRowCount());
+ if (failed(rowCount) || rowCount.value() != 1)
+ return failure();
+
+ rewriter.replaceOpWithNewOp<tosa::GatherOp>(
+ op, op.getOutput().getType(), op.getValues(), op.getIndices());
+ return success();
+ }
+};
+
+void RowGatherOp::getCanonicalizationPatterns(RewritePatternSet &results,
+ MLIRContext *context) {
+ results.add<RowGatherToGather>(context);
+}
+
//===----------------------------------------------------------------------===//
// Operator Folders.
//===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp b/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp
index 351dc1ae0dcd5..32e7d7bb47531 100644
--- a/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp
+++ b/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp
@@ -3104,18 +3104,6 @@ static FailureOr<int64_t> getZeroPoint(Value val, bool signExtend) {
return -1;
}
-static FailureOr<int64_t> getConstantScalarIntValue(Value val) {
- ElementsAttr attr;
- if (!matchPattern(val, m_Constant(&attr)))
- return failure();
-
- if (!llvm::isa<IntegerType>(attr.getElementType()) ||
- attr.getNumElements() != 1)
- return failure();
-
- return attr.getValues<APInt>()[0].getSExtValue();
-}
-
template <typename T>
static LogicalResult verifyZeroPoint(T op, Value val, const int64_t &zp,
const std::string &operand) {
@@ -3364,6 +3352,37 @@ LogicalResult tosa::GatherOp::inferReturnTypeComponents(
return success();
}
+LogicalResult tosa::RowGatherOp::inferReturnTypeComponents(
+ MLIRContext *context, ::std::optional<Location> location,
+ RowGatherOp::Adaptor adaptor,
+ SmallVectorImpl<ShapedTypeComponents> &inferredReturnShapes) {
+ llvm::SmallVector<int64_t> outputShape;
+ outputShape.resize(3, ShapedType::kDynamic);
+
+ const ShapeAdaptor valuesShape(adaptor.getValues().getType());
+ if (valuesShape.hasRank()) {
+ outputShape[0] = valuesShape.getDimSize(0);
+ outputShape[2] = valuesShape.getDimSize(2);
+ }
+
+ const ShapeAdaptor indicesShape(adaptor.getIndices().getType());
+ if (indicesShape.hasRank()) {
+ if (outputShape[0] == ShapedType::kDynamic)
+ outputShape[0] = indicesShape.getDimSize(0);
+
+ const FailureOr<int32_t> maybeRowCount =
+ getConstantScalarIntValue<int32_t>(adaptor.getRowCount());
+ if (succeeded(maybeRowCount) && maybeRowCount.value() > 0) {
+ const int64_t indicesW = indicesShape.getDimSize(1);
+ if (ShapedType::isStatic(indicesW))
+ outputShape[1] = indicesW * maybeRowCount.value();
+ }
+ }
+
+ inferredReturnShapes.push_back(ShapedTypeComponents(outputShape));
+ return success();
+}
+
LogicalResult tosa::RowGatherBlockScaledOp::inferReturnTypeComponents(
MLIRContext *context, ::std::optional<Location> location,
RowGatherBlockScaledOp::Adaptor adaptor,
@@ -3384,7 +3403,7 @@ LogicalResult tosa::RowGatherBlockScaledOp::inferReturnTypeComponents(
if (dataShape[0] == ShapedType::kDynamic)
dataShape[0] = indicesShape.getDimSize(0);
- if (auto rowCount = getConstantScalarIntValue(adaptor.getRowCount());
+ if (auto rowCount = getConstantScalarIntValue<int32_t>(adaptor.getRowCount());
succeeded(rowCount) && rowCount.value() > 0) {
const int64_t indicesW = indicesShape.getDimSize(1);
if (ShapedType::isStatic(indicesW))
@@ -3455,6 +3474,60 @@ LogicalResult tosa::GatherOp::verify() {
return success();
}
+LogicalResult tosa::RowGatherOp::verify() {
+ if (failed(verifySameElementTypes(*this, /* inType = */ getValues().getType(),
+ /* outType = */ getOutput().getType())))
+ return failure();
+
+ const FailureOr<int32_t> maybeRowCount =
+ getConstantScalarIntValue<int32_t>(getRowCount());
+ if (succeeded(maybeRowCount) && maybeRowCount.value() <= 0)
+ return emitOpError() << "requires row_count to be > 0, got "
+ << maybeRowCount.value();
+
+ int64_t n = ShapedType::kDynamic;
+ int64_t c = ShapedType::kDynamic;
+ int64_t w = ShapedType::kDynamic;
+
+ const ShapeAdaptor valuesShape(getValues().getType());
+ if (valuesShape.hasRank()) {
+ n = valuesShape.getDimSize(0);
+ c = valuesShape.getDimSize(2);
+ }
+
+ const ShapeAdaptor indicesShape(getIndices().getType());
+ if (indicesShape.hasRank()) {
+ if (failed(tryUpdateDimOrFailure(*this, n, indicesShape.getDimSize(0),
+ "indices", "batch")))
+ return failure();
+ w = indicesShape.getDimSize(1);
+ }
+
+ const ShapeAdaptor outputShape(getOutput().getType());
+ if (outputShape.hasRank()) {
+ if (failed(tryUpdateDimOrFailure(*this, n, outputShape.getDimSize(0),
+ "output", "batch")) ||
+ failed(tryUpdateDimOrFailure(*this, c, outputShape.getDimSize(2),
+ "output", "channels")))
+ return failure();
+
+ const FailureOr<int32_t> maybeRowCount =
+ getConstantScalarIntValue<int32_t>(getRowCount());
+ if (succeeded(maybeRowCount) && maybeRowCount.value() > 0 &&
+ ShapedType::isStatic(w)) {
+ const int64_t expectedOutputRows = w * maybeRowCount.value();
+ if (ShapedType::isStatic(outputShape.getDimSize(1)) &&
+ outputShape.getDimSize(1) != expectedOutputRows)
+ return emitOpError()
+ << "requires output dimension to be equal to "
+ "indices[1]*row_count ("
+ << expectedOutputRows << "), got " << outputShape.getDimSize(1);
+ }
+ }
+
+ return success();
+}
+
LogicalResult tosa::RowGatherBlockScaledOp::verify() {
const OperandRange values = getValues();
const ResultRange output = getOutput();
@@ -3488,7 +3561,7 @@ LogicalResult tosa::RowGatherBlockScaledOp::verify() {
"values[1]", "output[1]")))
return failure();
- if (auto rowCount = getConstantScalarIntValue(getRowCount());
+ if (auto rowCount = getConstantScalarIntValue<int32_t>(getRowCount());
succeeded(rowCount) && rowCount.value() <= 0)
return emitOpError() << "requires row_count to be > 0, got "
<< rowCount.value();
@@ -3527,7 +3600,7 @@ LogicalResult tosa::RowGatherBlockScaledOp::verify() {
"output[0]", "channels")))
return failure();
- if (auto rowCount = getConstantScalarIntValue(getRowCount());
+ if (auto rowCount = getConstantScalarIntValue<int32_t>(getRowCount());
succeeded(rowCount) && rowCount.value() > 0 &&
ShapedType::isStatic(w)) {
const int64_t expectedOutputRows = w * rowCount.value();
@@ -3556,7 +3629,7 @@ LogicalResult tosa::RowGatherBlockScaledOp::verify() {
"output[1]", "batch")))
return failure();
- if (auto rowCount = getConstantScalarIntValue(getRowCount());
+ if (auto rowCount = getConstantScalarIntValue<int32_t>(getRowCount());
succeeded(rowCount) && rowCount.value() > 0 &&
ShapedType::isStatic(w)) {
const int64_t expectedOutputRows = w * rowCount.value();
diff --git a/mlir/lib/Dialect/Tosa/Transforms/TosaProfileCompliance.cpp b/mlir/lib/Dialect/Tosa/Transforms/TosaProfileCompliance.cpp
index 9511d4da89dbd..1bf92ff562a84 100644
--- a/mlir/lib/Dialect/Tosa/Transforms/TosaProfileCompliance.cpp
+++ b/mlir/lib/Dialect/Tosa/Transforms/TosaProfileCompliance.cpp
@@ -185,6 +185,14 @@ LogicalResult ProfileInfoDepot::populateProfileInfo(tosa::GatherOp op) {
return success();
}
+template <>
+LogicalResult ProfileInfoDepot::populateProfileInfo(tosa::RowGatherOp op) {
+ addValue(op.getValues());
+ addValue(op.getIndices());
+ addValue(op.getOutput());
+ return success();
+}
+
template <>
LogicalResult
ProfileInfoDepot::populateProfileInfo(tosa::RowGatherBlockScaledOp op) {
@@ -300,6 +308,7 @@ LogicalResult ProfileInfoDepot::populatationDispatch(Operation *op) {
POPULATE_PROFILE_INFO_CUSTOM(Tile)
POPULATE_PROFILE_INFO_CUSTOM(Transpose)
POPULATE_PROFILE_INFO_CUSTOM(Gather)
+ POPULATE_PROFILE_INFO_CUSTOM(RowGather)
POPULATE_PROFILE_INFO_CUSTOM(RowGatherBlockScaled)
POPULATE_PROFILE_INFO_CUSTOM(Scatter)
POPULATE_PROFILE_INFO_CUSTOM(Resize)
diff --git a/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp b/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
index 34ac84d237f45..4a29e5327cc55 100644
--- a/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
+++ b/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
@@ -125,6 +125,15 @@ checkConstantOperandRowGatherBlockScaled(Operation *op, const TargetEnv &env) {
return success();
}
+static LogicalResult checkConstantOperandRowGather(Operation *op,
+ const TargetEnv &env) {
+ if (!env.allows(Extension::dynamic) && isa<tosa::RowGatherOp>(op)) {
+ // Check 'row_count'
+ return checkConstantOperands(op, {2});
+ }
+ return success();
+}
+
static LogicalResult checkConstantOperandAvgPool2d(Operation *op,
const TargetEnv &env) {
if (!env.allows(Extension::dynamic) && isa<tosa::AvgPool2dOp>(op)) {
@@ -210,6 +219,7 @@ struct TosaValidation : public tosa::impl::TosaValidationBase<TosaValidation> {
constCheckers.emplace_back(
checkConstantOperandConvOps<tosa::TransposeConv2DOp>);
constCheckers.emplace_back(checkConstantOperandMatMul);
+ constCheckers.emplace_back(checkConstantOperandRowGather);
constCheckers.emplace_back(checkConstantOperandRowGatherBlockScaled);
constCheckers.emplace_back(checkConstantOperandAvgPool2d);
constCheckers.emplace_back(checkConstantOperandAvgPool2dAdaptive);
@@ -834,6 +844,7 @@ LogicalResult TosaValidation::levelCheckRanksAndSizes(Operation *op) {
CHECK_SIZES(RFFT2d);
// Scatter/Gather Operators
CHECK_SIZES(Gather);
+ CHECK_SIZES(RowGather);
CHECK_SIZES(Scatter);
// Image Operators
CHECK_SIZES(Resize);
diff --git a/mlir/lib/Dialect/Tosa/Utils/ConversionUtils.cpp b/mlir/lib/Dialect/Tosa/Utils/ConversionUtils.cpp
index dd36083e910b4..e0d4b3470c981 100644
--- a/mlir/lib/Dialect/Tosa/Utils/ConversionUtils.cpp
+++ b/mlir/lib/Dialect/Tosa/Utils/ConversionUtils.cpp
@@ -248,3 +248,19 @@ bool mlir::tosa::hasUniqueConstantScatterIndices(
return true;
}
+
+template <typename T>
+FailureOr<T> mlir::tosa::getConstantScalarIntValue(Value val) {
+ ElementsAttr attr;
+ if (!matchPattern(val, m_Constant(&attr)))
+ return failure();
+
+ if (!llvm::isa<IntegerType>(attr.getElementType()) ||
+ attr.getNumElements() != 1)
+ return failure();
+
+ return attr.getValues<T>()[0];
+}
+
+template FailureOr<int32_t>
+mlir::tosa::getConstantScalarIntValue<int32_t>(Value val);
diff --git a/mlir/test/Dialect/Tosa/availability.mlir b/mlir/test/Dialect/Tosa/availability.mlir
index 81294f2c0c308..d2a156e028fc8 100644
--- a/mlir/test/Dialect/Tosa/availability.mlir
+++ b/mlir/test/Dialect/Tosa/availability.mlir
@@ -595,6 +595,16 @@ func.func @test_gather(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) ->
return %0 : tensor<13x26x3xf32>
}
+// -----
+// CHECK-LABEL: test_row_gather
+func.func @test_row_gather(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // CHECK: profiles: [ [pro_int, pro_fp] ]
+ // CHECK: extensions: [ [fp8e4m3, fp8e5m2, bf16, int64] ]
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xf32>
+ return %0 : tensor<13x52x3xf32>
+}
+
// -----
// CHECK-LABEL: row_gather_block_scaled
func.func @test_row_gather_block_scaled(%arg0: tensor<13x21x32xf4E2M1FN>, %arg1: tensor<13x21x1xf8E8M0FNU>, %arg2: tensor<13x26xi32>) -> (tensor<13x52x32xf4E2M1FN>, tensor<13x52x1xf8E8M0FNU>) {
diff --git a/mlir/test/Dialect/Tosa/canonicalize.mlir b/mlir/test/Dialect/Tosa/canonicalize.mlir
index d4f3d23fd761e..99b2102a0a754 100644
--- a/mlir/test/Dialect/Tosa/canonicalize.mlir
+++ b/mlir/test/Dialect/Tosa/canonicalize.mlir
@@ -18,6 +18,36 @@ func.func @test_argmax_fold_i64_index(%arg0: tensor<1xi8>) -> tensor<i64> {
// -----
+// CHECK-LABEL: @row_gather_row_count_one_to_gather
+func.func @row_gather_row_count_one_to_gather(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<13x26x3xf32> {
+ %row_count = "tosa.const"() {values = dense<1> : tensor<1xi32>} : () -> tensor<1xi32>
+ // CHECK-NOT: tosa.row_gather
+ // CHECK: tosa.gather %arg0, %arg1 : (tensor<13x21x3xf32>, tensor<13x26xi32>) -> tensor<13x26x3xf32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x26x3xf32>
+ return %0 : tensor<13x26x3xf32>
+}
+
+// -----
+
+// CHECK-LABEL: @row_gather_row_count_two_no_fold
+func.func @row_gather_row_count_two_no_fold(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // CHECK: tosa.row_gather %arg0, %arg1, %{{.*}} : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xf32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xf32>
+ return %0 : tensor<13x52x3xf32>
+}
+
+// -----
+
+// CHECK-LABEL: @row_gather_non_const_row_count_no_fold
+func.func @row_gather_non_const_row_count_no_fold(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>, %row_count: tensor<1xi32>) -> tensor<13x?x3xf32> {
+ // CHECK: tosa.row_gather %arg0, %arg1, %arg2 : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x?x3xf32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x?x3xf32>
+ return %0 : tensor<13x?x3xf32>
+}
+
+// -----
+
// CHECK-LABEL: @pad_wh_avg_pool2d_nofold
func.func @pad_wh_avg_pool2d_nofold(%input: tensor<1x10x8x3xf32>) -> tensor<1x6x5x3xf32> {
// CHECK: tosa.pad
diff --git a/mlir/test/Dialect/Tosa/invalid_extension.mlir b/mlir/test/Dialect/Tosa/invalid_extension.mlir
index 295d7172bc2c4..cd82e314e3b6e 100644
--- a/mlir/test/Dialect/Tosa/invalid_extension.mlir
+++ b/mlir/test/Dialect/Tosa/invalid_extension.mlir
@@ -249,6 +249,14 @@ func.func @test_gather(%arg0: tensor<13x21x3xbf16>, %arg1: tensor<13x26xi32>) ->
return %0 : tensor<13x26x3xbf16>
}
+// -----
+func.func @test_row_gather(%arg0: tensor<13x21x3xbf16>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xbf16> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // expected-error at +1 {{'tosa.row_gather' op illegal: requires any of [bf16] profiles/extensions to be specified in the target environment}}
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xbf16>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xbf16>
+ return %0 : tensor<13x52x3xbf16>
+}
+
// -----
func.func @test_row_gather_block_scaled(%arg0: tensor<13x21x32xf4E2M1FN>, %arg1: tensor<13x21x1xf8E8M0FNU>, %arg2: tensor<13x26xi32>) -> (tensor<13x52x32xf4E2M1FN>, tensor<13x52x1xf8E8M0FNU>) {
%row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
diff --git a/mlir/test/Dialect/Tosa/level_check.mlir b/mlir/test/Dialect/Tosa/level_check.mlir
index 3042f5a23e9e6..85b5d002434bc 100644
--- a/mlir/test/Dialect/Tosa/level_check.mlir
+++ b/mlir/test/Dialect/Tosa/level_check.mlir
@@ -1286,6 +1286,15 @@ func.func @test_gather_tensor_size_invalid(%arg0: tensor<536870912x21x3xf32>, %a
// -----
+func.func @test_row_gather_tensor_size_invalid(%arg0: tensor<536870912x21x3xf32>, %arg1: tensor<536870912x26xi32>) -> tensor<536870912x52x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // expected-error at +1 {{'tosa.row_gather' op failed level check: operand tensor size (in bytes) <= (1 << MAX_LOG2_SIZE - 1)}}
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<536870912x21x3xf32>, tensor<536870912x26xi32>, tensor<1xi32>) -> tensor<536870912x52x3xf32>
+ return %0 : tensor<536870912x52x3xf32>
+}
+
+// -----
+
func.func @test_custom_tensor_size_invalid(%arg0: tensor<536870912xi32>) -> tensor<536870912xi32> {
// expected-error at +1 {{'tosa.custom' op failed level check: operand tensor size (in bytes) <= (1 << MAX_LOG2_SIZE - 1)}}
%0 = tosa.custom %arg0 {operator_name="custom_test", domain_name="tosa.mlir_test", implementation_attrs="" } : (tensor<536870912xi32>) -> (tensor<536870912xi32>)
diff --git a/mlir/test/Dialect/Tosa/ops.mlir b/mlir/test/Dialect/Tosa/ops.mlir
index 5c368b3da4ff5..9c96c38e2a0d5 100644
--- a/mlir/test/Dialect/Tosa/ops.mlir
+++ b/mlir/test/Dialect/Tosa/ops.mlir
@@ -961,6 +961,45 @@ func.func @test_gather_unranked_indices(%arg0: tensor<13x21x3xf32>, %arg1: tenso
return %0 : tensor<13x26x3xf32>
}
+// -----
+// CHECK-LABEL: test_row_gather
+func.func @test_row_gather(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xf32>
+ return %0 : tensor<13x52x3xf32>
+}
+
+// -----
+// CHECK-LABEL: test_row_gather_int64
+func.func @test_row_gather_int64(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi64>) -> tensor<13x52x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi64>, tensor<1xi32>) -> tensor<13x52x3xf32>
+ return %0 : tensor<13x52x3xf32>
+}
+
+// -----
+// CHECK-LABEL: test_row_gather_non_const_row_count
+func.func @test_row_gather_non_const_row_count(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>, %row_count: tensor<1xi32>) -> tensor<13x?x3xf32> {
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x?x3xf32>
+ return %0 : tensor<13x?x3xf32>
+}
+
+// -----
+// CHECK-LABEL: test_row_gather_unranked_indices
+func.func @test_row_gather_unranked_indices(%arg0: tensor<13x21x3xf32>, %arg1: tensor<*xi32>) -> tensor<13x?x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<*xi32>, tensor<1xi32>) -> tensor<13x?x3xf32>
+ return %0 : tensor<13x?x3xf32>
+}
+
+// -----
+// CHECK-LABEL: test_row_gather_fully_unranked
+func.func @test_row_gather_fully_unranked(%arg0: tensor<*xf32>, %arg1: tensor<*xi32>) -> tensor<*xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<*xf32>, tensor<*xi32>, tensor<1xi32>) -> tensor<*xf32>
+ return %0 : tensor<*xf32>
+}
+
// -----
// CHECK-LABEL: test_row_gather_block_scaled
func.func @test_row_gather_block_scaled(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xf32> {
@@ -1284,6 +1323,14 @@ func.func @test_concat_f8E5M2(%arg0: tensor<13x21x3xf8E5M2>, %arg1: tensor<13x21
return %0 : tensor<26x21x3xf8E5M2>
}
+// -----
+// CHECK-LABEL: row_gather_f8E5M2
+func.func @test_row_gather_f8E5M2(%arg0: tensor<13x21x3xf8E5M2>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xf8E5M2> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf8E5M2>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xf8E5M2>
+ return %0 : tensor<13x52x3xf8E5M2>
+}
+
// -----
// CHECK-LABEL: pad_f8E5M2
func.func @test_pad_f8E5M2(%arg0: tensor<13x21x3xf8E5M2>) -> tensor<13x21x3xf8E5M2> {
diff --git a/mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir b/mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir
index db488baef3571..5499da8c6e135 100644
--- a/mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir
+++ b/mlir/test/Dialect/Tosa/tosa-infer-shapes.mlir
@@ -771,6 +771,45 @@ func.func @gather_minimum_info(%arg0 : tensor<3x?x5xi32>, %arg1 : tensor<?x6xi32
// -----
+// CHECK-LABEL: @row_gather_static
+func.func @row_gather_static(%arg0 : tensor<3x4x5xi32>, %arg1 : tensor<3x6xi32>) {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // CHECK: tosa.row_gather %arg0, %arg1, %[[ROW_COUNT:.+]] : (tensor<3x4x5xi32>, tensor<3x6xi32>, tensor<1xi32>) -> tensor<3x12x5xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<3x4x5xi32>, tensor<3x6xi32>, tensor<1xi32>) -> tensor<?x?x?xi32>
+ return
+}
+
+// -----
+
+// CHECK-LABEL: @row_gather_minimum_info
+func.func @row_gather_minimum_info(%arg0 : tensor<3x?x5xi32>, %arg1 : tensor<?x6xi32>) {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // CHECK: tosa.row_gather %arg0, %arg1, %[[ROW_COUNT:.+]] : (tensor<3x?x5xi32>, tensor<?x6xi32>, tensor<1xi32>) -> tensor<3x12x5xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<3x?x5xi32>, tensor<?x6xi32>, tensor<1xi32>) -> tensor<?x?x?xi32>
+ return
+}
+
+// -----
+
+// CHECK-LABEL: @row_gather_nonconstant_row_count
+func.func @row_gather_nonconstant_row_count(%arg0 : tensor<3x4x5xi32>, %arg1 : tensor<3x6xi32>, %row_count : tensor<1xi32>) {
+ // CHECK: tosa.row_gather %arg0, %arg1, %arg2 : (tensor<3x4x5xi32>, tensor<3x6xi32>, tensor<1xi32>) -> tensor<3x?x5xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<3x4x5xi32>, tensor<3x6xi32>, tensor<1xi32>) -> tensor<?x?x?xi32>
+ return
+}
+
+// -----
+
+// CHECK-LABEL: @row_gather_row_count_one
+func.func @row_gather_row_count_one(%arg0 : tensor<3x4x5xi32>, %arg1 : tensor<3x6xi32>) {
+ %row_count = "tosa.const"() {values = dense<1> : tensor<1xi32>} : () -> tensor<1xi32>
+ // CHECK: tosa.row_gather %arg0, %arg1, %[[ROW_COUNT:.+]] : (tensor<3x4x5xi32>, tensor<3x6xi32>, tensor<1xi32>) -> tensor<3x6x5xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<3x4x5xi32>, tensor<3x6xi32>, tensor<1xi32>) -> tensor<?x?x?xi32>
+ return
+}
+
+// -----
+
// CHECK-LABEL: @row_gather_block_scaled_static
func.func @row_gather_block_scaled_static(%arg0 : tensor<3x4x5xi32>, %arg1 : tensor<3x6xi32>) {
%row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
diff --git a/mlir/test/Dialect/Tosa/tosa-validation-version-1p0-invalid.mlir b/mlir/test/Dialect/Tosa/tosa-validation-version-1p0-invalid.mlir
index 7ff883e8e5431..3dc10eff15a62 100644
--- a/mlir/test/Dialect/Tosa/tosa-validation-version-1p0-invalid.mlir
+++ b/mlir/test/Dialect/Tosa/tosa-validation-version-1p0-invalid.mlir
@@ -78,6 +78,15 @@ func.func @test_gather_bool_i32(%arg0: tensor<13x21x3xi1>, %arg1: tensor<13x26xi
// -----
+func.func @test_row_gather_i8_i32(%arg0: tensor<13x21x3xi8>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xi8> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // expected-error at +1 {{'tosa.row_gather' op illegal: requires specification version compatible with 1.1.draft (got 1.0) OR requires specification version compatible with 1.1.draft (got 1.0) to be specified in the target environment}}
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xi8>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xi8>
+ return %0 : tensor<13x52x3xi8>
+}
+
+// -----
+
func.func @test_row_gather_block_scaled_i8_i32(%arg0: tensor<13x21x3xi8>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xi8> {
%row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
// expected-error at +1 {{'tosa.row_gather_block_scaled' op illegal: requires specification version compatible with 1.1.draft (got 1.0) OR requires specification version compatible with 1.1.draft (got 1.0) to be specified in the target environment}}
diff --git a/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-pro-fp-valid.mlir b/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-pro-fp-valid.mlir
index 57802fd147f23..408ee86690154 100644
--- a/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-pro-fp-valid.mlir
+++ b/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-pro-fp-valid.mlir
@@ -16,6 +16,15 @@ func.func @test_scatter_i8_i32(%input: tensor<13x27x3xi8>, %indices: tensor<13x2
// -----
+// CHECK-LABEL: test_row_gather_i8_i32
+func.func @test_row_gather_i8_i32(%input: tensor<13x27x3xi8>, %indices: tensor<13x26xi32>) -> tensor<13x52x3xi8> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %gather = tosa.row_gather %input, %indices, %row_count : (tensor<13x27x3xi8>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xi8>
+ return %gather : tensor<13x52x3xi8>
+}
+
+// -----
+
// CHECK-LABEL: test_row_gather_block_scaled_i8_i32
func.func @test_row_gather_block_scaled_i8_i32(%input: tensor<13x27x3xi8>, %indices: tensor<13x26xi32>) -> tensor<13x52x3xi8> {
%row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
diff --git a/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-valid.mlir b/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-valid.mlir
index 1bac8bacbaf40..584dc4e9aa9ed 100644
--- a/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-valid.mlir
+++ b/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-valid.mlir
@@ -223,6 +223,33 @@ func.func @test_gather_i8_i32_indices(%arg0: tensor<13x21x3xi8>, %arg1: tensor<1
// -----
+// CHECK-LABEL: test_row_gather_i8_i32_indices
+func.func @test_row_gather_i8_i32_indices(%arg0: tensor<13x21x3xi8>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xi8> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xi8>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xi8>
+ return %0 : tensor<13x52x3xi8>
+}
+
+// -----
+
+// CHECK-LABEL: test_row_gather_i8_i64_indices
+func.func @test_row_gather_i8_i64_indices(%arg0: tensor<13x21x3xi8>, %arg1: tensor<13x26xi64>) -> tensor<13x52x3xi8> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xi8>, tensor<13x26xi64>, tensor<1xi32>) -> tensor<13x52x3xi8>
+ return %0 : tensor<13x52x3xi8>
+}
+
+// -----
+
+// CHECK-LABEL: test_row_gather_f8e5m2_i32_indices
+func.func @test_row_gather_f8e5m2_i32_indices(%arg0: tensor<13x21x3xf8E5M2>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xf8E5M2> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf8E5M2>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x3xf8E5M2>
+ return %0 : tensor<13x52x3xf8E5M2>
+}
+
+// -----
+
// CHECK-LABEL: test_row_gather_block_scaled_i8_i32_indices
func.func @test_row_gather_block_scaled_i8_i32_indices(%arg0: tensor<13x21x3xi8>, %arg1: tensor<13x26xi32>) -> tensor<13x52x3xi8> {
%row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
diff --git a/mlir/test/Dialect/Tosa/verifier.mlir b/mlir/test/Dialect/Tosa/verifier.mlir
index b3ae5b7a5d5f3..11e5409b4d521 100644
--- a/mlir/test/Dialect/Tosa/verifier.mlir
+++ b/mlir/test/Dialect/Tosa/verifier.mlir
@@ -499,6 +499,51 @@ func.func @test_gather_invalid_out_C(%arg0: tensor<13x21x3xf32>, %arg1: tensor<1
return %0 : tensor<13x26x8xf32>
}
+// -----
+// CHECK-LABEL: @test_row_gather_invalid_indices_N
+func.func @test_row_gather_invalid_indices_N(%arg0: tensor<13x21x3xf32>, %arg1: tensor<12x26xi32>) -> tensor<13x52x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // expected-error at +1 {{'tosa.row_gather' op expected batch of indices to match size 13, got 12}}
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<12x26xi32>, tensor<1xi32>) -> tensor<13x52x3xf32>
+ return %0 : tensor<13x52x3xf32>
+}
+
+// -----
+// CHECK-LABEL: test_row_gather_invalid_out_N
+func.func @test_row_gather_invalid_out_N(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<12x52x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // expected-error at +1 {{'tosa.row_gather' op expected batch of output to match size 13, got 12}}
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<12x52x3xf32>
+ return %0 : tensor<12x52x3xf32>
+}
+
+// -----
+// CHECK-LABEL: test_row_gather_invalid_out_W
+func.func @test_row_gather_invalid_out_W(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<13x53x3xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // expected-error at +1 {{'tosa.row_gather' op requires output dimension to be equal to indices[1]*row_count (52), got 53}}
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x53x3xf32>
+ return %0 : tensor<13x53x3xf32>
+}
+
+// -----
+// CHECK-LABEL: test_row_gather_invalid_out_C
+func.func @test_row_gather_invalid_out_C(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<13x52x8xf32> {
+ %row_count = "tosa.const"() {values = dense<2> : tensor<1xi32>} : () -> tensor<1xi32>
+ // expected-error at +1 {{'tosa.row_gather' op expected channels of output to match size 3, got 8}}
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x52x8xf32>
+ return %0 : tensor<13x52x8xf32>
+}
+
+// -----
+// CHECK-LABEL: test_row_gather_invalid_row_count
+func.func @test_row_gather_invalid_row_count(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> tensor<13x26x3xf32> {
+ %row_count = "tosa.const"() {values = dense<0> : tensor<1xi32>} : () -> tensor<1xi32>
+ // expected-error at +1 {{'tosa.row_gather' op requires row_count to be > 0, got 0}}
+ %0 = tosa.row_gather %arg0, %arg1, %row_count : (tensor<13x21x3xf32>, tensor<13x26xi32>, tensor<1xi32>) -> tensor<13x26x3xf32>
+ return %0 : tensor<13x26x3xf32>
+}
+
// -----
// CHECK-LABEL: test_row_gather_block_scaled_output_count_mismatch
func.func @test_row_gather_block_scaled_output_count_mismatch(%arg0: tensor<13x21x3xf32>, %arg1: tensor<13x26xi32>) -> (tensor<13x52x3xf32>, tensor<13x52x3xf32>) {
More information about the Mlir-commits
mailing list