[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