[Mlir-commits] [mlir] [mlir][tosa] Support variadic batch dimensions in MATMUL operator (PR #232371)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Fri Oct 16 02:01:50 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-tosa
Author: kmehanna (karmeh01)
<details>
<summary>Changes</summary>
Extend TOSA-to-Linalg MATMUL lowering to support rank-two inputs and multiple batch dimensions.
Reuse MATMUL_T helpers to add missing batch dimensions and broadcast singleton dimensions, preserving quantized zero-point handling.
Add conversion tests for dynamic shapes, broadcasting, quantized inputs, and unsupported cases.
Assisted-by: Codex
---
Patch is 51.33 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/232371.diff
5 Files Affected:
- (modified) mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp (+24-22)
- (modified) mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp (+93-57)
- (modified) mlir/lib/Conversion/TosaToLinalg/TosaToLinalgUtils.h (+4-3)
- (modified) mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named-invalid.mlir (+36)
- (modified) mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir (+415-18)
``````````diff
diff --git a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp
index 25b4b360723b2..97793761f006c 100644
--- a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp
+++ b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalg.cpp
@@ -1432,9 +1432,9 @@ static LogicalResult reduceMatchAndRewriteHelper(OpTy op, uint64_t axis,
}
// Insert missing leading batch dimensions, taking their sizes from `other`.
-static Value broadcastMatMulTLeadingDimensions(PatternRewriter &rewriter,
- Location loc, Value operand,
- Value other) {
+static Value broadcastMatMulLeadingDimensions(PatternRewriter &rewriter,
+ Location loc, Value operand,
+ Value other) {
auto type = cast<RankedTensorType>(operand.getType());
int64_t missingDims =
cast<RankedTensorType>(other.getType()).getRank() - type.getRank();
@@ -1456,13 +1456,12 @@ static Value broadcastMatMulTLeadingDimensions(PatternRewriter &rewriter,
}
// Expand an existing singleton batch dimension to `size`, preserving the rank.
-// Missing dimensions are handled by broadcastMatMulTLeadingDimensions.
+// Missing dimensions are handled by broadcastMatMulLeadingDimensions.
// linalg.broadcast inserts dimensions, so first remove the singleton dimension
// with a rank-reducing slice, then reinsert it at the target size.
-static Value broadcastMatMulTSingletonDimension(PatternRewriter &rewriter,
- Location loc, Value operand,
- int64_t dim,
- OpFoldResult size) {
+static Value broadcastMatMulSingletonDimension(PatternRewriter &rewriter,
+ Location loc, Value operand,
+ int64_t dim, OpFoldResult size) {
auto type = cast<RankedTensorType>(operand.getType());
SmallVector<int64_t> sliceShape(type.getShape());
sliceShape.erase(sliceShape.begin() + dim);
@@ -1484,13 +1483,13 @@ static Value broadcastMatMulTSingletonDimension(PatternRewriter &rewriter,
// Align the ranks, then broadcast each shared batch dimension independently.
static std::pair<Value, Value>
-normalizeMatMulTBatchDimensions(PatternRewriter &rewriter, Location loc,
- Value a, Value b, int64_t batchRank) {
+normalizeMatMulBatchDimensions(PatternRewriter &rewriter, Location loc, Value a,
+ Value b, int64_t batchRank) {
int64_t aRank = cast<RankedTensorType>(a.getType()).getRank();
int64_t bRank = cast<RankedTensorType>(b.getType()).getRank();
int64_t firstSharedDim = std::max(aRank, bRank) - std::min(aRank, bRank);
- a = broadcastMatMulTLeadingDimensions(rewriter, loc, a, b);
- b = broadcastMatMulTLeadingDimensions(rewriter, loc, b, a);
+ a = broadcastMatMulLeadingDimensions(rewriter, loc, a, b);
+ b = broadcastMatMulLeadingDimensions(rewriter, loc, b, a);
IndexPool indexPool;
// The inserted leading dimensions already match their partner's sizes.
@@ -1507,11 +1506,11 @@ normalizeMatMulTBatchDimensions(PatternRewriter &rewriter, Location loc,
auto [size, masterOperand] =
computeTargetSize(rewriter, loc, indexPool, ValueRange{a, b}, dim);
if (aSize == 1) {
- a = broadcastMatMulTSingletonDimension(rewriter, loc, a, dim, size);
+ a = broadcastMatMulSingletonDimension(rewriter, loc, a, dim, size);
continue;
}
if (bSize == 1) {
- b = broadcastMatMulTSingletonDimension(rewriter, loc, b, dim, size);
+ b = broadcastMatMulSingletonDimension(rewriter, loc, b, dim, size);
continue;
}
a = broadcastDynamicDimension(rewriter, loc, indexPool, a, dim, size,
@@ -1522,14 +1521,15 @@ normalizeMatMulTBatchDimensions(PatternRewriter &rewriter, Location loc,
return {a, b};
}
-MatMulTOperands
-mlir::tosa::prepareMatMulTOperands(PatternRewriter &rewriter, Location loc,
- Value a, Value b,
- RankedTensorType outputType) {
+MatMulTOperands mlir::tosa::prepareMatMulOperands(PatternRewriter &rewriter,
+ Location loc, Value a,
+ Value b,
+ RankedTensorType outputType,
+ bool transposeB) {
const int64_t outputRank = outputType.getRank();
const int64_t batchRank = outputRank - 2;
auto normalizedOperands =
- normalizeMatMulTBatchDimensions(rewriter, loc, a, b, batchRank);
+ normalizeMatMulBatchDimensions(rewriter, loc, a, b, batchRank);
a = normalizedOperands.first;
b = normalizedOperands.second;
@@ -1538,7 +1538,8 @@ mlir::tosa::prepareMatMulTOperands(PatternRewriter &rewriter, Location loc,
for (int64_t batchDim = 0; batchDim < batchRank; ++batchDim)
outputShape.push_back(tensor::getMixedSize(rewriter, loc, a, batchDim));
outputShape.push_back(tensor::getMixedSize(rewriter, loc, a, outputRank - 2));
- outputShape.push_back(tensor::getMixedSize(rewriter, loc, b, outputRank - 2));
+ outputShape.push_back(
+ tensor::getMixedSize(rewriter, loc, b, outputRank - 1 - transposeB));
SmallVector<Value> dynamicDims;
for (auto [dim, size] : llvm::enumerate(outputShape))
if (outputType.isDynamicDim(dim))
@@ -1592,8 +1593,9 @@ class QuantizedMatMulTConverter : public OpConversionPattern<tosa::MatMulTOp> {
const int64_t outputRank = outputType.getRank();
const int64_t batchRank = outputRank - 2;
- auto [a, b, zeroTensor] = prepareMatMulTOperands(
- rewriter, loc, adaptor.getA(), adaptor.getB(), outputType);
+ auto [a, b, zeroTensor] =
+ prepareMatMulOperands(rewriter, loc, adaptor.getA(), adaptor.getB(),
+ outputType, /*transposeB=*/true);
Type outputElementType = outputType.getElementType();
int64_t loopRank = outputRank + 1;
diff --git a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp
index a7f7414bf56ed..f646c3a80f06f 100644
--- a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp
+++ b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgNamed.cpp
@@ -605,6 +605,16 @@ class DepthwiseConvConverter
}
};
+static SmallVector<ReassociationIndices>
+getMatMulBatchCollapseReassociation(int64_t rank) {
+ SmallVector<ReassociationIndices> reassociation(3);
+ for (int64_t dim = 0; dim < rank - 2; ++dim)
+ reassociation[0].push_back(dim);
+ reassociation[1].push_back(rank - 2);
+ reassociation[2].push_back(rank - 1);
+ return reassociation;
+}
+
class MatMulConverter : public OpConversionPattern<tosa::MatMulOp> {
public:
using OpConversionPattern<tosa::MatMulOp>::OpConversionPattern;
@@ -612,85 +622,110 @@ class MatMulConverter : public OpConversionPattern<tosa::MatMulOp> {
matchAndRewrite(tosa::MatMulOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const final {
Location loc = op.getLoc();
-
- auto outputTy = cast<ShapedType>(op.getType());
- auto outputElementTy = outputTy.getElementType();
-
- SmallVector<Value> dynDims;
- dynDims.resize(cast<ShapedType>(op->getResult(0).getType()).getRank());
-
- if (!outputTy.hasRank() || outputTy.isDynamicDim(0)) {
- dynDims[0] = tensor::DimOp::create(rewriter, loc, op->getOperand(0), 0);
- }
-
- if (!outputTy.hasRank() || outputTy.isDynamicDim(1)) {
- dynDims[1] = tensor::DimOp::create(rewriter, loc, op->getOperand(0), 1);
- }
-
- if (!outputTy.hasRank() || outputTy.isDynamicDim(2)) {
- dynDims[2] = tensor::DimOp::create(rewriter, loc, op->getOperand(1), 2);
- }
-
- SmallVector<Value> filteredDims = condenseValues(dynDims);
-
- auto zeroAttr = rewriter.getZeroAttr(outputElementTy);
- Value zero = arith::ConstantOp::create(rewriter, loc, zeroAttr);
- auto emptyTensor =
- tensor::EmptyOp::create(rewriter, loc, outputTy.getShape(),
- outputTy.getElementType(), filteredDims);
- Value zeroTensor = linalg::FillOp::create(rewriter, loc, ValueRange{zero},
- ValueRange{emptyTensor})
- .result();
+ auto aType = dyn_cast<RankedTensorType>(adaptor.getA().getType());
+ auto bType = dyn_cast<RankedTensorType>(adaptor.getB().getType());
+ auto outputType = dyn_cast<RankedTensorType>(op.getType());
+ if (!aType || !bType || !outputType)
+ return rewriter.notifyMatchFailure(op, "only supports ranked tensors");
FailureOr<int64_t> maybeAZp = op.getAZeroPoint();
FailureOr<int64_t> maybeBZp = op.getBZeroPoint();
if (failed(maybeAZp))
return rewriter.notifyMatchFailure(
- op, "input a zero point cannot be statically determined");
+ op, "input A zero point cannot be statically determined");
if (failed(maybeBZp))
return rewriter.notifyMatchFailure(
- op, "input b zero point cannot be statically determined");
+ op, "input B zero point cannot be statically determined");
const int64_t aZpVal = *maybeAZp;
const int64_t bZpVal = *maybeBZp;
if (op.verifyAZeroPoint(aZpVal).failed())
return rewriter.notifyMatchFailure(
- op, "input a zero point must be zero for non-int8 integer types");
+ op, "input A zero point must be zero for non-int8 integer types");
if (op.verifyBZeroPoint(bZpVal).failed())
return rewriter.notifyMatchFailure(
- op, "input b zero point must be zero for non-int8 integer types");
+ op, "input B zero point must be zero for non-int8 integer types");
+
+ const int64_t outputRank = outputType.getRank();
+ auto [a, b, zeroTensor] =
+ prepareMatMulOperands(rewriter, loc, adaptor.getA(), adaptor.getB(),
+ outputType, /*transposeB=*/false);
+
+ Value aZp, bZp;
+ const bool hasNonzeroZeroPoint = aZpVal != 0 || bZpVal != 0;
+ if (hasNonzeroZeroPoint) {
+ aZp = arith::ConstantOp::create(rewriter, loc,
+ rewriter.getI32IntegerAttr(aZpVal));
+ bZp = arith::ConstantOp::create(rewriter, loc,
+ rewriter.getI32IntegerAttr(bZpVal));
+ }
- if (aZpVal == 0 && bZpVal == 0) {
- rewriter.replaceOpWithNewOp<linalg::BatchMatmulOp>(
- op, TypeRange{op.getType()},
- ValueRange{adaptor.getA(), adaptor.getB()}, ValueRange{zeroTensor});
+ if (outputRank == 2) {
+ if (!hasNonzeroZeroPoint) {
+ rewriter.replaceOpWithNewOp<linalg::MatmulOp>(op, TypeRange{outputType},
+ ValueRange{a, b},
+ ValueRange{zeroTensor});
+ } else {
+ rewriter.replaceOpWithNewOp<linalg::QuantizedMatmulOp>(
+ op, TypeRange{outputType}, ValueRange{a, b, aZp, bZp},
+ ValueRange{zeroTensor});
+ }
return success();
}
- auto aZp = arith::ConstantOp::create(rewriter, loc,
- rewriter.getI32IntegerAttr(aZpVal));
- auto bZp = arith::ConstantOp::create(rewriter, loc,
- rewriter.getI32IntegerAttr(bZpVal));
- rewriter.replaceOpWithNewOp<linalg::QuantizedBatchMatmulOp>(
- op, TypeRange{op.getType()},
- ValueRange{adaptor.getA(), adaptor.getB(), aZp, bZp}, zeroTensor);
+ if (outputRank == 3) {
+ if (!hasNonzeroZeroPoint) {
+ rewriter.replaceOpWithNewOp<linalg::BatchMatmulOp>(
+ op, TypeRange{outputType}, ValueRange{a, b},
+ ValueRange{zeroTensor});
+ } else {
+ rewriter.replaceOpWithNewOp<linalg::QuantizedBatchMatmulOp>(
+ op, TypeRange{outputType}, ValueRange{a, b, aZp, bZp},
+ ValueRange{zeroTensor});
+ }
+ return success();
+ }
+
+ SmallVector<ReassociationIndices> reassociation =
+ getMatMulBatchCollapseReassociation(outputRank);
+ auto collapse = [&](Value value) {
+ auto type = cast<RankedTensorType>(value.getType());
+ auto collapsedType =
+ tensor::CollapseShapeOp::inferCollapsedType(type, reassociation);
+ return tensor::CollapseShapeOp::create(rewriter, loc, collapsedType,
+ value, reassociation);
+ };
+
+ Value collapsedA = collapse(a);
+ Value collapsedB = collapse(b);
+ Value collapsedInit = collapse(zeroTensor);
+ auto collapsedOutputType = cast<RankedTensorType>(collapsedInit.getType());
+ Value collapsedResult;
+ if (!hasNonzeroZeroPoint) {
+ collapsedResult =
+ linalg::BatchMatmulOp::create(
+ rewriter, loc, TypeRange{collapsedOutputType},
+ ValueRange{collapsedA, collapsedB}, ValueRange{collapsedInit})
+ .getResult(0);
+ } else {
+ collapsedResult = linalg::QuantizedBatchMatmulOp::create(
+ rewriter, loc, TypeRange{collapsedOutputType},
+ ValueRange{collapsedA, collapsedB, aZp, bZp},
+ ValueRange{collapsedInit})
+ .getResult(0);
+ }
+
+ Value result = tensor::ExpandShapeOp::create(
+ rewriter, loc, outputType, collapsedResult, reassociation,
+ tensor::getMixedSizes(rewriter, loc, zeroTensor));
+ rewriter.replaceOp(op, result);
return success();
}
};
-static SmallVector<ReassociationIndices>
-getMatMulTBatchCollapseReassociation(int64_t rank) {
- SmallVector<ReassociationIndices> reassociation(3);
- for (int64_t dim = 0; dim < rank - 2; ++dim)
- reassociation[0].push_back(dim);
- reassociation[1].push_back(rank - 2);
- reassociation[2].push_back(rank - 1);
- return reassociation;
-}
-
class MatMulTConverter : public OpConversionPattern<tosa::MatMulTOp> {
public:
using OpConversionPattern<tosa::MatMulTOp>::OpConversionPattern;
@@ -721,8 +756,9 @@ class MatMulTConverter : public OpConversionPattern<tosa::MatMulTOp> {
op, "nonzero zero points require the generic MATMUL_T lowering");
const int64_t outputRank = outputType.getRank();
- auto [a, b, zeroTensor] = prepareMatMulTOperands(
- rewriter, loc, adaptor.getA(), adaptor.getB(), outputType);
+ auto [a, b, zeroTensor] =
+ prepareMatMulOperands(rewriter, loc, adaptor.getA(), adaptor.getB(),
+ outputType, /*transposeB=*/true);
if (outputRank == 2) {
rewriter.replaceOpWithNewOp<linalg::MatmulTransposeBOp>(
@@ -737,7 +773,7 @@ class MatMulTConverter : public OpConversionPattern<tosa::MatMulTOp> {
}
SmallVector<ReassociationIndices> reassociation =
- getMatMulTBatchCollapseReassociation(outputRank);
+ getMatMulBatchCollapseReassociation(outputRank);
auto collapse = [&](Value value) {
auto type = cast<RankedTensorType>(value.getType());
auto collapsedType =
diff --git a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgUtils.h b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgUtils.h
index 2bc56c043a57b..e40b2d0a64216 100644
--- a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgUtils.h
+++ b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgUtils.h
@@ -25,9 +25,10 @@ struct MatMulTOperands {
/// Broadcast the batch dimensions of the ranked operands and create a
/// zero-filled result tensor for either MATMUL_T lowering pattern.
-MatMulTOperands prepareMatMulTOperands(PatternRewriter &rewriter, Location loc,
- Value a, Value b,
- RankedTensorType outputType);
+MatMulTOperands prepareMatMulOperands(PatternRewriter &rewriter, Location loc,
+ Value a, Value b,
+ RankedTensorType outputType,
+ bool transposeB);
} // namespace tosa
} // namespace mlir
diff --git a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named-invalid.mlir b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named-invalid.mlir
index d9de3f223f5d0..66be7f2603d62 100644
--- a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named-invalid.mlir
+++ b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named-invalid.mlir
@@ -25,3 +25,39 @@ func.func @avg_pool2d_adaptive_not_canonicalized(
(tensor<1x4x4x1xf32>, tensor<1xf32>, tensor<1xf32>, !tosa.shape<2>, !tosa.shape<2>, !tosa.shape<4>) -> tensor<1x2x2x1xf32>
return %0 : tensor<1x2x2x1xf32>
}
+
+// -----
+
+// Unranked operands must fail conversion cleanly before accessing their shape.
+func.func @matmul_unranked_a(
+ %a: tensor<*xf32>, %b: tensor<1x3x6xf32>) -> tensor<1x5x6xf32> {
+ %a_zp = "tosa.const"() <{values = dense<0.0> : tensor<1xf32>}> : () -> tensor<1xf32>
+ %b_zp = "tosa.const"() <{values = dense<0.0> : tensor<1xf32>}> : () -> tensor<1xf32>
+ // expected-error at +1 {{failed to legalize operation 'tosa.matmul'}}
+ %0 = tosa.matmul %a, %b, %a_zp, %b_zp : (tensor<*xf32>, tensor<1x3x6xf32>, tensor<1xf32>, tensor<1xf32>) -> tensor<1x5x6xf32>
+ return %0 : tensor<1x5x6xf32>
+}
+
+// -----
+
+// A's zero point must be statically known; keep B's zero point constant.
+func.func @matmul_nonconstant_a_zero_point(
+ %a: tensor<1x5x3xi8>, %b: tensor<1x3x6xi8>,
+ %a_zp: tensor<1xi8>) -> tensor<1x5x6xi32> {
+ %b_zp = "tosa.const"() <{values = dense<0> : tensor<1xi8>}> : () -> tensor<1xi8>
+ // expected-error at +1 {{failed to legalize operation 'tosa.matmul'}}
+ %0 = tosa.matmul %a, %b, %a_zp, %b_zp : (tensor<1x5x3xi8>, tensor<1x3x6xi8>, tensor<1xi8>, tensor<1xi8>) -> tensor<1x5x6xi32>
+ return %0 : tensor<1x5x6xi32>
+}
+
+// -----
+
+// B's zero point must be statically known after A's zero point is accepted.
+func.func @matmul_nonconstant_b_zero_point(
+ %a: tensor<1x5x3xi8>, %b: tensor<1x3x6xi8>,
+ %b_zp: tensor<1xi8>) -> tensor<1x5x6xi32> {
+ %a_zp = "tosa.const"() <{values = dense<0> : tensor<1xi8>}> : () -> tensor<1xi8>
+ // expected-error at +1 {{failed to legalize operation 'tosa.matmul'}}
+ %0 = tosa.matmul %a, %b, %a_zp, %b_zp : (tensor<1x5x3xi8>, tensor<1x3x6xi8>, tensor<1xi8>, tensor<1xi8>) -> tensor<1x5x6xi32>
+ return %0 : tensor<1x5x6xi32>
+}
diff --git a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir
index 4a052337335a7..22caf9d409eac 100644
--- a/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir
+++ b/mlir/test/Conversion/TosaToLinalg/tosa-to-linalg-named.mlir
@@ -4,15 +4,15 @@
// RUN: mlir-opt --verify-each --split-input-file -pass-pipeline="builtin.module(func.func(tosa-to-linalg-named{allow-non-finites=true}))" %s -verify-diagnostics -o -| FileCheck %s --check-prefixes=CHECK,NONFINITE
// CHECK-LABEL: @matmul
-func.func @matmul(%arg0: tensor<1x5x3xf32>, %arg1: tensor<1x3x6xf32>) -> (tensor<1x5x6xf32>) {
+func.func @matmul(%arg0: tensor<2x5x3xf32>, %arg1: tensor<2x3x6xf32>) -> (tensor<2x5x6xf32>) {
// CHECK: [[C0:%.+]] = arith.constant 0
// CHECK: [[INIT:%.+]] = tensor.empty()
- // CHECK: [[FILLED:%.+]] = linalg.fill ins([[C0]] : f32) outs([[INIT]] : tensor<1x5x6xf32>) -> tensor<1x5x6xf32>
- // CHECK: linalg.batch_matmul ins(%arg0, %arg1 : tensor<1x5x3xf32>, tensor<1x3x6xf32>) outs([[FILLED]] : tensor<1x5x6xf32>) -> tensor<1x5x6xf32>
+ // CHECK: [[FILLED:%.+]] = linalg.fill ins([[C0]] : f32) outs([[INIT]] : tensor<2x5x6xf32>) -> tensor<2x5x6xf32>
+ // CHECK: linalg.batch_matmul ins(%arg0, %arg1 : tensor<2x5x3xf32>, tensor<2x3x6xf32>) outs([[FILLED]] : tensor<2x5x6xf32>) -> tensor<2x5x6xf32>
%a_zp = "tosa.const"() <{values = dense<0.0> : tensor<1xf32>}> : () -> tensor<1xf32>
%b_zp = "tosa.const"() <{values = dense<0.0> : tensor<1xf32>}> : () -> tensor<1xf32>
- %0 = tosa.matmul %arg0, %arg1, %a_zp, %b_zp : (tensor<1x5x3xf32>, tensor<1x3x6xf32>, tensor<1xf32>, tensor<1xf32>) -> tensor<1x5x6xf32>
- return %0 : tensor<1x5x6xf32>
+ %0 = tosa.matmul %arg0, %arg1, %a_zp, %b_zp : (tensor<2x5x3xf32>, tensor<2x3x6xf32>, tensor<1xf32>, tensor<1xf32>) -> tensor<2x5x6xf32>
+ return %0 : tensor...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/232371
More information about the Mlir-commits
mailing list