Skip to content

Conversation

@rengolin
Copy link
Member

No description provided.

@llvmbot
Copy link
Member

llvmbot commented Aug 19, 2025

@llvm/pr-subscribers-mlir-linalg

Author: Renato Golin (rengolin)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/154313.diff

1 Files Affected:

  • (modified) mlir/include/mlir/Dialect/Linalg/Passes.td (+72-49)
diff --git a/mlir/include/mlir/Dialect/Linalg/Passes.td b/mlir/include/mlir/Dialect/Linalg/Passes.td
index f23662930accc..5204ee146a412 100644
--- a/mlir/include/mlir/Dialect/Linalg/Passes.td
+++ b/mlir/include/mlir/Dialect/Linalg/Passes.td
@@ -11,6 +11,78 @@
 
 include "mlir/Pass/PassBase.td"
 
+// ------------------ Begin of "form" conversions
+//
+// These conversions allow for the transformation of linalg ops between different forms.
+// Structured ops can be represented in different forms, such as named ops, category ops, and generic ops.
+//
+// The operation tree is as follows:
+//   generic     category      named
+//  ---------|-------------|----------
+//  generic ---> contract ----> matmul
+//           |              \-> batch_matmul
+//           |              \-> batch_reduce_matmul
+//           |              \-> ...
+//           \-> elementwise -> add
+//                          \-> sub
+//                          \-> ...
+//
+// Morphisms between representations can happen in the following 6 ways:
+//  generic <---> category <---> named
+//      \-------------------------/
+//
+// generic subsumes category which subsumes named.
+// The generalization path is guaranteed, the specialization path is not.
+
+def LinalgMorphOpsPass : Pass<"linalg-morph-ops"> {
+  let summary = "Convert linalg ops between forms";
+
+  let description = [{
+    Convert a linalg op from one representation to another equivalent.
+    For example, a linalg named op `linalg.add` can also be written as an
+    category op `linalg.elementwise`, and can also be re-written as
+    a `linalg.generic`, giving the morphism:
+
+      named-op <--> category_op (elementwise, contraction, ..) <--> generic
+
+    Note that the set of `linalg.generic` subsumes named and category ops
+    and therefore not all `linalg.genric` can be converted to  named or
+    category op. Similarly, catgory ops subsume named ops.
+
+    Note:
+     Legacy converters:
+     `--linalg-generalize-named-ops` is the path `named-op --> generic-op`
+     `--linalg-specialize-generic-ops` is the path `named-op <-- generic-op`
+  }];
+  let dependentDialects = ["linalg::LinalgDialect"];
+
+  let options = [
+    // Generalization path is guaranteed.
+    Option<"namedToCategory", "named-to-category", "bool", /*default=*/"false",
+           "convert named ops to category op e.g. `linalg.elementwise`">,
+    Option<"categoryToGeneric", "category-to-generic", "bool", /*default=*/"false",
+           "convert category ops e.g. `linalg.elementwise` to `linalg.generic`">,
+    Option<"namedToGeneric", "named-to-generic", "bool", /*default=*/"false",
+           "convert named ops e.g. `linalg.add` to `linalg.generic`">,
+    
+    // Specialization path is not guaranteed.
+    Option<"genericToNamed", "generic-to-named", "bool", /*default=*/"false",
+           "convert linalg.generic to equivalent named ops"> ];
+    //  TODOs: `generic-to-category`, `category-to-named`
+}
+
+def LinalgGeneralizeNamedOpsPass : Pass<"linalg-generalize-named-ops">, Deprecated<"Use 'linalg-morph-ops' instead."> {
+  let summary = "Convert named ops into generic 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";
+  let dependentDialects = ["linalg::LinalgDialect"];
+}
+
+// ------------------ End of "form" conversions
+
 def ConvertElementwiseToLinalgPass : Pass<"convert-elementwise-to-linalg", ""> {
   let summary = "Convert ElementwiseMappable ops to linalg";
   let description = [{
@@ -89,55 +161,6 @@ def LinalgInlineScalarOperandsPass : Pass<"linalg-inline-scalar-operands"> {
   ];
 }
 
-def LinalgMorphOpsPass : Pass<"linalg-morph-ops"> {
-  let summary = "Convert named op to category ops or generic and vice-versa";
-
-  let description = [{
-    Convert a linalg op from one representation to another equivalent.
-    For example, a linalg named op `linalg.add` can also be written as an
-    category op `linalg.elementwise`, and can also be re-written as
-    a `linalg.generic`, giving the morphism:
-
-      named-op <--> category_op (elementwise, contraction, ..) <--> generic
-
-    Note that the set of `linalg.generic` subsumes named and category ops
-    and therefore not all `linalg.genric` can be converted to  named or
-    category op. Similarly, catgory ops subsume named ops.
-
-    Note:
-     Legacy converters:
-     `--linalg-generalize-named-ops` is the path `named-op --> generic-op`
-     `--linalg-specialize-generic-ops` is the path `named-op <-- generic-op`
-  }];
-  let dependentDialects = ["linalg::LinalgDialect"];
-
-  let options = [
-    // named-op <--> category <--> generic
-
-    // Lowering options
-    Option<"namedToCategory", "named-to-category", "bool", /*default=*/"false",
-           "convert named ops to category op e.g. `linalg.elementwise`">,
-    Option<"categoryToGeneric", "category-to-generic", "bool", /*default=*/"false",
-           "convert category ops e.g. `linalg.elementwise` to `linalg.generic`">,
-    Option<"namedToGeneric", "named-to-generic", "bool", /*default=*/"false",
-           "convert named ops e.g. `linalg.add` to `linalg.generic`">,
-
-    // Lifting options
-    //  TODOs: `generic-to-category`, `category-to-named`
-    Option<"genericToNamed", "generic-to-named", "bool", /*default=*/"false",
-           "convert linalg.generic to equivalent named ops"> ];
-}
-
-def LinalgGeneralizeNamedOpsPass : Pass<"linalg-generalize-named-ops"> {
-  let summary = "Convert named ops into generic ops";
-  let dependentDialects = ["linalg::LinalgDialect"];
-}
-
-def LinalgSpecializeGenericOpsPass : Pass<"linalg-specialize-generic-ops"> {
-  let summary = "Convert generic ops back to named ops";
-  let dependentDialects = ["linalg::LinalgDialect"];
-}
-
 def LinalgFoldIntoElementwisePass : Pass<"linalg-fold-into-elementwise"> {
   let summary = "Fold transform, broadcast and other ops into elementwise";
   let dependentDialects = ["linalg::LinalgDialect"];

@llvmbot
Copy link
Member

llvmbot commented Aug 19, 2025

@llvm/pr-subscribers-mlir

Author: Renato Golin (rengolin)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/154313.diff

1 Files Affected:

  • (modified) mlir/include/mlir/Dialect/Linalg/Passes.td (+72-49)
diff --git a/mlir/include/mlir/Dialect/Linalg/Passes.td b/mlir/include/mlir/Dialect/Linalg/Passes.td
index f23662930accc..5204ee146a412 100644
--- a/mlir/include/mlir/Dialect/Linalg/Passes.td
+++ b/mlir/include/mlir/Dialect/Linalg/Passes.td
@@ -11,6 +11,78 @@
 
 include "mlir/Pass/PassBase.td"
 
+// ------------------ Begin of "form" conversions
+//
+// These conversions allow for the transformation of linalg ops between different forms.
+// Structured ops can be represented in different forms, such as named ops, category ops, and generic ops.
+//
+// The operation tree is as follows:
+//   generic     category      named
+//  ---------|-------------|----------
+//  generic ---> contract ----> matmul
+//           |              \-> batch_matmul
+//           |              \-> batch_reduce_matmul
+//           |              \-> ...
+//           \-> elementwise -> add
+//                          \-> sub
+//                          \-> ...
+//
+// Morphisms between representations can happen in the following 6 ways:
+//  generic <---> category <---> named
+//      \-------------------------/
+//
+// generic subsumes category which subsumes named.
+// The generalization path is guaranteed, the specialization path is not.
+
+def LinalgMorphOpsPass : Pass<"linalg-morph-ops"> {
+  let summary = "Convert linalg ops between forms";
+
+  let description = [{
+    Convert a linalg op from one representation to another equivalent.
+    For example, a linalg named op `linalg.add` can also be written as an
+    category op `linalg.elementwise`, and can also be re-written as
+    a `linalg.generic`, giving the morphism:
+
+      named-op <--> category_op (elementwise, contraction, ..) <--> generic
+
+    Note that the set of `linalg.generic` subsumes named and category ops
+    and therefore not all `linalg.genric` can be converted to  named or
+    category op. Similarly, catgory ops subsume named ops.
+
+    Note:
+     Legacy converters:
+     `--linalg-generalize-named-ops` is the path `named-op --> generic-op`
+     `--linalg-specialize-generic-ops` is the path `named-op <-- generic-op`
+  }];
+  let dependentDialects = ["linalg::LinalgDialect"];
+
+  let options = [
+    // Generalization path is guaranteed.
+    Option<"namedToCategory", "named-to-category", "bool", /*default=*/"false",
+           "convert named ops to category op e.g. `linalg.elementwise`">,
+    Option<"categoryToGeneric", "category-to-generic", "bool", /*default=*/"false",
+           "convert category ops e.g. `linalg.elementwise` to `linalg.generic`">,
+    Option<"namedToGeneric", "named-to-generic", "bool", /*default=*/"false",
+           "convert named ops e.g. `linalg.add` to `linalg.generic`">,
+    
+    // Specialization path is not guaranteed.
+    Option<"genericToNamed", "generic-to-named", "bool", /*default=*/"false",
+           "convert linalg.generic to equivalent named ops"> ];
+    //  TODOs: `generic-to-category`, `category-to-named`
+}
+
+def LinalgGeneralizeNamedOpsPass : Pass<"linalg-generalize-named-ops">, Deprecated<"Use 'linalg-morph-ops' instead."> {
+  let summary = "Convert named ops into generic 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";
+  let dependentDialects = ["linalg::LinalgDialect"];
+}
+
+// ------------------ End of "form" conversions
+
 def ConvertElementwiseToLinalgPass : Pass<"convert-elementwise-to-linalg", ""> {
   let summary = "Convert ElementwiseMappable ops to linalg";
   let description = [{
@@ -89,55 +161,6 @@ def LinalgInlineScalarOperandsPass : Pass<"linalg-inline-scalar-operands"> {
   ];
 }
 
-def LinalgMorphOpsPass : Pass<"linalg-morph-ops"> {
-  let summary = "Convert named op to category ops or generic and vice-versa";
-
-  let description = [{
-    Convert a linalg op from one representation to another equivalent.
-    For example, a linalg named op `linalg.add` can also be written as an
-    category op `linalg.elementwise`, and can also be re-written as
-    a `linalg.generic`, giving the morphism:
-
-      named-op <--> category_op (elementwise, contraction, ..) <--> generic
-
-    Note that the set of `linalg.generic` subsumes named and category ops
-    and therefore not all `linalg.genric` can be converted to  named or
-    category op. Similarly, catgory ops subsume named ops.
-
-    Note:
-     Legacy converters:
-     `--linalg-generalize-named-ops` is the path `named-op --> generic-op`
-     `--linalg-specialize-generic-ops` is the path `named-op <-- generic-op`
-  }];
-  let dependentDialects = ["linalg::LinalgDialect"];
-
-  let options = [
-    // named-op <--> category <--> generic
-
-    // Lowering options
-    Option<"namedToCategory", "named-to-category", "bool", /*default=*/"false",
-           "convert named ops to category op e.g. `linalg.elementwise`">,
-    Option<"categoryToGeneric", "category-to-generic", "bool", /*default=*/"false",
-           "convert category ops e.g. `linalg.elementwise` to `linalg.generic`">,
-    Option<"namedToGeneric", "named-to-generic", "bool", /*default=*/"false",
-           "convert named ops e.g. `linalg.add` to `linalg.generic`">,
-
-    // Lifting options
-    //  TODOs: `generic-to-category`, `category-to-named`
-    Option<"genericToNamed", "generic-to-named", "bool", /*default=*/"false",
-           "convert linalg.generic to equivalent named ops"> ];
-}
-
-def LinalgGeneralizeNamedOpsPass : Pass<"linalg-generalize-named-ops"> {
-  let summary = "Convert named ops into generic ops";
-  let dependentDialects = ["linalg::LinalgDialect"];
-}
-
-def LinalgSpecializeGenericOpsPass : Pass<"linalg-specialize-generic-ops"> {
-  let summary = "Convert generic ops back to named ops";
-  let dependentDialects = ["linalg::LinalgDialect"];
-}
-
 def LinalgFoldIntoElementwisePass : Pass<"linalg-fold-into-elementwise"> {
   let summary = "Fold transform, broadcast and other ops into elementwise";
   let dependentDialects = ["linalg::LinalgDialect"];

@rengolin rengolin merged commit 5cc8c92 into llvm:main Aug 19, 2025
9 checks passed
@rengolin rengolin deleted the morph-linalg branch August 19, 2025 16:51
@rolfmorel
Copy link
Contributor

I will just point out that in the context of just linalg's structured ops, the term "category" could/should be "einsum" - that's the kind of operation modelled by linalg.contract and linalg.elementwise together.

The term "category" seems more helpful in the context of there being a "category" (i.e. a tree) of structured ops and there being a distinct "category" (i.e. tree, though potentially a trivial one) for convolution ops.

Wouldn't want to be missing the forest due to thinking we are seeing just a single tree 😉

@rengolin
Copy link
Member Author

einsum encompasses contract and elementwise but not the others. "Category" is the name for the ops that have "children" and are themselves a sub-tree.

Let's see what people think about this in the RFC, and we can rename things accordingly.

@rolfmorel
Copy link
Contributor

I don't mind the definition: "Category" is the name for the ops that have "children" and are themselves a sub-tree.

As far as I can tell though, non of the other groups of ops will have linalg.generic as their ancestor (if they have an ancestor at all). The current docs appear to say that the einsum group is the thing that's called "category". Rather than the einsum group just being an instance of a category.

@rengolin
Copy link
Member Author

The current docs appear to say that the einsum group is the thing that's called "category". Rather than the einsum group just being an instance of a category.

Documentation, amirite? 🤷 😆

Let's decide nomenclature after we agree on semantics. (then fix the docs)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants