xref: /llvm-project/mlir/docs/Tutorials/transform/Ch2.md (revision 5a9bdd85ee4d8527e2cedf44f3ce26ff414f9b6a)
168ae0d78SAlex Zinenko# Chapter 2: Adding a Simple New Transformation Operation
268ae0d78SAlex Zinenko
368ae0d78SAlex Zinenko## Setting Up to Add New Transformations
468ae0d78SAlex Zinenko
568ae0d78SAlex ZinenkoBefore defining a new transform operation, we need to choose where its implementation should be located. While MLIR encourages upstream contributions, it is not always possible or even desirable to modify the main Transform dialect, for example, if the transformation is specific to some out-of-tree dialect that is not itself available upstream.
668ae0d78SAlex Zinenko
768ae0d78SAlex ZinenkoThe Transform dialect uses the dialect extension mechanism to allow additional operations to be injected without modifying the dialect itself. Dialect extensions are registered with the context and loaded when the dialect itself is loaded. Extension definition is straightforward:
868ae0d78SAlex Zinenko
968ae0d78SAlex Zinenko```cpp
1068ae0d78SAlex Zinenko// In MyExtension.cpp.
1168ae0d78SAlex Zinenko#include "mlir/Dialect/Transform/IR/TransformDialect.h"
1268ae0d78SAlex Zinenko
13b33b91a2SOleksandr "Alex" Zinenko// Define a new Transform dialect extension. This uses the CRTP idiom to
14b33b91a2SOleksandr "Alex" Zinenko// identify extensions.
1568ae0d78SAlex Zinenkoclass MyExtension : public ::mlir::transform::TransformDialectExtension<MyExtension> {
1668ae0d78SAlex Zinenkopublic:
1768ae0d78SAlex Zinenko  // The extension must derive the base constructor.
1868ae0d78SAlex Zinenko  using Base::Base;
1968ae0d78SAlex Zinenko
20b33b91a2SOleksandr "Alex" Zinenko  // This function initializes the extension, similarly to `initialize` in
21b33b91a2SOleksandr "Alex" Zinenko  // dialect  definitions. List individual operations and dependent dialects
22b33b91a2SOleksandr "Alex" Zinenko  // here.
2368ae0d78SAlex Zinenko  void init();
2468ae0d78SAlex Zinenko};
2568ae0d78SAlex Zinenko
2668ae0d78SAlex Zinenkovoid MyExtension::init() {
27b33b91a2SOleksandr "Alex" Zinenko  // Similarly to dialects, an extension can declare a dependent dialect. This
28b33b91a2SOleksandr "Alex" Zinenko  // dialect will be loaded along with the extension and, therefore, along with
29b33b91a2SOleksandr "Alex" Zinenko  // the Transform  dialect. Only declare as dependent the dialects that contain
30b33b91a2SOleksandr "Alex" Zinenko  // the attributes or types used by transform operations. Do NOT declare as
31b33b91a2SOleksandr "Alex" Zinenko  // dependent the dialects produced during the transformation.
32b33b91a2SOleksandr "Alex" Zinenko  //
3368ae0d78SAlex Zinenko  // declareDependentDialect<MyDialect>();
3468ae0d78SAlex Zinenko
35b33b91a2SOleksandr "Alex" Zinenko  // When transformations are applied, they may produce new operations from
36b33b91a2SOleksandr "Alex" Zinenko  // previously unloaded dialects. Typically, a pass would need to declare
37b33b91a2SOleksandr "Alex" Zinenko  // itself dependent on the dialects containing such new operations. To avoid
38b33b91a2SOleksandr "Alex" Zinenko  // confusion with the dialects the extension itself depends on, the Transform
39b33b91a2SOleksandr "Alex" Zinenko  // dialects differentiates between:
4068ae0d78SAlex Zinenko  //   - dependent dialects, which are used by the transform operations, and
4168ae0d78SAlex Zinenko  //   - generated dialects, which contain the entities (attributes, operations,
42b33b91a2SOleksandr "Alex" Zinenko  //     types) that may be produced by applying the transformation even when
43b33b91a2SOleksandr "Alex" Zinenko  //     not present in the original payload IR.
44b33b91a2SOleksandr "Alex" Zinenko  // In the following chapter, we will be add operations that generate function
45b33b91a2SOleksandr "Alex" Zinenko  // calls and structured control flow operations, so let's declare the
46b33b91a2SOleksandr "Alex" Zinenko  // corresponding dialects as generated.
4768ae0d78SAlex Zinenko  declareGeneratedDialect<::mlir::scf::SCFDialect>();
4868ae0d78SAlex Zinenko  declareGeneratedDialect<::mlir::func::FuncDialect>();
4968ae0d78SAlex Zinenko
5068ae0d78SAlex Zinenko  // Finally, we register the additional transform operations with the dialect.
5168ae0d78SAlex Zinenko  registerTransformOps<
5268ae0d78SAlex Zinenko    // TODO: list the operation classes.
5368ae0d78SAlex Zinenko  >();
5468ae0d78SAlex Zinenko}
5568ae0d78SAlex Zinenko```
5668ae0d78SAlex Zinenko
5768ae0d78SAlex ZinenkoThe operations themselves can be defined using ODS, exactly in the same way as regular operations in a dialect.
5868ae0d78SAlex Zinenko
5968ae0d78SAlex Zinenko```tablegen
6068ae0d78SAlex Zinenko// In MyExtension.td
6168ae0d78SAlex Zinenko#ifndef MY_EXTENSION
6268ae0d78SAlex Zinenko#define MY_EXTENSION
6368ae0d78SAlex Zinenko
6468ae0d78SAlex Zinenkoinclude "mlir/Dialect/Transform/IR/TransformDialect.td"
65*5a9bdd85SOleksandr "Alex" Zinenkoinclude "mlir/Dialect/Transform/Interfaces/TransformInterfaces.td"
6668ae0d78SAlex Zinenkoinclude "mlir/IR/OpBase.td"
6768ae0d78SAlex Zinenkoinclude "mlir/Interfaces/SideEffectInterfaces.td"
6868ae0d78SAlex Zinenko
6968ae0d78SAlex Zinenkodef MyOp : Op<Transform_Dialect, "transform.my.op", [
7068ae0d78SAlex Zinenko    // TODO: interfaces and traits here.
7168ae0d78SAlex Zinenko   ]> {
7268ae0d78SAlex Zinenko  let summary = "my transform op";
7368ae0d78SAlex Zinenko  // TODO: define the operation properties.
7468ae0d78SAlex Zinenko}
7568ae0d78SAlex Zinenko
7668ae0d78SAlex Zinenko#endif // MY_EXTENSION
7768ae0d78SAlex Zinenko```
7868ae0d78SAlex Zinenko
7968ae0d78SAlex ZinenkoSimilarly to dialects, we must use Tablegen to generate the header and implementation of these operations. We can instruct CMake to do it as follows.
8068ae0d78SAlex Zinenko
8168ae0d78SAlex Zinenko
8268ae0d78SAlex Zinenko```sh
8368ae0d78SAlex Zinenko# In CMakeLists.txt next to MyExtension.td.
8468ae0d78SAlex Zinenko
8568ae0d78SAlex Zinenko# Tell Tablegen to use MyExtension.td as input.
8668ae0d78SAlex Zinenkoset(LLVM_TARGET_DEFINITIONS MyExtension.td)
8768ae0d78SAlex Zinenko
8868ae0d78SAlex Zinenko# Ask Tablegen to generate op declarations and definitions from ODS.
8968ae0d78SAlex Zinenkomlir_tablegen(MyExtension.h.inc -gen-op-decls)
9068ae0d78SAlex Zinenkomlir_tablegen(MyExtension.cpp.inc -gen-op-defs)
9168ae0d78SAlex Zinenko
9268ae0d78SAlex Zinenko# Add a CMakeTarget we can depend on to ensure the generation happens before the compilation.
9368ae0d78SAlex Zinenkoadd_public_tablegen_target(MyExtensionIncGen)
9468ae0d78SAlex Zinenko
9568ae0d78SAlex Zinenko# Don't forget to generate the documentation, this will produce a MyExtension.md under
9668ae0d78SAlex Zinenko# Dialects.
9768ae0d78SAlex Zinenkoadd_mlir_doc(MyExtension MyExtension Dialects/ -gen-op-doc)
9868ae0d78SAlex Zinenko```
9968ae0d78SAlex Zinenko
10068ae0d78SAlex Zinenko```sh
10168ae0d78SAlex Zinenko# In CMakeLists.txt next to MyExtension.cpp
10268ae0d78SAlex Zinenkoadd_mlir_library(
10368ae0d78SAlex Zinenko  # Library called MyExtension.
10468ae0d78SAlex Zinenko  MyExtension
10568ae0d78SAlex Zinenko
10668ae0d78SAlex Zinenko  # Built from the following source files.
10768ae0d78SAlex Zinenko  MyExtension.cpp
10868ae0d78SAlex Zinenko
109b33b91a2SOleksandr "Alex" Zinenko  # Make sure ODS declaration and definitions are generated before compiling
110b33b91a2SOleksandr "Alex" Zinenko  # this.
11168ae0d78SAlex Zinenko  DEPENDS
11268ae0d78SAlex Zinenko  MyExtensionIncGen
11368ae0d78SAlex Zinenko
11468ae0d78SAlex Zinenko  # Link in the transform dialect, and all generated dialects.
11568ae0d78SAlex Zinenko  LINK_LIBS PUBLIC
11668ae0d78SAlex Zinenko  MLIRTransformDialect
11768ae0d78SAlex Zinenko  MLIRFuncDialect
11868ae0d78SAlex Zinenko  MLIRSCFDialect
11968ae0d78SAlex Zinenko)
12068ae0d78SAlex Zinenko```
12168ae0d78SAlex Zinenko
12268ae0d78SAlex ZinenkoThis will generate two files, `MyExtension.h.inc` and `MyExtension.cpp.inc`, that are supposed to be included into the declaration and definition of the transform operations, respectively.
12368ae0d78SAlex Zinenko
12468ae0d78SAlex Zinenko```c++
12568ae0d78SAlex Zinenko// In MyExtension.h.
12668ae0d78SAlex Zinenko#include "mlir/Dialect/Transform/IR/TransformDialect.h"
127*5a9bdd85SOleksandr "Alex" Zinenko#include "mlir/Dialect/Transform/Interfaces/TransformInterfaces.h"
12868ae0d78SAlex Zinenko
12968ae0d78SAlex Zinenko#define GET_OP_CLASSES
13068ae0d78SAlex Zinenko#include "MyExtension.h.inc"
13168ae0d78SAlex Zinenko```
13268ae0d78SAlex Zinenko
13368ae0d78SAlex Zinenko```c++
13468ae0d78SAlex Zinenko// In MyExtension.cpp.
13568ae0d78SAlex Zinenko
13668ae0d78SAlex Zinenko#define GET_OP_CLASSES
13768ae0d78SAlex Zinenko#include "MyExtension.cpp.inc"
13868ae0d78SAlex Zinenko
13968ae0d78SAlex Zinenko// …
14068ae0d78SAlex Zinenkovoid MyExtension::init() {
14168ae0d78SAlex Zinenko  // …
14268ae0d78SAlex Zinenko
143b33b91a2SOleksandr "Alex" Zinenko  // Finally, we register the additional transform operations with the dialect.
144b33b91a2SOleksandr "Alex" Zinenko  // List all  operations generated from ODS. This call will perform additional
145b33b91a2SOleksandr "Alex" Zinenko  // checks that the  operations implement the transform and memory effect
146b33b91a2SOleksandr "Alex" Zinenko  // interfaces required by the dialect interpreter and assert if they do not.
14768ae0d78SAlex Zinenko  registerTransformOps<
14868ae0d78SAlex Zinenko#define GET_OP_LIST
14968ae0d78SAlex Zinenko#include "MyExtension.cpp.inc"
15068ae0d78SAlex Zinenko  >();
15168ae0d78SAlex Zinenko}
15268ae0d78SAlex Zinenko```
15368ae0d78SAlex Zinenko
15468ae0d78SAlex Zinenko## Defining a Transform Operation
15568ae0d78SAlex Zinenko
15668ae0d78SAlex ZinenkoWith this setup, we are now ready to define the new transform operation to rewrite the function call. This is identical to defining a regular operation in a dialect. Note that the Transform dialect requires operations to implement the `TransformOpInterface` as well as `MemoryEffectsOpInterface` to indicate whether the operands are consumed or only read. Our operation can be defined along the following lines.
15768ae0d78SAlex Zinenko
15868ae0d78SAlex Zinenko```tablegen
15968ae0d78SAlex Zinenko// In MyExtension.td.
16068ae0d78SAlex Zinenko
161b33b91a2SOleksandr "Alex" Zinenko// Define the new operation. By convention, prefix its name with the name of the
162b33b91a2SOleksandr "Alex" Zinenko// dialect  extension, "my.". The full operation name will be further prefixed
163b33b91a2SOleksandr "Alex" Zinenko// with "transform.".
16468ae0d78SAlex Zinenkodef ChangeCallTargetOp : Op<Transform_Dialect, "my.change_call_target",
165b33b91a2SOleksandr "Alex" Zinenko    // Indicate that the operation implements the required TransformOpInterface
166b33b91a2SOleksandr "Alex" Zinenko    // and MemoryEffectsOpInterface.
16768ae0d78SAlex Zinenko    [DeclareOpInterfaceMethods<TransformOpInterface>,
16868ae0d78SAlex Zinenko     DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
169b33b91a2SOleksandr "Alex" Zinenko  // Provide a brief and a full description. It is recommended that the latter
170b33b91a2SOleksandr "Alex" Zinenko  // describes the effects on the operands and how the operation processes
171b33b91a2SOleksandr "Alex" Zinenko  // various failure modes.
17268ae0d78SAlex Zinenko  let summary = "Changes the callee of a call operation to the specified one";
17368ae0d78SAlex Zinenko  let description = [{
174b33b91a2SOleksandr "Alex" Zinenko    For each `func.call` payload operation associated with the handle, changes
175b33b91a2SOleksandr "Alex" Zinenko    its callee to be the symbol whose name is provided as an attribute to this operation.
17668ae0d78SAlex Zinenko
177b33b91a2SOleksandr "Alex" Zinenko    Generates a silenceable failure if the operand is associated with payload operations that are not `func.call`. Only reads the operand.
17868ae0d78SAlex Zinenko  }];
17968ae0d78SAlex Zinenko
180b33b91a2SOleksandr "Alex" Zinenko  // The arguments include the handle to the payload operations and the
181b33b91a2SOleksandr "Alex" Zinenko  // attribute that specifies the new callee. The handle must implement
182b33b91a2SOleksandr "Alex" Zinenko  // TransformHandleTypeInterface.
183b33b91a2SOleksandr "Alex" Zinenko  // We use a string attribute as the symbol may not exist in the transform IR
184b33b91a2SOleksandr "Alex" Zinenko  // so the verification may fail.
18568ae0d78SAlex Zinenko  let arguments = (ins
18668ae0d78SAlex Zinenko    TransformHandleTypeInterface:$call,
18768ae0d78SAlex Zinenko    StrAttr:$new_target);
18868ae0d78SAlex Zinenko
189b33b91a2SOleksandr "Alex" Zinenko  // The results are empty as the transformation does not produce any new
190b33b91a2SOleksandr "Alex" Zinenko  // payload.
19168ae0d78SAlex Zinenko  let results = (outs);
19268ae0d78SAlex Zinenko
19368ae0d78SAlex Zinenko  // Provide nice syntax.
19468ae0d78SAlex Zinenko  let assemblyFormat = "$call `,` $new_target attr-dict `:` type($call)";
19568ae0d78SAlex Zinenko}
19668ae0d78SAlex Zinenko```
19768ae0d78SAlex Zinenko
198c63d2b2cSMatthias SpringerTo finalize the definition of the transform operation, we need to implement the
199c63d2b2cSMatthias Springerinterface methods. The `TransformOpInterface` currently requires only one method
200c63d2b2cSMatthias Springer– `apply` – that performs the actual transformation. It is a good practice to
201c63d2b2cSMatthias Springerlimit the body of the method to manipulation of the Transform dialect constructs
202c63d2b2cSMatthias Springerand have the actual transformation implemented as a standalone function so it
203c63d2b2cSMatthias Springercan be used from other places in the code. Similar to rewrite patterns, all IR
204c63d2b2cSMatthias Springermust be modified with the provided rewriter.
20568ae0d78SAlex Zinenko
20668ae0d78SAlex Zinenko```c++
20768ae0d78SAlex Zinenko// In MyExtension.cpp
20868ae0d78SAlex Zinenko
20939298b09SAndrzej Warzyński// Implementation of our Transform dialect operation.
21068ae0d78SAlex Zinenko// This operation returns a tri-state result that can be one of:
21168ae0d78SAlex Zinenko// - success when the transformation succeeded;
212c63d2b2cSMatthias Springer// - definite failure when the transformation failed in such a way that
213c63d2b2cSMatthias Springer//   following transformations are impossible or undesirable, typically it could
214c63d2b2cSMatthias Springer//   have left payload IR in an invalid state; it is expected that a diagnostic
215c63d2b2cSMatthias Springer//   is emitted immediately before returning the definite error;
216c63d2b2cSMatthias Springer// - silenceable failure when the transformation failed but following
217c63d2b2cSMatthias Springer//   transformations are still applicable, typically this means a precondition
218c63d2b2cSMatthias Springer//   for the transformation is not satisfied and the payload IR has not been
219c63d2b2cSMatthias Springer//   modified. The silenceable failure additionally carries a Diagnostic that
220c63d2b2cSMatthias Springer//   can be emitted to the user.
221c63d2b2cSMatthias Springer::mlir::DiagnosedSilenceableFailure mlir::transform::ChangeCallTargetOp::apply(
222c63d2b2cSMatthias Springer    // The rewriter that should be used when modifying IR.
223c63d2b2cSMatthias Springer    ::mlir::transform::TransformRewriter &rewriter,
224c63d2b2cSMatthias Springer    // The list of payload IR entities that will be associated with the
225c63d2b2cSMatthias Springer    // transform IR values defined by this transform operation. In this case, it
226c63d2b2cSMatthias Springer    // can remain empty as there are no results.
22768ae0d78SAlex Zinenko    ::mlir::transform::TransformResults &results,
228c63d2b2cSMatthias Springer    // The transform application state. This object can be used to query the
229c63d2b2cSMatthias Springer    // current associations between transform IR values and payload IR entities.
230c63d2b2cSMatthias Springer    // It can also carry additional user-defined state.
23168ae0d78SAlex Zinenko    ::mlir::transform::TransformState &state) {
23268ae0d78SAlex Zinenko
233b33b91a2SOleksandr "Alex" Zinenko  // First, we need to obtain the list of payload operations that are associated
234b33b91a2SOleksandr "Alex" Zinenko  // with the operand handle.
23568ae0d78SAlex Zinenko  auto payload = state.getPayloadOps(getCall());
23668ae0d78SAlex Zinenko
23768ae0d78SAlex Zinenko  // Then, we iterate over the list of operands and call the actual IR-mutating
23868ae0d78SAlex Zinenko  // function. We also check the preconditions here.
23968ae0d78SAlex Zinenko  for (Operation *payloadOp : payload) {
24068ae0d78SAlex Zinenko    auto call = dyn_cast<::mlir::func::CallOp>(payloadOp);
24168ae0d78SAlex Zinenko    if (!call) {
24268ae0d78SAlex Zinenko      DiagnosedSilenceableFailure diag = emitSilenceableError()
24368ae0d78SAlex Zinenko          << "only applies to func.call payloads";
24468ae0d78SAlex Zinenko      diag.attachNote(payloadOp->getLoc()) << "offending payload";
24568ae0d78SAlex Zinenko      return diag;
24668ae0d78SAlex Zinenko    }
24768ae0d78SAlex Zinenko
24868ae0d78SAlex Zinenko    updateCallee(call, getNewTarget());
24968ae0d78SAlex Zinenko  }
25068ae0d78SAlex Zinenko
25168ae0d78SAlex Zinenko  // If everything went well, return success.
25268ae0d78SAlex Zinenko  return DiagnosedSilenceableFailure::success();
25368ae0d78SAlex Zinenko}
25468ae0d78SAlex Zinenko```
25568ae0d78SAlex Zinenko
25668ae0d78SAlex ZinenkoThe implementation of the `MemoryEffectsOpInterface` must specify the effects this operation has on its operands (consumed or readonly) and on the payload IR (mutates or readonly). Transform dialect verifiers will check for side effects being present and assert in debug builds if they are not.
25768ae0d78SAlex Zinenko
25868ae0d78SAlex Zinenko```c++
25968ae0d78SAlex Zinenko// In MyExtension.cpp
26068ae0d78SAlex Zinenko
26168ae0d78SAlex Zinenkovoid ChangeCallTargetOp::getEffects(
26268ae0d78SAlex Zinenko    ::llvm::SmallVectorImpl<::mlir::MemoryEffects::EffectInstance> &effects) {
26368ae0d78SAlex Zinenko  // Indicate that the `call` handle is only read by this operation because the
26468ae0d78SAlex Zinenko  // associated operation is not erased but rather modified in-place, so the
26568ae0d78SAlex Zinenko  // reference to it remains valid.
26668ae0d78SAlex Zinenko  onlyReadsHandle(getCall(), effects);
26768ae0d78SAlex Zinenko
26868ae0d78SAlex Zinenko  // Indicate that the payload is modified by this operation.
26968ae0d78SAlex Zinenko  modifiesPayload(effects);
27068ae0d78SAlex Zinenko}
27168ae0d78SAlex Zinenko```
27268ae0d78SAlex Zinenko
27368ae0d78SAlex Zinenko## Registration and Usage
27468ae0d78SAlex Zinenko
27568ae0d78SAlex ZinenkoThis is enough to define transform operations. The only remaining bit is providing the extension registration hook that can be called from the project’s `main`.
27668ae0d78SAlex Zinenko
27768ae0d78SAlex Zinenko
27868ae0d78SAlex Zinenko```c++
27968ae0d78SAlex Zinenko// In TransformDialect.cpp (don't forget a declaration in TransformDialect.h);
28068ae0d78SAlex Zinenko
28168ae0d78SAlex Zinenkovoid registerMyExtension(::mlir::DialectRegistry &registry) {
28268ae0d78SAlex Zinenko  registry.addExtensions<MyExtension>();
28368ae0d78SAlex Zinenko}
28468ae0d78SAlex Zinenko```
28568ae0d78SAlex Zinenko
28639298b09SAndrzej WarzyńskiAfter registering the extension, it becomes possible to use our new operation in the Transform dialect interpreter. The upstream testing pass can be used as is.
28768ae0d78SAlex Zinenko
28868ae0d78SAlex Zinenko```mlir
289b33b91a2SOleksandr "Alex" Zinenkomodule attributes {transform.with_named_sequence} {
290b33b91a2SOleksandr "Alex" Zinenko  transform.named_sequence @__transform_main(
291b33b91a2SOleksandr "Alex" Zinenko      %arg0: !transform.any_op,
29268ae0d78SAlex Zinenko      %arg1: !transform.op<"linalg.matmul">,
293b33b91a2SOleksandr "Alex" Zinenko      %arg2: !transform.op<"linalg.elemwise_binary">) {
29468ae0d78SAlex Zinenko    // Since the %arg2 handle is associated with both elementwise operations,
29568ae0d78SAlex Zinenko    // we need to split it into two handles so we can target only the second
29668ae0d78SAlex Zinenko    // elementwise operation.
297b33b91a2SOleksandr "Alex" Zinenko    %add, %max = transform.split_handle %arg2
298b33b91a2SOleksandr "Alex" Zinenko        : (!transform.op<"linalg.elemwise_binary">)
29968ae0d78SAlex Zinenko        -> (!transform.any_op, !transform.any_op)
30068ae0d78SAlex Zinenko
301b33b91a2SOleksandr "Alex" Zinenko    // The actual tiling transformation takes tile sizes as attributes. It
302b33b91a2SOleksandr "Alex" Zinenko    // produces a handle to the loop generated during tiling.
303b33b91a2SOleksandr "Alex" Zinenko    %loop, %tiled = transform.structured.tile_using_forall %max
304b33b91a2SOleksandr "Alex" Zinenko                    tile_sizes [8, 32]
30568ae0d78SAlex Zinenko        : (!transform.any_op) -> (!transform.any_op, !transform.any_op)
30668ae0d78SAlex Zinenko
30768ae0d78SAlex Zinenko    // We can now fuse the other operations into the loop. Here, we fuse
30868ae0d78SAlex Zinenko    // operations one-by-one. This requires the operation that is being fused
30968ae0d78SAlex Zinenko    // to define the value used within the loop, so the order of such fusions
31068ae0d78SAlex Zinenko    // is important. We could also use "transform.merge_handles" to obtain
311b33b91a2SOleksandr "Alex" Zinenko    // a single handle to all operations and give it to
312b33b91a2SOleksandr "Alex" Zinenko    // `fuse_into_containing_op` that would take care of the ordering in this
313b33b91a2SOleksandr "Alex" Zinenko    // case.
31468ae0d78SAlex Zinenko    %add_fused = transform.structured.fuse_into_containing_op %add into %loop
31568ae0d78SAlex Zinenko        : (!transform.any_op, !transform.any_op) -> !transform.any_op
316b33b91a2SOleksandr "Alex" Zinenko    %matmul_fused = transform.structured.fuse_into_containing_op %arg1
317b33b91a2SOleksandr "Alex" Zinenko                    into %loop
318b33b91a2SOleksandr "Alex" Zinenko        : (!transform.op<"linalg.matmul">, !transform.any_op)
319b33b91a2SOleksandr "Alex" Zinenko       -> !transform.any_op
32068ae0d78SAlex Zinenko
32168ae0d78SAlex Zinenko    // Tile again to get the desired size. Note that this time this tiles the
32268ae0d78SAlex Zinenko    // "add" operation and fuses matmul into the loop, but doesn't affect the
323b33b91a2SOleksandr "Alex" Zinenko    // "max" operation. This illustrates the precise targeting with the
324b33b91a2SOleksandr "Alex" Zinenko    // transform dialect. Otherwise, it is difficult to differentiate "add" and
325b33b91a2SOleksandr "Alex" Zinenko    // "max", both of which having the same kind.
326b33b91a2SOleksandr "Alex" Zinenko    %loop_2, %tiled_2 = transform.structured.tile_using_forall %add_fused
327b33b91a2SOleksandr "Alex" Zinenko                        tile_sizes [4, 4]
32868ae0d78SAlex Zinenko        : (!transform.any_op) -> (!transform.any_op, !transform.any_op)
329b33b91a2SOleksandr "Alex" Zinenko    %matmul_fused_2 = transform.structured.fuse_into_containing_op %matmul_fused
330b33b91a2SOleksandr "Alex" Zinenko                      into %loop_2
33168ae0d78SAlex Zinenko        : (!transform.any_op, !transform.any_op) -> !transform.any_op
33268ae0d78SAlex Zinenko
333b33b91a2SOleksandr "Alex" Zinenko    // Since outlining is currently only implemented for region-holding
334b33b91a2SOleksandr "Alex" Zinenko    // operations such as loops, use tiling to size 1 to materialize the outer
335b33b91a2SOleksandr "Alex" Zinenko    // loop that is going to be outlined.
33696ff0255SOleksandr "Alex" Zinenko    %outline_target, %_ = transform.structured.tile_using_forall %tiled_2 tile_sizes [1]
33768ae0d78SAlex Zinenko        : (!transform.any_op) -> (!transform.any_op, !transform.any_op)
33868ae0d78SAlex Zinenko    transform.structured.fuse_into_containing_op %matmul_fused_2 into %outline_target
33968ae0d78SAlex Zinenko        : (!transform.any_op, !transform.any_op) -> !transform.any_op
340b33b91a2SOleksandr "Alex" Zinenko    %func, %call = transform.loop.outline %outline_target
341b33b91a2SOleksandr "Alex" Zinenko                   {func_name = "outlined"}
34268ae0d78SAlex Zinenko        : (!transform.any_op) -> (!transform.any_op, !transform.any_op)
34368ae0d78SAlex Zinenko
34468ae0d78SAlex Zinenko    // Rewrite the call target.
34568ae0d78SAlex Zinenko    transform.my.change_call_target %call, "microkernel" : !transform.any_op
34668ae0d78SAlex Zinenko
34768ae0d78SAlex Zinenko    transform.yield
34868ae0d78SAlex Zinenko  }
349b33b91a2SOleksandr "Alex" Zinenko}
35068ae0d78SAlex Zinenko```
351d8c18e42SAlex Zinenko
352d8c18e42SAlex Zinenko## Appendix: Autogenerated Documentation
353d8c18e42SAlex Zinenko
3544921ff5bSKohei Yamaguchi[include "Tutorials/transform/MyExtensionCh2.md"]
355