[Mlir-commits] [mlir] [MLIR][Linalg] Improve morphism semantics (PR #222959)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Sep 11 07:42:31 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Renato Golin (rengolin)

<details>
<summary>Changes</summary>

As exposed on the issues linked, the linalg morphism semantics could be improved.

This PR changes the following semantics:
* Completes support for `-linalg-morph-ops` to include contractions and ternary elementwise.
* Adds a `-linalg-categorize-ops`, which moves both named and generic ops to category (100% code reuse).
* Adds the possibility of "stopping at category" for both specialization and generalization (not accessible from command line options but used by the new categorize / old morph-ops pass).
* Removes the morphism options, since it's just a boolean flag.
* Improve test coverage, and make sure that `-linalg-morph-ops` does the same thing as the other options.

It does NOT:
* Change the semantics of generalization, which continues to generalize everything.
* Change the semantics of specialization, which continues to convert to named or category whenever possible.

There's also come cleanups for the matching of elementwise that was simpler as a `TypeSwitch`.

Fixes #<!-- -->221266 and #<!-- -->221191 

---

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


15 Files Affected:

- (modified) mlir/include/mlir/Dialect/Linalg/IR/Linalg.h (+15) 
- (modified) mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h (+12-10) 
- (modified) mlir/include/mlir/Dialect/Linalg/Passes.td (+21-9) 
- (modified) mlir/include/mlir/Dialect/Linalg/Transforms/Transforms.h (+47-24) 
- (modified) mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp (+24-14) 
- (modified) mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp (+1-13) 
- (modified) mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp (+1-3) 
- (modified) mlir/lib/Dialect/Linalg/Transforms/Generalization.cpp (+45-5) 
- (modified) mlir/lib/Dialect/Linalg/Transforms/MorphOps.cpp (+27-6) 
- (modified) mlir/lib/Dialect/Linalg/Transforms/Specialize.cpp (+169-195) 
- (modified) mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp (+5-3) 
- (added) mlir/test/Dialect/Linalg/categorize-named-ops.mlir (+377) 
- (modified) mlir/test/Dialect/Linalg/decompose-generic-by-unfolding-projected-permutation.mlir (+3-11) 
- (modified) mlir/test/Dialect/Linalg/generalize-named-ops.mlir (+2) 
- (modified) mlir/test/Dialect/Linalg/specialize-generic-ops.mlir (+270-216) 


``````````diff
diff --git a/mlir/include/mlir/Dialect/Linalg/IR/Linalg.h b/mlir/include/mlir/Dialect/Linalg/IR/Linalg.h
index 9d46f9aeb9c15..aaf4a61b19567 100644
--- a/mlir/include/mlir/Dialect/Linalg/IR/Linalg.h
+++ b/mlir/include/mlir/Dialect/Linalg/IR/Linalg.h
@@ -147,6 +147,21 @@ template <typename OpTy,
                                       std::is_same_v<OpTy, linalg::UnPackOp>>>
 SmallVector<int64_t> getPackedOuterShapeWithoutTransposition(OpTy packOrUnPack);
 
+/// Elementwise Arity and Kind groups.
+struct ArityGroupAndKind {
+  // The enum class {Unary, Binary, Ternary, ..}
+  ElementwiseArityGroup arityGroup;
+
+  // The kind (e.g. `exp` or `add`) belonging to the arity group.
+  union Kind {
+    UnaryFn unaryFn;
+    BinaryFn binaryFn;
+    TernaryFn ternaryFn;
+  } kind;
+};
+ArityGroupAndKind getArityGroupAndKind(ElementwiseKind kind);
+
+
 /// Specialization of `linalg.matmul` op that has a transpose map on A
 class MatmulTransposeAOp : public MatmulOp {
   /// Create an affine map for a transpose-A matmul. Used only in the builders.
diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h b/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h
index 28039eb8230fb..ecb2397a21689 100644
--- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h
+++ b/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h
@@ -149,22 +149,24 @@ isaTransposeOpInterface(GenericOp genericOp);
 /// Checks whether a given `genericOp` is semantically equivalent to a single
 /// linalg elementwise unary op, e.g.
 /// `linalg.elementwise <exp>`.
-/// If `allowNonIdentityMaps` is true, operations with custom indexing maps are
-/// included in the check. Note that these operations can only be represented by
-/// the category op.
+/// Operations with non-identity indexing maps are included in the check, as
+/// they can be represented by the category op.
 /// A linalg.generic body could be a series of unary elementwise ops e.g.
 /// `exp(neg(x))`, such as formed by linalg op fusion. Here we restrict it to
 /// detecting cases where body is a single computation op.
-bool isaElemwiseSingleUnaryOpInterface(GenericOp genericOp,
-                                       bool allowNonIdentityMaps = false);
+bool isaElemwiseSingleUnaryOpInterface(GenericOp genericOp);
 
 /// Checks whether `genericOp` is semantically equivalent to a single linalg
 /// elementwise binary op e.g. linalg.sub.
-/// If `allowNonIdentityMaps` is true, operations with custom indexing maps are
-/// included in the check. Note that these operations can only be represented by
-/// the category op.
-bool isaElemwiseSingleBinaryOpInterface(GenericOp genericOp,
-                                        bool allowNonIdentityMaps = false);
+/// Operations with non-identity indexing maps are included in the check, as
+/// they can be represented by the category op.
+bool isaElemwiseSingleBinaryOpInterface(GenericOp genericOp);
+
+/// Checks whether `genericOp` is semantically equivalent to a single linalg
+/// elementwise ternary op e.g. linalg.fma.
+/// Operations with non-identity indexing maps are included in the check, as
+/// they can be represented by the category op.
+bool isaElemwiseSingleTernaryOpInterface(GenericOp genericOp);
 
 /// Checks whether `genericOp` is semantically equivalent to a `linalg.fill`.
 /// Supports two patterns:
diff --git a/mlir/include/mlir/Dialect/Linalg/Passes.td b/mlir/include/mlir/Dialect/Linalg/Passes.td
index 1a9268d514e01..c6a49f37e2879 100644
--- a/mlir/include/mlir/Dialect/Linalg/Passes.td
+++ b/mlir/include/mlir/Dialect/Linalg/Passes.td
@@ -29,9 +29,17 @@ include "mlir/IR/Constraints.td"
 //                          \-> sub
 //                          \-> ...
 //
-// Morphisms between representations can happen in the following 6 ways:
-//  generic <---> category <---> named
-//      \-------------------------/
+// Morphisms between representations can happen in the following ways:
+//  * Specialize: Convert `generic` operations to either:
+//       * `named`, where the interfaces match and there is such op, OR
+//       * `category`, where enough interfaces still match, OR
+//       * `category`, when the `emitCategory` flag is set (regardless of named ops)
+//  * Generalize: Convert `named` or `category` operations to `generic` operations
+//       * Any structured operation can be converted to `generic`
+//  * Categorize: Convert both `named` and `generic` operations to `category` operations:
+//       * `elementwise`, for non-contracting `generic` operations that match the interfaces, OR
+//       * `contract`, for contracting `generic` operations that match the interfaces
+//       * `contract`, for contracting `named` operations that match the interfaces
 //
 // generic subsumes category which subsumes structured named (not softmax,
 // convolutions, etc). The generalization path is guaranteed, the
@@ -77,14 +85,18 @@ def LinalgMorphOpsPass : Pass<"linalg-morph-ops"> {
            "convert category ops e.g. `linalg.elementwise` to equivalent named ops"> ];
 }
 
-def LinalgGeneralizeNamedOpsPass : Pass<"linalg-generalize-named-ops">,
-                                   Deprecated<"Use 'linalg-morph-ops' instead."> {
-  let summary = "Convert named ops into generic ops";
+def LinalgGeneralizeNamedOpsPass : Pass<"linalg-generalize-named-ops"> {
+  let summary = "Convert `named` and `category` ops into `generic` ops";
+  let dependentDialects = ["linalg::LinalgDialect"];
+}
+
+def LinalgSpecializeGenericOpsPass : Pass<"linalg-specialize-generic-ops"> {
+  let summary = "Convert `generic` ops into `named` or `category` ops";
+  let dependentDialects = ["linalg::LinalgDialect"];
 }
 
-def LinalgSpecializeGenericOpsPass : Pass<"linalg-specialize-generic-ops">,
-                                     Deprecated<"Use 'linalg-morph-ops' instead."> {
-  let summary = "Convert generic ops back to named ops";
+def LinalgCategorizeOpsPass : Pass<"linalg-categorize-ops"> {
+  let summary = "Convert `named` and `generic` ops into `category` ops";
   let dependentDialects = ["linalg::LinalgDialect"];
 }
 
diff --git a/mlir/include/mlir/Dialect/Linalg/Transforms/Transforms.h b/mlir/include/mlir/Dialect/Linalg/Transforms/Transforms.h
index cbc338c9f1b32..699ffa61a8863 100644
--- a/mlir/include/mlir/Dialect/Linalg/Transforms/Transforms.h
+++ b/mlir/include/mlir/Dialect/Linalg/Transforms/Transforms.h
@@ -917,21 +917,17 @@ FailureOr<GenericOp> interchangeGenericOp(RewriterBase &rewriter,
                                           GenericOp genericOp,
                                           ArrayRef<unsigned> interchangeVector);
 
-/// Create a GenericOp from the given named operation `linalgOp` and replace
-/// the given `linalgOp`.
-/// Return failure if `linalgOp` is a GenericOp or misses a region builder.
-FailureOr<GenericOp> generalizeNamedOp(RewriterBase &rewriter,
-                                       LinalgOp linalgOp);
-
-struct GenericOpSpecializationOptions {
-  // Specialize generics to category ops (default: named ops).
-  bool emitCategoryOps = false;
-};
+/// Create a GenericOp or CategoryOp from the given named operation `linalgOp`
+/// and replace the given `linalgOp`. Return failure if `linalgOp` is a
+/// GenericOp or misses a region builder.
+FailureOr<LinalgOp> generalizeNamedOp(RewriterBase &rewriter,
+                                      LinalgOp linalgOp,
+                                      bool emitCategoryOps = false);
 
 /// Replace the given GenericOp with a namedOp or categoryOp.
-FailureOr<LinalgOp>
-specializeGenericOp(RewriterBase &rewriter, GenericOp genericOp,
-                    const GenericOpSpecializationOptions &options = {});
+FailureOr<LinalgOp> specializeGenericOp(RewriterBase &rewriter,
+                                        GenericOp genericOp,
+                                        bool emitCategoryOps = false);
 
 /// Create a new buffer using the `allocationFn` provided. The size of this
 /// buffer is either the original subview size when 'useOriginalSubviewSize' is
@@ -1671,31 +1667,38 @@ FailureOr<LinalgOp> downscaleSizeOneWindowedConvolution(RewriterBase &rewriter,
 // returning FailureOr<GenericOp>.
 struct LinalgGeneralizationPattern
     : public OpInterfaceRewritePattern<LinalgOp> {
-  using OpInterfaceRewritePattern<LinalgOp>::OpInterfaceRewritePattern;
+
+  LinalgGeneralizationPattern(
+      MLIRContext *context, bool emitCategoryOps = false,
+      PatternBenefit benefit = 1)
+      : OpInterfaceRewritePattern<LinalgOp>(context, benefit), emitCategoryOps(emitCategoryOps) {}
 
   /// `matchAndRewrite` implementation that returns the significant
   /// transformed pieces of IR.
-  FailureOr<GenericOp>
+  FailureOr<LinalgOp>
   returningMatchAndRewrite(LinalgOp op, PatternRewriter &rewriter) const {
-    return generalizeNamedOp(rewriter, op);
+    return generalizeNamedOp(rewriter, op, emitCategoryOps);
   }
 
   LogicalResult matchAndRewrite(LinalgOp op,
                                 PatternRewriter &rewriter) const override {
     return returningMatchAndRewrite(op, rewriter);
   }
+
+private:
+  bool emitCategoryOps;
 };
 
 struct LinalgSpecializationPattern : public OpRewritePattern<GenericOp> {
 
   LinalgSpecializationPattern(
-      MLIRContext *context, const GenericOpSpecializationOptions &options = {},
+      MLIRContext *context, bool emitCategoryOps = false,
       PatternBenefit benefit = 1)
-      : OpRewritePattern<GenericOp>(context, benefit), options(options) {}
+      : OpRewritePattern<GenericOp>(context, benefit), emitCategoryOps(emitCategoryOps) {}
 
   FailureOr<GenericOp>
   returningMatchAndRewrite(GenericOp op, PatternRewriter &rewriter) const {
-    return specializeGenericOp(rewriter, op, options);
+    return specializeGenericOp(rewriter, op, emitCategoryOps);
   }
 
   LogicalResult matchAndRewrite(GenericOp op,
@@ -1704,7 +1707,27 @@ struct LinalgSpecializationPattern : public OpRewritePattern<GenericOp> {
   }
 
 private:
-  GenericOpSpecializationOptions options;
+  bool emitCategoryOps;
+};
+
+struct LinalgCategorizationPattern : public OpInterfaceRewritePattern<LinalgOp> {
+
+  LinalgCategorizationPattern(
+      MLIRContext *context, PatternBenefit benefit = 1)
+      : OpInterfaceRewritePattern<LinalgOp>(context, benefit) {}
+
+  FailureOr<LinalgOp>
+  returningMatchAndRewrite(LinalgOp op, PatternRewriter &rewriter) const {
+    bool emitCategoryOps = true;
+    if (GenericOp generic = dyn_cast<GenericOp>(*op))
+      return specializeGenericOp(rewriter, generic, emitCategoryOps);
+    return generalizeNamedOp(rewriter, op, emitCategoryOps);
+  }
+
+  LogicalResult matchAndRewrite(LinalgOp op,
+                                PatternRewriter &rewriter) const override {
+    return returningMatchAndRewrite(op, rewriter);
+  }
 };
 
 /// Vectorization pattern for memref::CopyOp.
@@ -1911,7 +1934,8 @@ void populateLinalgTilingCanonicalizationPatterns(RewritePatternSet &patterns);
 
 /// Populates `patterns` with patterns to convert spec-generated named ops to
 /// linalg.generic ops.
-void populateLinalgNamedOpsGeneralizationPatterns(RewritePatternSet &patterns);
+void populateLinalgNamedOpsGeneralizationPatterns(RewritePatternSet &patterns,
+                                                  bool emitCategoryOps = false);
 
 /// Populates `patterns` with patterns to convert linalg.generic ops to named
 /// or category ops where possible. A linalg.generic can represent wide range
@@ -1919,9 +1943,8 @@ void populateLinalgNamedOpsGeneralizationPatterns(RewritePatternSet &patterns);
 /// e.g. linalg.generic that takes a tensor and computes a polynomial such as:
 ///     p(x) = an*x^n + ... + a1x + a0
 /// There is no equivalent named op to convert to. Many such cases exist.
-void populateLinalgGenericOpsSpecializationPatterns(
-    RewritePatternSet &patterns,
-    const GenericOpSpecializationOptions &options = {});
+void populateLinalgGenericOpsSpecializationPatterns(RewritePatternSet &patterns,
+                                                    bool emitCategoryOps = false);
 
 /// Populates `patterns` with patterns that fold operations like
 /// `linalg.transform` into elementwise op map.
diff --git a/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
index cff613f389669..e1152b287330b 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
@@ -228,18 +228,15 @@ linalg::isaTransposeOpInterface(GenericOp op) {
 // Elementwise Single Unary/Binary-OpInterface implementation
 //===----------------------------------------------------------------------===//
 static bool
-isaElemwiseSingleUnaryOrBinaryOpInterface(linalg::GenericOp op, unsigned arity,
-                                          bool allowNonIdentityMaps) {
+isaElemwiseSingleUnaryOrBinaryOpInterface(linalg::GenericOp op,
+                                          unsigned arity) {
   // Check all loops are parallel.
   if (!op.isAllParallelLoops() || op.getNumLoops() < 1)
     return false;
 
-  // Check there are arity-inputs, 1-output and all are identity-maps (unless
-  // requested otherwise).
-  if (op.getNumDpsInputs() != arity || op.getNumDpsInits() != 1 ||
-      (!allowNonIdentityMaps &&
-       !llvm::all_of(op.getIndexingMapsArray(),
-                     [](AffineMap map) { return map.isIdentity(); })))
+  // Check there are arity-inputs and 1-output. Non-identity indexing maps are
+  // allowed as they can be represented by the category op.
+  if (op.getNumDpsInputs() != arity || op.getNumDpsInits() != 1)
     return false;
 
   // Init should not be referenced for elementwise operations.
@@ -267,10 +264,9 @@ isaElemwiseSingleUnaryOrBinaryOpInterface(linalg::GenericOp op, unsigned arity,
            yieldOp->getOperand(0).getDefiningOp() != oper);
 }
 
-bool linalg::isaElemwiseSingleUnaryOpInterface(linalg::GenericOp op,
-                                               bool allowNonIdentityMaps) {
+bool linalg::isaElemwiseSingleUnaryOpInterface(linalg::GenericOp op) {
   // All basic elemwise checks.
-  if (!isaElemwiseSingleUnaryOrBinaryOpInterface(op, 1, allowNonIdentityMaps))
+  if (!isaElemwiseSingleUnaryOrBinaryOpInterface(op, 1))
     return false;
 
   // Check input is actually used.
@@ -279,10 +275,9 @@ bool linalg::isaElemwiseSingleUnaryOpInterface(linalg::GenericOp op,
   return true;
 }
 
-bool linalg::isaElemwiseSingleBinaryOpInterface(linalg::GenericOp op,
-                                                bool allowNonIdentityMaps) {
+bool linalg::isaElemwiseSingleBinaryOpInterface(linalg::GenericOp op) {
   // All basic elemwise checks.
-  if (!isaElemwiseSingleUnaryOrBinaryOpInterface(op, 2, allowNonIdentityMaps))
+  if (!isaElemwiseSingleUnaryOrBinaryOpInterface(op, 2))
     return false;
 
   // Check both inputs are used (elementwise).
@@ -292,6 +287,21 @@ bool linalg::isaElemwiseSingleBinaryOpInterface(linalg::GenericOp op,
            !op.payloadUsesValueFromOperand(inputOpOperand1));
 }
 
+bool linalg::isaElemwiseSingleTernaryOpInterface(linalg::GenericOp op) {
+  // All basic elemwise checks.
+  if (!isaElemwiseSingleUnaryOrBinaryOpInterface(op, 3))
+    return false;
+
+  // Check all three inputs are used (elementwise).
+  OpOperand *inputOpOperand0 = op.getDpsInputOperand(0);
+  OpOperand *inputOpOperand1 = op.getDpsInputOperand(1);
+  OpOperand *inputOpOperand2 = op.getDpsInputOperand(2);
+  return !(!op.payloadUsesValueFromOperand(inputOpOperand0) ||
+           !op.payloadUsesValueFromOperand(inputOpOperand1) ||
+           !op.payloadUsesValueFromOperand(inputOpOperand2));
+}
+
+
 //===----------------------------------------------------------------------===//
 // ContractionOpInterface implementation
 //===----------------------------------------------------------------------===//
diff --git a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
index 0909732728eca..87b14439597b4 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
@@ -5081,24 +5081,12 @@ Speculation::Speculatability BatchMatmulOp::getSpeculatability() {
 //===----------------------------------------------------------------------===//
 //
 namespace {
-struct ArityGroupAndKind {
-  // The enum class {Unary, Binary, Ternary, ..}
-  ElementwiseArityGroup arityGroup;
-
-  // The kind (e.g. `exp` or `add`) belonging to the arity group.
-  union Kind {
-    UnaryFn unaryFn;
-    BinaryFn binaryFn;
-    TernaryFn ternaryFn;
-  } kind;
-};
-
 unsigned getArityGroupAsUInt(ElementwiseArityGroup arityGroup) {
   return static_cast<unsigned>(arityGroup);
 }
 } // namespace
 
-static ArityGroupAndKind getArityGroupAndKind(ElementwiseKind kind) {
+ArityGroupAndKind getArityGroupAndKind(ElementwiseKind kind) {
   constexpr int lastUnary = static_cast<int>(ElementwiseCaseLimits::LastUnary);
   constexpr int lastBinary =
       static_cast<int>(ElementwiseCaseLimits::LastBinary);
diff --git a/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp b/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
index 74cba3c6ac484..9948d7b12d859 100644
--- a/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
+++ b/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
@@ -1449,10 +1449,8 @@ transform::SpecializeOp::applyToOne(transform::TransformRewriter &rewriter,
     return DiagnosedSilenceableFailure::success();
   }
   rewriter.setInsertionPoint(target);
-  GenericOpSpecializationOptions opts;
-  opts.emitCategoryOps = getEmitCategory();
   FailureOr<LinalgOp> named =
-      specializeGenericOp(rewriter, cast<GenericOp>(target), opts);
+      specializeGenericOp(rewriter, cast<GenericOp>(target), getEmitCategory());
   if (succeeded(named)) {
     results.push_back(named->getOperation());
     return DiagnosedSilenceableFailure::success();
diff --git a/mlir/lib/Dialect/Linalg/Transforms/Generalization.cpp b/mlir/lib/Dialect/Linalg/Transforms/Generalization.cpp
index 35abdc027f194..dd3a89bc21000 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/Generalization.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/Generalization.cpp
@@ -43,11 +43,51 @@ static LogicalResult generalizeNamedOpPrecondition(LinalgOp linalgOp) {
   return success();
 }
 
-FailureOr<GenericOp> mlir::linalg::generalizeNamedOp(RewriterBase &rewriter,
-                                                     LinalgOp linalgOp) {
+// Converts a named matmul-like op (`matmul`, `batch_matmul`, or
+// `batch_reduce_matmul`) into a `linalg.contract` category op, preserving the
+// operand indexing maps and cast semantics. Returns failure for other ops.
+static FailureOr<LinalgOp> generalizeToContractOp(RewriterBase &rewriter,
+                                                  LinalgOp namedOp) {
+  if (!isa<MatmulOp, BatchMatmulOp, BatchReduceMatmulOp>(
+          namedOp.getOperation()))
+    return failure();
+
+  SmallVector<NamedAttribute> attributes;
+
+  // Preserve operand indexing semantics (transposition, batch/reduction dims)
+  // via the named op's indexing maps.
+  SmallVector<Attribute> indexingMaps = llvm::map_to_vector(
+      namedOp.getIndexingMapsArray(),
+      [](AffineMap map) -> Attribute { return AffineMapAttr::get(map); });
+  attributes.push_back(rewriter.getNamedAttr(
+      "indexing_maps", rewriter.getArrayAttr(indexingMaps)));
+
+  // Only the unsigned cast needs to be explicit; signed is the default.
+  if (auto castAttr = namedOp->getAttrOfType<TypeFnAttr>("cast");
+      castAttr && castAttr.getValue() == TypeFn::cast_unsigned)
+    attributes.push_back(rewriter.getNamedAttr("cast", castAttr));
+
+  LinalgOp contractOp = rewriter.replaceOpWithNewOp<ContractOp>(
+      namedOp,
+      ValueRange{namedOp.getDpsInputs()[0], namedOp.getDpsInputs()[1]},
+      ValueRange{namedOp.getDpsInits()[0]}, attributes);
+  return contractOp;
+}
+
+FailureOr<LinalgOp> mlir::linalg::generalizeNamedOp(RewriterBase &rewriter,
+                                                    LinalgOp linalgOp,
+                                                    bool emitCategoryOps) {
   if (failed(generalizeNamedOpPrecondition(linalgOp)))
     return rewriter.notifyMatchFailure(linalgOp, "preconditions not met");
 
+  // Emit the `linalg.contract` category op for matmul-like named ops.
+  if (emitCategoryOps) {
+    FailureOr<LinalgOp> contractOp = generalizeToContractOp(rewriter, linalgOp);
+    if (suc...
[truncated]

``````````

</details>


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


More information about the Mlir-commits mailing list