[Mlir-commits] [mlir] [mlir][tosa] Lower MATMUL_T to Linalg (PR #222661)

Sayan Saha llvmlistbot at llvm.org
Mon Sep 28 05:27:52 PDT 2026


================
@@ -679,6 +680,185 @@ class MatMulConverter : public OpConversionPattern<tosa::MatMulOp> {
   }
 };
 
+// Broadcast the batch dimension of B from one to the batch dimension of A.
+// linalg.broadcast adds dimensions rather than expanding dimensions of size
+// one, so first rank-reduce B from 1xWxC to WxC.
+static Value broadcastMatMulTRhs(PatternRewriter &rewriter, Location loc,
+                                 Value a, Value b) {
+  auto bType = cast<RankedTensorType>(b.getType());
+  auto rankReducedBType =
+      RankedTensorType::get(bType.getShape().drop_front(),
+                            bType.getElementType(), bType.getEncoding());
+
+  SmallVector<OpFoldResult> offsets(3, rewriter.getIndexAttr(0));
+  SmallVector<OpFoldResult> sizes = {rewriter.getIndexAttr(1),
+                                     tensor::getMixedSize(rewriter, loc, b, 1),
+                                     tensor::getMixedSize(rewriter, loc, b, 2)};
+  SmallVector<OpFoldResult> strides(3, rewriter.getIndexAttr(1));
+  Value rankReducedB = tensor::ExtractSliceOp::create(
+      rewriter, loc, rankReducedBType, b, offsets, sizes, strides);
+
+  SmallVector<OpFoldResult> broadcastShape = {
+      tensor::getMixedSize(rewriter, loc, a, 0),
+      tensor::getMixedSize(rewriter, loc, b, 1),
+      tensor::getMixedSize(rewriter, loc, b, 2)};
+  Value broadcastInit =
+      tensor::EmptyOp::create(rewriter, loc, broadcastShape,
+                              bType.getElementType(), bType.getEncoding());
+  return linalg::BroadcastOp::create(rewriter, loc, rankReducedB, broadcastInit,
+                                     ArrayRef<int64_t>{0})
+      .getResult()
+      .front();
+}
+
+// Materialize MATMUL_T's optional B batch broadcast. When the B batch is
+// dynamic, select the broadcasted value at runtime if its size is one.
+static Value normalizeMatMulTRhsBatch(PatternRewriter &rewriter, Location loc,
+                                      Value a, Value b) {
+  auto aType = cast<RankedTensorType>(a.getType());
+  auto bType = cast<RankedTensorType>(b.getType());
+  const int64_t aBatch = aType.getDimSize(0);
+  const int64_t bBatch = bType.getDimSize(0);
+
+  if (bBatch == 1 && aBatch != 1)
+    return broadcastMatMulTRhs(rewriter, loc, a, b);
+
+  if (ShapedType::isStatic(bBatch) || aBatch == 1)
+    return b;
+
+  Value bBatchSize = tensor::DimOp::create(rewriter, loc, b, 0);
+  Value one = arith::ConstantIndexOp::create(rewriter, loc, 1);
+  Value requiresBroadcast = arith::CmpIOp::create(
+      rewriter, loc, arith::CmpIPredicate::eq, bBatchSize, one);
+
+  auto thenBuilder = [&](OpBuilder &builder, Location nestedLoc) {
+    Value broadcastedB = broadcastMatMulTRhs(rewriter, nestedLoc, a, b);
+    Value castB =
+        builder.createOrFold<tensor::CastOp>(nestedLoc, bType, broadcastedB);
+    scf::YieldOp::create(builder, nestedLoc, castB);
+  };
+  auto elseBuilder = [&](OpBuilder &builder, Location nestedLoc) {
+    scf::YieldOp::create(builder, nestedLoc, b);
+  };
+  return scf::IfOp::create(rewriter, loc, requiresBroadcast, thenBuilder,
+                           elseBuilder)
+      .getResult(0);
+}
+
+class MatMulTConverter : public OpConversionPattern<tosa::MatMulTOp> {
+public:
+  using OpConversionPattern<tosa::MatMulTOp>::OpConversionPattern;
+  LogicalResult
+  matchAndRewrite(tosa::MatMulTOp op, OpAdaptor adaptor,
+                  ConversionPatternRewriter &rewriter) const final {
+    Location loc = op.getLoc();
+    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");
+
+    if (aType.getRank() != 3 || bType.getRank() != 3)
+      return rewriter.notifyMatchFailure(
+          op, "only supports rank-3 A and B tensors");
+
+    const int64_t aBatch = aType.getDimSize(0);
+    const int64_t bBatch = bType.getDimSize(0);
+    if (aBatch == 1 && ShapedType::isStatic(bBatch) && bBatch != 1)
+      return rewriter.notifyMatchFailure(
+          op, "does not support broadcasting the A matrix");
+
+    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");
----------------
sahas3 wrote:

```suggestion
          op, "input A zero point cannot be statically determined");
```
and similar changes elsewhere 

https://github.com/llvm/llvm-project/pull/222661


More information about the Mlir-commits mailing list