From 209843e05007e7b7209dca68e5f25d02d4a482d1 Mon Sep 17 00:00:00 2001 From: Alex Zinenko Date: Wed, 12 Oct 2022 11:02:01 +0000 Subject: [PATCH] [mlir][doc] move transform dialect docs to .md, NFC The description of the Transform dialect has become quite lengthy to be kept as a Tablegen string literal. Move it to a proper Markdown file. --- mlir/docs/Dialects/Transform.md | 328 ++++++++++++++++++ .../mlir/Dialect/Transform/IR/CMakeLists.txt | 2 +- .../Dialect/Transform/IR/TransformDialect.td | 326 +---------------- 3 files changed, 330 insertions(+), 326 deletions(-) diff --git a/mlir/docs/Dialects/Transform.md b/mlir/docs/Dialects/Transform.md index 837cd8d92fb1..70ea6e9498fe 100644 --- a/mlir/docs/Dialects/Transform.md +++ b/mlir/docs/Dialects/Transform.md @@ -1,7 +1,335 @@ # Transform Dialect +Fine-grain transformation control dialect. + [TOC] +## Disclaimer + +**This dialect is actively developed and may change frequently.** + +To decrease the maintenance burden and churn, please post a description of +the intended use case on the MLIR forum. A few in-tree use cases are +currently supported: + + - high-level transformations on "structured ops" (i.e. ops that operate on + chunks of data in a way that can be decomposed into operations on + smaller chunks of data and control flow) in Linalg, Tensor and Vector + dialects; + - loop transformations in the SCF dialect. + +## Overview + +This dialect provides operations that can be used to control transformation +of the IR using a different portion of the IR. It refers to the IR being +transformed as payload IR, and to the IR guiding the transformation as +transform IR. + +The main use case for this dialect is orchestrating fine-grain +transformations on individual operations or sets thereof. For example, it +may involve finding loop-like operations with specific properties (e.g., +large size) in the payload IR, applying loop tiling to those and only those +operations, and then applying loop unrolling to the inner loops produced +by the previous transformations. As such, it is not intended as a +replacement for the pass infrastructure, nor for the pattern rewriting +infrastructure. In the most common case, the transform IR will be processed +and applied to the payload IR by a pass. Transformations expressed by the +transform dialect may be implemented using the pattern infrastructure or any +other relevant MLIR component. + +The following IR gives a rough idea of what the operations in this dialect +may look like: + +```mlir +%0 = transform.loop.find { size > 42 } : !transform.interface +%1:2 = transform.loop.tile %0 { tile_sizes = [2,3,4] } + : (!transform.interface) + -> (!transform.op, !transform.op) +transform.loop.unroll %1#1 : !transform.op +``` + +The values used in the Transform dialect, also referred to as *handles*, +correspond to (groups of) operations in the payload IR. In the example +above, `%0` corresponds to the set of loops found in the payload IR that +satisfy the condition, and `%1` correspond to groups of outer and inner +loops, respectively, produced by the tiling transformation. + +A transform handle such as `%0` may be associated with multiple payload +operations. This is conceptually a set of operations and no assumptions +should be made about the order of ops unless specified otherwise by the +operation. Most Transform IR ops support operand values that are mapped to +multiple operations. They usually apply the respective transformation for +every mapped op ("batched execution"). Deviations from this convention are +described in the documentation of Transform IR ops. + +The handle values have transform IR types. These types describe properties +of payload IR operations associated with the value that are known to the +transform dialect, for example, all associated payload operations implement +a "TileableOp" interface, or have a specific "loop" kind. These properties +are used to statically indicate pre- and post-conditions of a +transformation connected to a Transform dialect operation. The conditions +are verified when payload IR operations are first associated with a +transform handle. By convention, Transform dialect operations are expected +to indicate narrow preconditions for their operands by enforcing operand +type constraints in the their definitions and verifiers. On the contrary, +operations are expected to have few constraints on their results. Specific +instances of a transform operation can then be created with a more +restricted result type than the constraint in the operation (e.g., the +"find" operation only constrains the result type to be a transform IR type +while its concrete instance can have a type with stricter constraints such +as implementing the "tilable" interface). The verification will then happen +at transform execution time. This approach allows one to capture payload IR +operation properties in the transform IR without resorting to excessive +use of type casts or coupling dialect extensions between themselves. It is +a trade-off between verbosity/complexity and static hardening, which can +be revised in the future. + +Overall, Transform IR ops are expected to be contained in a single top-level +op. Such top-level ops specify how to apply the transformations described +by the operations they contain, e.g., `transform.sequence` executes +transformations one by one and fails if any of them fails. Such ops are +expected to have the `PossibleTopLevelTransformOpTrait` and may be used +without arguments. + +A program transformation expressed using the Transform dialect can be +programmatically triggered by calling: + +```c++ +LogicalResult transform::applyTransforms(Operation *payloadRoot, + TransformOpInterface transform, + const TransformOptions &options); +``` + +that applies the transformations specified by the top-level `transform` to +payload IR contained in `payloadRoot`. + +## Dialect Extension Mechanism + +This dialect is designed to be extensible, that is, clients of this dialect +are allowed to inject additional operations into this dialect using the +`TransformDialectExtension` mechanism. This allows the dialect to avoid a +dependency on the implementation of the transformation as well as to avoid +introducing dialect-specific transform dialects. In the example above, +the operations may have been injected by a notional `loop` dialect rather +than defined in this dialect, hence the common prefix. + +It is recommended to prefix injected operations with one or several +dot-separated words that indicate which extension adds them. For +dialect-specific transformations, the prefix is naturally the name of the +dialect, e.g., `transform.affine.reschedule`. For dialect-agnostic +transformations (typically implemented using interfaces), the prefix may +be derived from the interface name or from a common concept, e.g., +`transform.loop.tile` may apply to any loop-like operation that implements +`TileableOpInterface`. The C++ classes for the dialect extension should +include the prefix in their name, e.g., `AffineTransformDialectExtension` or +`LoopTransformDialectExtension` in the cases above. Unprefixed operation +names are reserved for ops defined directly in the Transform dialect. + +Operations injected into the dialect must: + + * Implement the `TransformOpInterface` to execute the corresponding + transformation on the payload IR. + + * Implement the `MemoryEffectsOpInterface` to annotate the effects of + the transform IR operation on the payload IR as well as on the mapping + between transform IR values and payload IR operations. See below for + the description of available effects. + +The presence of interface implementations is checked at runtime when the +dialect is loaded to allow for those implementations to be supplied by +separate dialect extensions if desired. + +## Side Effects + +The Transform dialect relies on MLIR side effect modelling to enable +optimization of the transform IR. More specifically, it provides several +side effect resource objects and expects operations to describe their +effects on these resources. + + * `TransformMappingResource` - side effect resource corresponding to the + mapping between transform IR values and payload IR operations. + + - An `Allocate` effect from this resource means creating a new mapping + entry, it is always accompanied by a `Write` effect. + + - A `Read` effect from this resource means accessing the mapping. + + - A `Free` effect on this resource indicates the removal of the mapping + entry, typically after a transformation that modifies the payload IR + operations associated with one of the transform IR operation's + operands. It is always accompanied by a `Read` effect. + + * `PayloadIRResource` - side effect resource corresponding to the payload + IR itself. + + - A `Read` effect from this resource means accessing the payload IR. + + - A `Write` effect on this resource means mutating the payload IR. It is + almost always accompanied by a `Read`. + +The typical flow of values in the transform IR is as follows. Most +operations produce new transform IR values and immediately associate them +with a list of payload IR operations. This corresponds to `Allocate` and +`Write` effects on the `TransformMappingResource`, and often requires at +least a `Read` effect on the `PayloadIRResource`. Transform operations that +only inspect the payload IR to produce new handles are usually limited to +these effects on their operands. Transform operations that mutate the +payload IR are thought to _consume_ the handles provided as operands, that +is have the `Read` and `Free` effects on them. As with the usual memory +effects, using a value after it was freed is incorrect. In case of the +transform IR, this value is likely associated with payload IR operations +that were modified or even removed by the transformation, so it is +meaningless to refer to them. When further transformations are desired, the +transform operations can return _new_ handles that can be read or consumed +by subsequent operations. + +## Execution Model + +The transformation starts at the user-specified top-level transform IR +operation and applies to some user-specified payload IR scope, identified by +the payload IR op that contains the IR to transform. It is the +responsibility of the user to properly select the scope and/or to avoid the +transformations to modify the IR outside of the given scope. The top-level +transform IR operation may contain further transform operations and execute +them in the desired order. + +Transformation application functions produce a tri-state status: + +- success; +- recoverable (silenceable) failure; +- irrecoverable failure. + +Transformation container operations may intercept recoverable failures and +perform the required recovery steps thus succeeding themselves. On +the other hand, they must propagate irrecoverable failures. For such +failures, the diagnostics are emitted immediately whereas their emission is +postponed for recoverable failures. Transformation container operations may +also fail to recover from a theoretically recoverable failure, in which case +they can either propagate it to their parent or emit the diagnostic and turn +the failure into an irrecoverable one. A recoverable failure produced by +applying the top-level transform IR operation is considered irrecoverable. + +Transformation container operations are allowed to "step over" some nested +operations if the application of some previous operation produced a failure. +This can be conceptually thought of as having a global "recoverable error +register" that is read/write accessed by each transform operation as a side +effect. The transformation is skipped if the register already contains an +error description, and the control flow proceeds to the following operation. + +Note that a silenceable failure, if emitted, is a compiler _error_ rather +than a warning. Transformations are expected to produce silenceable failures +if they haven't yet modified the payload IR, i.e. when reporting a +precondition failure, and an irrecoverable failure when they modified the IR +in a way that is contrary to the semantics of the transform operation or +would fail a postcondition. Some "navigation" operations that identify +payload IR targets for the following transformation may have a conceptual +"failure to match" that is considered a successful execution in the +execution model but results in handles associated with empty payload IR +operation lists. + +## Handle Invalidation + +The execution model of the transform dialect allows a payload IR operation +to be associated with _multiple_ handles as well as nested payload IR +operations to be associated with different handles. A transform IR operation +that consumes a handle automatically _invalidates_ all the other handles +associated with the same payload IR operations, or with any of their +descendants, as the consumed handle. Note that the _entire_ handle is +invalidated, even if some of the payload IR operations associated with it +or their ancestors were not associated with the consumed handle. Any use of +the invalidated handle results in undefined behavior since the payload IR +operations associated with it are likely to have been mutated or erased. The +mere fact of the handle being invalidated does _not_ trigger undefined +behavior, only its appearance as an operand does. + +The Transform dialect infrastructure has the capability of checking whether +the transform IR op operand is invalidated before applying the +transformation. However, such a check is computationally expensive and +must be enabled explicitly through `TransformOptions`. Additionally, the +`transform-dialect-check-uses` pass emits warnings when a handle may be used +after it has been consumed, but does so abstractly, without processing the +payload IR. + +## Intended Use and Integrations + +The transformation control infrastructure provided by this dialect is +positioned roughly between rewrite patterns and passes. A transformation +that is executed by a transform operation is likely to be sufficiently +complex to require at least a set of patterns to be implemented. It is also +expected to be more focused than a pass: a pass typically applies identical +transformations everywhere in the IR, a transform dialect-controlled +transformation would apply to a small subset of operations selected, e.g., +by a pattern-matching operation or generated by a previous transformation. +It is discouraged, although technically possible, to run a pass pipeline as +part of the transform op implementation. + +One of the main scenarios for using this dialect is fine-grain chaining of +transformations. For example, a loop-like operation may see its iteration +domain split into two parts, implemented as separate loops (transformation +known as index-set splitting), each of which is then transformed differently +(e.g., the first loop is tiled and the second unrolled) with the necessary +enabling and cleanup patterns around the main transformation: + +```mlir +// +// ... +%parts:2 = transform.loop.split %loop { upper_bound_divisible_by = 8 } +transform.loop.tile %parts#0 { tile_sizes = [8] } +transform.loop.unroll %parts#1 { full } +``` + +This composition would have been difficult to implement as separate passes +since the hypothetical "tiling" and "unrolling" pass would need to somehow +differentiate between the parts of the loop produced by the previous pass +(both are the same operation, and it is likely undesirable to pollute the +operation with pass-specific information). Implementing passes that run the +combined transformation would have run into the combinatorial explosion +issue due to multiple possible transform compositions or into the need for +deep pass parameterization, the ultimate form of which is an ad-hoc dialect +to specify which transformations the pass should run. The transform dialect +provides a uniform, extensible mechanism for controlling transformations in +such cases. + +The transform dialect is supposed to be consumed by an "interpreter" pass +that drives the application of transformations. To ensure extensibility and +composability, this pass is not expected to actually perform the +transformations specified by the ops. Instead, the transformations are +implemented by the transform ops themselves via `TransformOpInterface`. The +pass serves as the entry point, handles the flow of transform operations and +takes care of bookkeeping. As such, the transform dialect does not provide +the interpreter pass. Instead, it provides a set of utilities that can be +used by clients to define their own interpreter passes or as part of a more +complex pass. For example, the mapping between values in the transform IR +and operations in the payload IR, or the function that applies the +transformations specified by ops in the given block sequentially. Note that +a transform op may have regions with further transform ops in them, with +the op itself guiding how to dispatch the transformation control flow to +those regions. This approach allows clients to decide on the relative +location of the transform IR in their input (e.g., nested modules, separate +modules, optional regions to certain operations, etc.), register additional +transform operations and perform client-specific bookkeeping. + +## Effects on the Infrastructure + +Although scoped to a single dialect, this functionality conceptually belongs +to the MLIR infrastructure. It aims to be minimally intrusive and opt-in. + +Some infrastructural components may grow extra functionality to support the +transform dialect. In particular, the pattern infrastructure may add extra +hooks to identify the "main results" of a transformation or to notify +external observers about changes made to certain operations. These are not +expected to affect the existing uses of the infrastructure. + +For the sake of reusability, transformations should be implemented as +utility functions that are called from the interface methods of transform +ops rather than having the methods directly act on the payload IR. + +## Type Definitions + +[include "Dialects/TransformTypes.md"] + +## Core Operations + [include "Dialects/TransformOps.md"] ## Bufferization Transform Operations diff --git a/mlir/include/mlir/Dialect/Transform/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/Transform/IR/CMakeLists.txt index 99f3c417fa33..1487c2c5461d 100644 --- a/mlir/include/mlir/Dialect/Transform/IR/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/Transform/IR/CMakeLists.txt @@ -22,7 +22,7 @@ add_public_tablegen_target(MLIRTransformDialectEnumIncGen) add_dependencies(mlir-headers MLIRTransformDialectEnumIncGen) add_mlir_dialect(TransformOps transform) -add_mlir_doc(TransformOps TransformOps Dialects/ -gen-dialect-doc -dialect=transform) +add_mlir_doc(TransformOps TransformOps Dialects/ -gen-op-doc -dialect=transform) # Contrary to what the name claims, this only produces the _op_ interface. add_mlir_interface(TransformInterfaces) diff --git a/mlir/include/mlir/Dialect/Transform/IR/TransformDialect.td b/mlir/include/mlir/Dialect/Transform/IR/TransformDialect.td index 5308a0046402..a7bb75e9c7e7 100644 --- a/mlir/include/mlir/Dialect/Transform/IR/TransformDialect.td +++ b/mlir/include/mlir/Dialect/Transform/IR/TransformDialect.td @@ -13,331 +13,7 @@ include "mlir/IR/OpBase.td" def Transform_Dialect : Dialect { let summary = "Fine-grain transformation control dialect"; - let description = [{ - ## Disclaimer - - **This dialect is actively developed and may change frequently.** - - To decrease the maintenance burden and churn, please post a description of - the intended use case on the MLIR forum. A few in-tree use cases are - currently supported: - - - high-level transformations on "structured ops" (i.e. ops that operate on - chunks of data in a way that can be decomposed into operations on - smaller chunks of data and control flow) in Linalg, Tensor and Vector - dialects; - - loop transformations in the SCF dialect. - - ## Overview - - This dialect provides operations that can be used to control transformation - of the IR using a different portion of the IR. It refers to the IR being - transformed as payload IR, and to the IR guiding the transformation as - transform IR. - - The main use case for this dialect is orchestrating fine-grain - transformations on individual operations or sets thereof. For example, it - may involve finding loop-like operations with specific properties (e.g., - large size) in the payload IR, applying loop tiling to those and only those - operations, and then applying loop unrolling to the inner loops produced - by the previous transformations. As such, it is not intended as a - replacement for the pass infrastructure, nor for the pattern rewriting - infrastructure. In the most common case, the transform IR will be processed - and applied to the payload IR by a pass. Transformations expressed by the - transform dialect may be implemented using the pattern infrastructure or any - other relevant MLIR component. - - The following IR gives a rough idea of what the operations in this dialect - may look like: - - ```mlir - %0 = transform.loop.find { size > 42 } : !transform.interface - %1:2 = transform.loop.tile %0 { tile_sizes = [2,3,4] } - : (!transform.interface) - -> (!transform.op, !transform.op) - transform.loop.unroll %1#1 : !transform.op - ``` - - The values used in the Transform dialect, also referred to as *handles*, - correspond to (groups of) operations in the payload IR. In the example - above, `%0` corresponds to the set of loops found in the payload IR that - satisfy the condition, and `%1` correspond to groups of outer and inner - loops, respectively, produced by the tiling transformation. - - A transform handle such as `%0` may be associated with multiple payload - operations. This is conceptually a set of operations and no assumptions - should be made about the order of ops unless specified otherwise by the - operation. Most Transform IR ops support operand values that are mapped to - multiple operations. They usually apply the respective transformation for - every mapped op ("batched execution"). Deviations from this convention are - described in the documentation of Transform IR ops. - - The handle values have transform IR types. These types describe properties - of payload IR operations associated with the value that are known to the - transform dialect, for example, all associated payload operations implement - a "TileableOp" interface, or have a specific "loop" kind. These properties - are used to statically indicate pre- and post-conditions of a - transformation connected to a Transform dialect operation. The conditions - are verified when payload IR operations are first associated with a - transform handle. By convention, Transform dialect operations are expected - to indicate narrow preconditions for their operands by enforcing operand - type constraints in the their definitions and verifiers. On the contrary, - operations are expected to have few constraints on their results. Specific - instances of a transform operation can then be created with a more - restricted result type than the constraint in the operation (e.g., the - "find" operation only constrains the result type to be a transform IR type - while its concrete instance can have a type with stricter constraints such - as implementing the "tilable" interface). The verification will then happen - at transform execution time. This approach allows one to capture payload IR - operation properties in the transform IR without resorting to excessive - use of type casts or coupling dialect extensions between themselves. It is - a trade-off between verbosity/complexity and static hardening, which can - be revised in the future. - - Overall, Transform IR ops are expected to be contained in a single top-level - op. Such top-level ops specify how to apply the transformations described - by the operations they contain, e.g., `transform.sequence` executes - transformations one by one and fails if any of them fails. Such ops are - expected to have the `PossibleTopLevelTransformOpTrait` and may be used - without arguments. - - A program transformation expressed using the Transform dialect can be - programmatically triggered by calling: - - ```c++ - LogicalResult transform::applyTransforms(Operation *payloadRoot, - TransformOpInterface transform, - const TransformOptions &options); - ``` - - that applies the transformations specified by the top-level `transform` to - payload IR contained in `payloadRoot`. - - ## Dialect Extension Mechanism - - This dialect is designed to be extensible, that is, clients of this dialect - are allowed to inject additional operations into this dialect using the - `TransformDialectExtension` mechanism. This allows the dialect to avoid a - dependency on the implementation of the transformation as well as to avoid - introducing dialect-specific transform dialects. In the example above, - the operations may have been injected by a notional `loop` dialect rather - than defined in this dialect, hence the common prefix. - - It is recommended to prefix injected operations with one or several - dot-separated words that indicate which extension adds them. For - dialect-specific transformations, the prefix is naturally the name of the - dialect, e.g., `transform.affine.reschedule`. For dialect-agnostic - transformations (typically implemented using interfaces), the prefix may - be derived from the interface name or from a common concept, e.g., - `transform.loop.tile` may apply to any loop-like operation that implements - `TileableOpInterface`. The C++ classes for the dialect extension should - include the prefix in their name, e.g., `AffineTransformDialectExtension` or - `LoopTransformDialectExtension` in the cases above. Unprefixed operation - names are reserved for ops defined directly in the Transform dialect. - - Operations injected into the dialect must: - - * Implement the `TransformOpInterface` to execute the corresponding - transformation on the payload IR. - - * Implement the `MemoryEffectsOpInterface` to annotate the effects of - the transform IR operation on the payload IR as well as on the mapping - between transform IR values and payload IR operations. See below for - the description of available effects. - - The presence of interface implementations is checked at runtime when the - dialect is loaded to allow for those implementations to be supplied by - separate dialect extensions if desired. - - ## Side Effects - - The Transform dialect relies on MLIR side effect modelling to enable - optimization of the transform IR. More specifically, it provides several - side effect resource objects and expects operations to describe their - effects on these resources. - - * `TransformMappingResource` - side effect resource corresponding to the - mapping between transform IR values and payload IR operations. - - - An `Allocate` effect from this resource means creating a new mapping - entry, it is always accompanied by a `Write` effect. - - - A `Read` effect from this resource means accessing the mapping. - - - A `Free` effect on this resource indicates the removal of the mapping - entry, typically after a transformation that modifies the payload IR - operations associated with one of the transform IR operation's - operands. It is always accompanied by a `Read` effect. - - * `PayloadIRResource` - side effect resource corresponding to the payload - IR itself. - - - A `Read` effect from this resource means accessing the payload IR. - - - A `Write` effect on this resource means mutating the payload IR. It is - almost always accompanied by a `Read`. - - The typical flow of values in the transform IR is as follows. Most - operations produce new transform IR values and immediately associate them - with a list of payload IR operations. This corresponds to `Allocate` and - `Write` effects on the `TransformMappingResource`, and often requires at - least a `Read` effect on the `PayloadIRResource`. Transform operations that - only inspect the payload IR to produce new handles are usually limited to - these effects on their operands. Transform operations that mutate the - payload IR are thought to _consume_ the handles provided as operands, that - is have the `Read` and `Free` effects on them. As with the usual memory - effects, using a value after it was freed is incorrect. In case of the - transform IR, this value is likely associated with payload IR operations - that were modified or even removed by the transformation, so it is - meaningless to refer to them. When further transformations are desired, the - transform operations can return _new_ handles that can be read or consumed - by subsequent operations. - - ## Execution Model - - The transformation starts at the user-specified top-level transform IR - operation and applies to some user-specified payload IR scope, identified by - the payload IR op that contains the IR to transform. It is the - responsibility of the user to properly select the scope and/or to avoid the - transformations to modify the IR outside of the given scope. The top-level - transform IR operation may contain further transform operations and execute - them in the desired order. - - Transformation application functions produce a tri-state status: - - - success; - - recoverable (silenceable) failure; - - irrecoverable failure. - - Transformation container operations may intercept recoverable failures and - perform the required recovery steps thus succeeding themselves. On - the other hand, they must propagate irrecoverable failures. For such - failures, the diagnostics are emitted immediately whereas their emission is - postponed for recoverable failures. Transformation container operations may - also fail to recover from a theoretically recoverable failure, in which case - they can either propagate it to their parent or emit the diagnostic and turn - the failure into an irrecoverable one. A recoverable failure produced by - applying the top-level transform IR operation is considered irrecoverable. - - Transformation container operations are allowed to "step over" some nested - operations if the application of some previous operation produced a failure. - This can be conceptually thought of as having a global "recoverable error - register" that is read/write accessed by each transform operation as a side - effect. The transformation is skipped if the register already contains an - error description, and the control flow proceeds to the following operation. - - Note that a silenceable failure, if emitted, is a compiler _error_ rather - than a warning. Transformations are expected to produce silenceable failures - if they haven't yet modified the payload IR, i.e. when reporting a - precondition failure, and an irrecoverable failure when they modified the IR - in a way that is contrary to the semantics of the transform operation or - would fail a postcondition. Some "navigation" operations that identify - payload IR targets for the following transformation may have a conceptual - "failure to match" that is considered a successful execution in the - execution model but results in handles associated with empty payload IR - operation lists. - - ## Handle Invalidation - - The execution model of the transform dialect allows a payload IR operation - to be associated with _multiple_ handles as well as nested payload IR - operations to be associated with different handles. A transform IR operation - that consumes a handle automatically _invalidates_ all the other handles - associated with the same payload IR operations, or with any of their - descendants, as the consumed handle. Note that the _entire_ handle is - invalidated, even if some of the payload IR operations associated with it - or their ancestors were not associated with the consumed handle. Any use of - the invalidated handle results in undefined behavior since the payload IR - operations associated with it are likely to have been mutated or erased. The - mere fact of the handle being invalidated does _not_ trigger undefined - behavior, only its appearance as an operand does. - - The Transform dialect infrastructure has the capability of checking whether - the transform IR op operand is invalidated before applying the - transformation. However, such a check is computationally expensive and - must be enabled explicitly through `TransformOptions`. Additionally, the - `transform-dialect-check-uses` pass emits warnings when a handle may be used - after it has been consumed, but does so abstractly, without processing the - payload IR. - - ## Intended Use and Integrations - - The transformation control infrastructure provided by this dialect is - positioned roughly between rewrite patterns and passes. A transformation - that is executed by a transform operation is likely to be sufficiently - complex to require at least a set of patterns to be implemented. It is also - expected to be more focused than a pass: a pass typically applies identical - transformations everywhere in the IR, a transform dialect-controlled - transformation would apply to a small subset of operations selected, e.g., - by a pattern-matching operation or generated by a previous transformation. - It is discouraged, although technically possible, to run a pass pipeline as - part of the transform op implementation. - - One of the main scenarios for using this dialect is fine-grain chaining of - transformations. For example, a loop-like operation may see its iteration - domain split into two parts, implemented as separate loops (transformation - known as index-set splitting), each of which is then transformed differently - (e.g., the first loop is tiled and the second unrolled) with the necessary - enabling and cleanup patterns around the main transformation: - - ```mlir - // - // ... - %parts:2 = transform.loop.split %loop { upper_bound_divisible_by = 8 } - transform.loop.tile %parts#0 { tile_sizes = [8] } - transform.loop.unroll %parts#1 { full } - ``` - - This composition would have been difficult to implement as separate passes - since the hypothetical "tiling" and "unrolling" pass would need to somehow - differentiate between the parts of the loop produced by the previous pass - (both are the same operation, and it is likely undesirable to pollute the - operation with pass-specific information). Implementing passes that run the - combined transformation would have run into the combinatorial explosion - issue due to multiple possible transform compositions or into the need for - deep pass parameterization, the ultimate form of which is an ad-hoc dialect - to specify which transformations the pass should run. The transform dialect - provides a uniform, extensible mechanism for controlling transformations in - such cases. - - The transform dialect is supposed to be consumed by an "interpreter" pass - that drives the application of transformations. To ensure extensibility and - composability, this pass is not expected to actually perform the - transformations specified by the ops. Instead, the transformations are - implemented by the transform ops themselves via `TransformOpInterface`. The - pass serves as the entry point, handles the flow of transform operations and - takes care of bookkeeping. As such, the transform dialect does not provide - the interpreter pass. Instead, it provides a set of utilities that can be - used by clients to define their own interpreter passes or as part of a more - complex pass. For example, the mapping between values in the transform IR - and operations in the payload IR, or the function that applies the - transformations specified by ops in the given block sequentially. Note that - a transform op may have regions with further transform ops in them, with - the op itself guiding how to dispatch the transformation control flow to - those regions. This approach allows clients to decide on the relative - location of the transform IR in their input (e.g., nested modules, separate - modules, optional regions to certain operations, etc.), register additional - transform operations and perform client-specific bookkeeping. - - ## Effects on the Infrastructure - - Although scoped to a single dialect, this functionality conceptually belongs - to the MLIR infrastructure. It aims to be minimally intrusive and opt-in. - - Some infrastructural components may grow extra functionality to support the - transform dialect. In particular, the pattern infrastructure may add extra - hooks to identify the "main results" of a transformation or to notify - external observers about changes made to certain operations. These are not - expected to affect the existing uses of the infrastructure. - - For the sake of reusability, transformations should be implemented as - utility functions that are called from the interface methods of transform - ops rather than having the methods directly act on the payload IR. - - ## Type Definitions - - [include "Dialects/TransformTypes.md"] - }]; + // For description, see docs/Dialects/Transform.md. let name = "transform"; let cppNamespace = "::mlir::transform";