xref: /llvm-project/mlir/docs/Dialects/Transform.md (revision 49a5ad8e5714fd404210279303acc97b495d66d0)
1# Transform Dialect
2
3Fine-grain transformation control dialect. See [tutorial](../Tutorials/transform) for more introductory information.
4
5[TOC]
6
7## Overview
8
9This dialect provides operations that can be used to control transformation
10of the IR using a different portion of the IR. It refers to the IR being
11transformed as payload IR, and to the IR guiding the transformation as
12transform IR.
13
14The main use case for this dialect is orchestrating fine-grain transformations
15on individual IR objects (operations or values) or sets thereof. For example, it
16may involve finding loop-like operations with specific properties (e.g., large
17size) in the payload IR, applying loop tiling to those and only those
18operations, and then applying loop unrolling to the inner loops produced by the
19previous transformations. As such, it is not intended as a replacement for the
20pass infrastructure, nor for the pattern rewriting infrastructure. In the most
21common case, the transform IR will be processed and applied to the payload IR by
22a pass. Transformations expressed by the Transform dialect may be implemented
23using the pattern infrastructure or any other relevant MLIR component.
24
25The following IR gives a rough idea of what the operations in this dialect
26may look like without using actually existing operations:
27
28```mlir
29%0 = transform.loop.find { size > 42 } : !transform.interface<tileable>
30%1 = transform.compute_trailing_tile_size %0 : !transform.param<index>
31%2:2 = transform.loop.tile %0 tile_sizes(1, 4, %1)
32      : (!transform.interface<tileable>)
33     -> (!transform.op<loop>, !transform.op<loop>)
34%3 = transform.get_op_result [0] %2#0 : !transform.any_value
35transform.assign_to_fast_memory %3
36transform.loop.unroll %1#1 : !transform.op<loop>
37```
38
39The values used in the Transform dialect may correspond to:
40
41  * sets of operations in the payload IR;
42
43  * sets of values in the payload IR;
44
45  * sets of parameters (attributes) known at the execution time of the
46    transform dialect.
47
48The former two kinds of values are also referred to as operation and value
49*handles*, respectively. In the example above, `%0` corresponds to the set of
50loops found in the payload IR that satisfy the condition, and `%2` correspond to
51groups of outer and inner loops, respectively, produced by the tiling
52transformation. `%3` corresponds to a set of values that are produced by the
53outer loops after tiling. `%1` corresponds to a list of tile sizes selected for
54each of the operations that `%0` corresponds to.
55
56An operation handle such as `%0` may be associated with multiple payload
57operations. This is conceptually a set of operations and no assumptions should
58be made about the order of ops unless specified otherwise by the operation.
59Similarly, a value handle such as `%3` may be associated with a set of payload
60IR values. Transform dialect operations may take as operands and produce an
61arbitrary combination of values representing handles and parameters. Most
62Transform IR ops support operand values that are mapped to multiple payload
63objects. They usually apply the respective transformation for every mapped
64object ("batched execution"). Deviations from this convention are described in
65the documentation of Transform IR ops.
66
67Parameters, such as `%1` in the above example, have two logical roles in
68transform IR. In parameter based control, they carry the values needed to
69execute the explicit control defined by the transforms, for example:
70
71```mlir
72%0 = transform.match.structured.rank %linalg_op_handle : !transform.param<index>
73%1 = transform.param.constant 3 : i32 -> !transform.param<index>
74transform.execute_if_cmpi eq %0, %1 : !transform.param<index>, !transform.param<index>
75// Some nested body of transform ops
76```
77
78Alternatively, parameters can associate with the payload IR where the specific
79value at execution time has no bearing on the execution of the transform IR. In
80other words, parameters can either associate with the transform IR or the
81payload IR.  Note that it is generally discouraged to use parameters containing
82arbitrary attributes within transform control. Parameter based control should
83try to be explicitly typed when possible.
84
85The transform IR values have transform IR types, which should implement exactly one of:
86
87  * [TransformHandleTypeInterface](#transformhandletypeinterface-transformhandletypeinterface),
88
89  * [TransformValueHandleTypeInterface](#transformvaluehandletypeinterface-transformvaluehandletypeinterface),
90
91  * [TransformParamTypeInterface](#transformparamtypeinterface-transformparamtypeinterface).
92
93The goal of these type interfaces, beyond providing a common base for accepted
94types, is to verify the properties of the associated objects. For example, a
95handle type interface implementation may check whether all associated payload IR
96operations implement the "TileableOp" interface or have a specific "loop" kind.
97Similarly, a value handle type interface implementation may check if the
98associated payload IR values are block arguments or have a specific type, or a
99parameter type interface may check whether the associated attributes contain
100non-negative integer values. These properties are used to statically indicate
101 pre- and post-conditions of a transformation connected to a Transform dialect
102operation. The conditions are verified when payload objects operations are first
103associated with a transform handle. By convention, Transform dialect operations
104are expected to indicate narrow preconditions for their operands by enforcing
105operand type constraints in the their definitions and verifiers. On the
106contrary, operations are expected to have few constraints on their results.
107Specific instances of a transform operation can then be created with a more
108restricted result type than the constraint in the operation (e.g., the "find"
109operation only constrains the result type to be a transform IR type while its
110concrete instance can have a type with stricter constraints such as implementing
111the "tilable" interface). The verification will then happen at transform
112execution time. This approach allows one to capture payload IR operation
113properties in the transform IR without resorting to excessive use of type casts
114or coupling dialect extensions between themselves. It is a trade-off between
115verbosity/complexity and static hardening, which can be revised in the future.
116
117Overall, Transform IR ops are expected to be contained in a single top-level
118op. Such top-level ops specify how to apply the transformations described
119by the operations they contain, e.g., `transform.sequence` executes
120transformations one by one and fails if any of them fails. Such ops are
121expected to have the `PossibleTopLevelTransformOpTrait` and may be used
122without arguments.
123
124A program transformation expressed using the Transform dialect can be
125programmatically triggered by calling:
126
127```c++
128LogicalResult transform::applyTransforms(
129    Operation *payloadRoot,
130    const RaggedArray<transform::MappedValue> &extraMappings,
131    TransformOpInterface transform,
132    const TransformOptions &options);
133```
134
135that applies the transformations specified by the top-level `transform` to
136payload IR contained in `payloadRoot`. The payload root operation will be
137associated with the first argument of the entry block of the top-level transform
138op. This block may have additional arguments, handles or parameters. They will
139be associated with values provided as `extraMappings`. The call will report an
140error and return if the wrong number of mappings is provided.
141
142## Dialect Extension Mechanism
143
144This dialect is designed to be extensible, that is, clients of this dialect
145are allowed to inject additional operations into this dialect using the
146`TransformDialectExtension` mechanism. This allows the dialect to avoid a
147dependency on the implementation of the transformation as well as to avoid
148introducing dialect-specific transform dialects. In the example above,
149the operations may have been injected by a notional `loop` dialect rather
150than defined in this dialect, hence the common prefix.
151
152It is recommended to prefix injected operations with one or several
153dot-separated words that indicate which extension adds them. For
154dialect-specific transformations, the prefix is naturally the name of the
155dialect, e.g., `transform.affine.reschedule`. For dialect-agnostic
156transformations (typically implemented using interfaces), the prefix may
157be derived from the interface name or from a common concept, e.g.,
158`transform.loop.tile` may apply to any loop-like operation that implements
159`TileableOpInterface`. The C++ classes for the dialect extension should
160include the prefix in their name, e.g., `AffineTransformDialectExtension` or
161`LoopTransformDialectExtension` in the cases above. Unprefixed operation
162names are reserved for ops defined directly in the Transform dialect.
163
164Operations injected into the dialect must:
165
166  * Implement the `TransformOpInterface` to execute the corresponding
167    transformation on the payload IR.
168
169  * Implement the `MemoryEffectsOpInterface` to annotate the effects of
170    the transform IR operation on the payload IR as well as on the mapping
171    between transform IR values and payload IR operations. See below for
172    the description of available effects.
173
174The presence of interface implementations is checked at runtime when the
175dialect is loaded to allow for those implementations to be supplied by
176separate dialect extensions if desired.
177
178Similarly to operations, additional types can be injected into the dialect using
179the same extension mechanism. The types must:
180
181  * Implement exactly one of `TransformHandleTypeInterface`,
182    `TransformValueHandleTypeInterface`, `TransformParamTypeInterface`.
183
184## Side Effects
185
186The Transform dialect relies on MLIR side effect modelling to enable
187optimization of the transform IR. More specifically, it provides several
188side effect resource objects and expects operations to describe their
189effects on these resources.
190
191  * `TransformMappingResource` - side effect resource corresponding to the
192    mapping between transform IR values and payload IR operations.
193
194    - An `Allocate` effect from this resource means creating a new mapping
195      entry, it is always accompanied by a `Write` effect.
196
197    - A `Read` effect from this resource means accessing the mapping.
198
199    - A `Free` effect on this resource indicates the removal of the mapping
200      entry, typically after a transformation that modifies the payload IR
201      operations associated with one of the transform IR operation's
202      operands. It is always accompanied by a `Read` effect.
203
204  * `PayloadIRResource` - side effect resource corresponding to the payload
205    IR itself.
206
207    - A `Read` effect from this resource means accessing the payload IR.
208
209    - A `Write` effect on this resource means mutating the payload IR. It is
210      almost always accompanied by a `Read`.
211
212The typical flow of values in the transform IR is as follows. Most
213operations produce new transform IR values and immediately associate them
214with a list of payload IR operations. This corresponds to `Allocate` and
215`Write` effects on the `TransformMappingResource`, and often requires at
216least a `Read` effect on the `PayloadIRResource`. Transform operations that
217only inspect the payload IR to produce new handles are usually limited to
218these effects on their operands. Transform operations that mutate the
219payload IR are thought to _consume_ the handles provided as operands, that
220is have the `Read` and `Free` effects on them. As with the usual memory
221effects, using a value after it was freed is incorrect. In case of the
222transform IR, this value is likely associated with payload IR operations
223that were modified or even removed by the transformation, so it is
224meaningless to refer to them. When further transformations are desired, the
225transform operations can return _new_ handles that can be read or consumed
226by subsequent operations.
227
228## Execution Model
229
230The transformation starts at the user-specified top-level transform IR
231operation and applies to some user-specified payload IR scope, identified by
232the payload IR op that contains the IR to transform. It is the
233responsibility of the user to properly select the scope and/or to avoid the
234transformations to modify the IR outside of the given scope. The top-level
235transform IR operation may contain further transform operations and execute
236them in the desired order.
237
238Transformation application functions produce a tri-state status:
239
240- success;
241- recoverable (silenceable) failure;
242- irrecoverable failure.
243
244Transformation container operations may intercept recoverable failures and
245perform the required recovery steps thus succeeding themselves. On
246the other hand, they must propagate irrecoverable failures. For such
247failures, the diagnostics are emitted immediately whereas their emission is
248postponed for recoverable failures. Transformation container operations may
249also fail to recover from a theoretically recoverable failure, in which case
250they can either propagate it to their parent or emit the diagnostic and turn
251the failure into an irrecoverable one. A recoverable failure produced by
252applying the top-level transform IR operation is considered irrecoverable.
253
254Transformation container operations are allowed to "step over" some nested
255operations if the application of some previous operation produced a failure.
256This can be conceptually thought of as having a global "recoverable error
257register" that is read/write accessed by each transform operation as a side
258effect. The transformation is skipped if the register already contains an
259error description, and the control flow proceeds to the following operation.
260
261Note that a silenceable failure, if emitted, is a compiler _error_ rather
262than a warning. Transformations are expected to produce silenceable failures
263if they haven't yet modified the payload IR, i.e. when reporting a
264precondition failure, and an irrecoverable failure when they modified the IR
265in a way that is contrary to the semantics of the transform operation or
266would fail a postcondition. Some "navigation" operations that identify
267payload IR targets for the following transformation may have a conceptual
268"failure to match" that is considered a successful execution in the
269execution model but results in handles associated with empty payload IR
270operation lists.
271
272## Handle Invalidation
273
274The execution model of the Transform dialect allows a payload IR operation to be
275associated with _multiple_ handles as well as nested payload IR operations to be
276associated with different handles. Similarly, a payload IR value may be
277associated with multiple transform IR value handles. When a transform IR
278operation consumes a handle, it usually indicates that the corresponding payload
279IR object was destroyed and should no longer be referenced. Transform IR handles
280that _may_ be pointing to an erased payload IR object are _invalidated_. The
281mere presence of an invalidated handle in the transform IR is not a problem, but
282_using_ it results in undefined behavior. Invalidated handles can be thought of
283as dangling pointers. Note that the _entire_ handle is invalidated, even if some
284of the payload IR objects associated with it remain live.
285
286The following handle invalidation rules apply.
287
288  * When an operation handle is consumed, are invalidated:
289
290    - operation handles associated with one of the payload operations that the
291      consumed handle is associated with;
292
293    - operation handles associated with one of the operations _nested_ in the
294      payload operations described above;
295
296    - value handles associated with any result of any operation described above;
297
298    - value handles associated with any argument of a block contained in a
299      region attached to any operation described above.
300
301  * When a value handle is consumed, are invalidated:
302
303    - operation handles associated with payload operations that produce as
304      result any value associated with the consumed handle (when the associated
305      is an operation result);
306
307    - operation handles associated with payload operations _nested_ in the
308      payload operations described above;
309
310    - operation handles associated with payload operations (recursively)
311      _contained_ in the block that defines as argument any value associated
312      with the consumed handle (when the associated value is a block argument);
313      note that the adjacent blocks are not affected;
314
315    - value handles associated with any result of any operation described above,
316      including all results of the operation defining as result the value
317      associated with the consumed handle;
318
319    - value handles associated with any argument of a block contained in a
320      region attached to any operation described above.
321
322More intuitively, consuming a handle invalidates any handle that may be pointing
323to an object defined or contained in the payload IR subtree rooted at the
324closest operation or block.
325
326The Transform dialect infrastructure has the capability of checking whether
327the transform IR op operand is invalidated before applying the
328transformation. However, such a check is computationally expensive and
329must be enabled explicitly through `TransformOptions`. Additionally, the
330`transform-dialect-check-uses` pass emits warnings when a handle may be used
331after it has been consumed, but does so abstractly, without processing the
332payload IR.
333
334Values associated with parameters (non-handles) cannot be invalidated.
335
336## Intended Use and Integrations
337
338The transformation control infrastructure provided by this dialect is
339positioned roughly between rewrite patterns and passes. A transformation
340that is executed by a transform operation is likely to be sufficiently
341complex to require at least a set of patterns to be implemented. It is also
342expected to be more focused than a pass: a pass typically applies identical
343transformations everywhere in the IR, a transform dialect-controlled
344transformation would apply to a small subset of operations selected, e.g.,
345by a pattern-matching operation or generated by a previous transformation.
346It is discouraged, although technically possible, to run a pass pipeline as
347part of the transform op implementation.
348
349One of the main scenarios for using this dialect is fine-grain chaining of
350transformations. For example, a loop-like operation may see its iteration
351domain split into two parts, implemented as separate loops (transformation
352known as index-set splitting), each of which is then transformed differently
353(e.g., the first loop is tiled and the second unrolled) with the necessary
354enabling and cleanup patterns around the main transformation:
355
356```mlir
357// <generate %loop, e.g., by pattern-matching>
358// ...
359%parts:2 = transform.loop.split %loop { upper_bound_divisible_by = 8 }
360transform.loop.tile %parts#0 { tile_sizes = [8] }
361transform.loop.unroll %parts#1 { full }
362```
363
364This composition would have been difficult to implement as separate passes
365since the hypothetical "tiling" and "unrolling" pass would need to somehow
366differentiate between the parts of the loop produced by the previous pass
367(both are the same operation, and it is likely undesirable to pollute the
368operation with pass-specific information). Implementing passes that run the
369combined transformation would have run into the combinatorial explosion
370issue due to multiple possible transform compositions or into the need for
371deep pass parameterization, the ultimate form of which is an ad-hoc dialect
372to specify which transformations the pass should run. The transform dialect
373provides a uniform, extensible mechanism for controlling transformations in
374such cases.
375
376The Transform dialect is supposed to be consumed by an "interpreter" pass
377that drives the application of transformations. To ensure extensibility and
378composability, this pass is not expected to actually perform the
379transformations specified by the ops. Instead, the transformations are
380implemented by the transform ops themselves via `TransformOpInterface`. The
381pass serves as the entry point, handles the flow of transform operations and
382takes care of bookkeeping. As such, the Transform dialect does not provide
383the interpreter pass. Instead, it provides a set of utilities that can be
384used by clients to define their own interpreter passes or as part of a more
385complex pass. For example, the mapping between values in the transform IR
386and operations in the payload IR, or the function that applies the
387transformations specified by ops in the given block sequentially. Note that
388a transform op may have regions with further transform ops in them, with
389the op itself guiding how to dispatch the transformation control flow to
390those regions. This approach allows clients to decide on the relative
391location of the transform IR in their input (e.g., nested modules, separate
392modules, optional regions to certain operations, etc.), register additional
393transform operations and perform client-specific bookkeeping.
394
395## Effects on the Infrastructure
396
397Although scoped to a single dialect, this functionality conceptually belongs
398to the MLIR infrastructure. It aims to be minimally intrusive and opt-in.
399
400Some infrastructural components may grow extra functionality to support the
401transform dialect. In particular, the pattern infrastructure may add extra
402hooks to identify the "main results" of a transformation or to notify
403external observers about changes made to certain operations. These are not
404expected to affect the existing uses of the infrastructure.
405
406For the sake of reusability, transformations should be implemented as
407utility functions that are called from the interface methods of transform
408ops rather than having the methods directly act on the payload IR.
409
410## Type Definitions
411
412[include "Dialects/TransformTypes.md"]
413
414## Core Operations
415
416[include "Dialects/TransformOps.md"]
417
418## Affine Transform Operations
419
420[include "Dialects/AffineLoopTransformOps.md"]
421
422## Bufferization Transform Operations
423
424[include "Dialects/BufferizationTransformOps.md"]
425
426## Debug Transform Operations
427
428[include "Dialects/DebugExtensionOps.md"]
429
430## DLTI Transform Operations
431
432[include "Dialects/DLTITransformOps.md"]
433
434## IRDL (extension) Transform Operations
435
436[include "Dialects/IRDLExtensionOps.md"]
437
438## Func Transform Operations
439
440[include "Dialects/FuncTransformOps.md"]
441
442## GPU Transform Operations
443
444[include "Dialects/GPUTransformOps.md"]
445
446## Loop (extension) Transform Operations
447
448[include "Dialects/LoopExtensionOps.md"]
449
450## Loop (SCF) Transform Operations
451
452[include "Dialects/SCFLoopTransformOps.md"]
453
454## MemRef Transform Operations
455
456[include "Dialects/MemRefTransformOps.md"]
457
458## PDL (extension) Transform Operations
459
460[include "Dialects/PDLExtensionOps.md"]
461
462## Structured (Linalg) Match Operations
463
464[include "Dialects/LinalgStructuredMatchOps.md"]
465
466## Structured (Linalg) Transform Operations
467
468[include "Dialects/LinalgStructuredTransformOps.md"]
469
470## Tensor Transform Operations
471
472[include "Dialects/TensorTransformOps.md"]
473
474## Vector Transform Operations
475
476[include "Dialects/VectorTransformOps.md"]
477
478[include "Dialects/TransformTypeInterfaces.md"]
479
480[include "Dialects/TransformOpInterfaces.md"]
481