1//===- TransformDialect.td - Transform dialect definition --*- tablegen -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef MLIR_DIALECT_TRANSFORM_IR_TRANSFORMDIALECT
10#define MLIR_DIALECT_TRANSFORM_IR_TRANSFORMDIALECT
11
12include "mlir/IR/OpBase.td"
13
14def Transform_Dialect : Dialect {
15  let summary = "Fine-grain transformation control dialect";
16  let description = [{
17    ## Disclaimer
18
19    ** Proceed with care: not ready for general use. **
20
21    This dialect is evolving rapidly and may change on a very short notice. To
22    decrease the maintenance burden and churn, only a few in-tree use cases are
23    currently supported in the main tree:
24
25      - high-level transformations on "structured ops" (i.e. ops that operate on
26        chunks of data in a way that can be decomposed into operations on
27        smaller chunks of data and control flow) in Linalg, Tensor and Vector
28        dialects.
29
30    *Please post a description of the intended use case on the MLIR forum and
31    wait for confirmation.*
32
33    ## Overview
34
35    This dialect provides operations that can be used to control transformation
36    of the IR using a different portion of the IR. It refers to the IR being
37    transformed as payload IR, and to the IR guiding the transformation as
38    transform IR.
39
40    The main use case for this dialect is orchestrating fine-grain
41    transformations on individual operations or sets thereof. For example, it
42    may involve finding loop-like operations with specific properties (e.g.,
43    large size) in the payload IR, applying loop tiling to those and only those
44    operations, and then applying loop unrolling to the inner loops produced
45    by the previous transformations. As such, it is not intended as a
46    replacement for the pass infrastructure, nor for the pattern rewriting
47    infrastructure. In the most common case, the transform IR will be processed
48    and applied to the payload IR by a pass. Transformations expressed by the
49    transform dialect may be implemented using the pattern infrastructure or any
50    other relevant MLIR component.
51
52    The following IR gives a rough idea of what the operations in this dialect
53    may look like:
54
55    ```mlir
56    %0 = transform.loop.find { size > 42 }
57    %1:2 = transform.loop.tile { tile_sizes = [2,3,4] }
58    transform.loop.unroll %1#1
59    ```
60
61    The values defined by operations in this dialect correspond to (groups of)
62    operations in the payload IR. In the example above, `%0` corresponds to the
63    set of loops found in the payload IR that satisfy the condition, and `%1`
64    correspond to groups of outer and inner loops, respectively, produced by
65    the tiling transformation.
66
67    A Transform IR value such as `%0` may be associated with multiple payload
68    operations. This is conceptually a set of operations and no assumptions
69    should be made about the order of ops. Most Transform IR ops support
70    operand values that are mapped to multiple operations. They usually apply
71    the respective transformation for every mapped op ("batched execution").
72    Deviations from this convention are described in the documentation of
73    Transform IR ops.
74
75    Overall, Transform IR ops are expected to be contained in a single top-level
76    op. Such top-level ops specify how to apply the transformations described
77    by the operations they contain, e.g., `transform.sequence` executes
78    transformations one by one and fails if any of them fails. Such ops are
79    expected to have the `PossibleTopLevelTransformOpTrait` and may be used
80    without arguments.
81
82    ## Dialect Extension Mechanism
83
84    This dialect is designed to be extensible, that is, clients of this dialect
85    are allowed to inject additional operations into this dialect using the
86    `TransformDialectExtension` mechanism. This allows the dialect to avoid a
87    dependency on the implementation of the transformation as well as to avoid
88    introducing dialect-specific transform dialects. In the example above,
89    the operations may have been injected by a notional `loop` dialect rather
90    than defined in this dialect, hence the common prefix.
91
92    It is recommended to prefix injected operations with one or several
93    dot-separated words that indicate which extension adds them. For
94    dialect-specific transformations, the prefix is naturally the name of the
95    dialect, e.g., `transform.affine.reschedule`. For dialect-agnostic
96    transformations (typically implemented using interfaces), the prefix may
97    be derived from the interface name or from a common concept, e.g.,
98    `transform.loop.tile` may apply to any loop-like operation that implements
99    `TileableOpInterface`. The C++ classes for the dialect extension should
100    include the prefix in their name, e.g., `AffineTransformDialectExtension` or
101    `LoopTransformDialectExtension` in the cases above. Unprefixed operation
102    names are reserved for ops defined directly in the Transform dialect.
103
104    Operations injected into the dialect must:
105
106      * Implement the `TransformOpInterface` to execute the corresponding
107        transformation on the payload IR.
108
109      * Implement the `MemoryEffectsOpInterface` to annotate the effects of
110        the transform IR operation on the payload IR as well as on the mapping
111        between transform IR values and payload IR operations. See below for
112        the description of available effects.
113
114    The presence of interface implementations is checked at runtime when the
115    dialect is loaded to allow for those implementations to be supplied by
116    separate dialect extensions if desired.
117
118    ## Side Effects
119
120    The Transform dialect relies on MLIR side effect modelling to enable
121    optimization of the transform IR. More specifically, it provides several
122    side effect resource objects and expects operations to describe their
123    effects on these resources.
124
125      * `TransformMappingResource` - side effect resource corresponding to the
126        mapping between transform IR values and payload IR operations.
127
128        - An `Allocate` effect from this resource means creating a new mapping
129          entry, it is always accompanied by a `Write` effect.
130
131        - A `Read` effect from this resource means accessing the mapping.
132
133        - A `Free` effect on this resource indicates the removal of the mapping
134          entry, typically after a transformation that modifies the payload IR
135          operations associated with one of the transform IR operation's
136          operands. It is always accompanied by a `Read` effect.
137
138      * `PayloadIRResource` - side effect resource corresponding to the payload
139        IR itself.
140
141        - A `Read` effect from this resource means accessing the payload IR.
142
143        - A `Write` effect on this resource means mutating the payload IR. It is
144          almost always accompanied by a `Read`.
145
146    The typical flow of values in the transform IR is as follows. Most
147    operations produce new transform IR values and immediately associate them
148    with a list of payload IR operations. This corresponds to `Allocate` and
149    `Write` effects on the `TransformMappingResource`, and often requires at
150    least a `Read` effect on the `PayloadIRResource`. Transform operations that
151    only inspect the payload IR to produce new handles are usually limited to
152    these effects on their operands. Transform operations that mutate the
153    payload IR are thought to _consume_ the handles provided as operands, that
154    is have the `Read` and `Free` effects on them. As with the usual memory
155    effects, using a value after it was freed is incorrect. In case of the
156    transform IR, this value is likely associated with payload IR operations
157    that were modified or even removed by the transformation, so it is
158    meaningless to refer to them. When further transformations are desired, the
159    transform operations can return _new_ handles that can be read or consumed
160    by subsequent operations.
161
162    ## Execution Model
163
164    The transformation starts at the specifed top-level transform IR operation
165    and applies to some payload IR scope, identified by the payload IR op that
166    contains the IR to transform. It is the responsibility of the user to
167    properly select the scope and/or to avoid the transformations to modify the
168    IR outside of the given scope. The top-level transform IR operation may
169    contain further transform operations and execute them in the desired order.
170
171    Transformation application functions produce a tri-state status:
172
173    - success;
174    - recoverable (silencable) failure;
175    - irrecoverable failure.
176
177    Transformation container operations may intercept recoverable failures and
178    perform the required recovery steps thus succeeding themselves. On
179    the other hand, they must propagate irrecoverable failures. For such
180    failures, the diagnostics are emitted immediately whereas their emission is
181    postponed for recoverable failures. Transformation container operations may
182    also fail to recover from a theoretically recoverable failure, in which case
183    they are expected to emit the diagnostic and turn the failure into an
184    irrecoverable one. A recoverable failure produced by applying the top-level
185    transform IR operation is considered irrecoverable.
186
187    Transformation container operations are allowed to "step over" some nested
188    operations if the application of some previous operation produced a failure.
189    This can be conceptually thought of as having a global "recoverable error
190    register" that is read/write accessed by each transform operation as a side
191    effect. The transformation is skipped if the register already contains an
192    error description, and the control flow proceeds to the following operation.
193
194    ## Handle Invalidation
195
196    The execution model of the transform dialect expects that a payload IR
197    operation is associated with _at most one_ transform IR handle. This avoids
198    the situation when a handle to an operation outlives the operation itself
199    that can be erased during a transformation triggered through another handle.
200
201    Handles pointing to operations nested in each other are allowed to co-exist
202    in the transform IR. However, a transform IR operation that consumes such a
203    handle automatically _invalidates_ all the other handles that are associated
204    with operations nested in the operations associated with the consumed
205    handle. Any use of the invalidated handle results in undefined behavior
206    since the payload IR operations associated with it are likely to have been
207    mutated or erased. The mere fact of the handle being invalidated does _not_
208    trigger undefined behavior, only its appearance as an operand does.
209    Invalidation applies to the entire handle, even if some of the payload IR
210    operations associated with it are not nested in payload IR operations
211    associated with another, consumed handle.
212
213    Note: the restriction on two handles not pointing to the same operation may
214    be relaxed in the future to follow the invalidation model for nested
215    operation.
216
217    The Transform dialect infrastructure has the capability of checking whether
218    the transform IR op operand is invalidated before applying the
219    transformation. However, such a check is computationally expensive and
220    must be enabled explicitly through `TransformOptions`. Additionally, the
221    `transform-dialect-check-uses` pass emits warnings when a handle may be used
222    after it has been consumed, but does so abstractly, without processing the
223    payload IR.
224
225    ## Intended Use and Integrations
226
227    The transformation control infrastructure provided by this dialect is
228    positioned roughly between rewrite patterns and passes. A transformation
229    that is executed by a transform operation is likely to be sufficiently
230    complex to require at least a set of patterns to be implemented. It is also
231    expected to be more focused than a pass: a pass typically applies identical
232    transformations everywhere in the IR, a transform dialect-controlled
233    transformation would apply to a small subset of operations selected, e.g.,
234    by a pattern-matching operation or generated by a previous transformation.
235    It is discouraged, although technically possible, to run a pass pipeline as
236    part of the transform op implementation.
237
238    One of the main scenarios for using this dialect is fine-grain chaining of
239    transformations. For example, a loop-like operation may see its iteration
240    domain split into two parts, implemented as separate loops (transformation
241    known as index-set splitting), each of which is then transformed differently
242    (e.g., the first loop is tiled and the second unrolled) with the necessary
243    enabling and cleanup patterns around the main transformation:
244
245    ```mlir
246    // <generate %loop, e.g., by pattern-matching>
247    // ...
248    %parts:2 = transform.loop.split %loop { upper_bound_divisible_by = 8 }
249    transform.loop.tile %parts#0 { tile_sizes = [8] }
250    transform.loop.unroll %parts#1 { full }
251    ```
252
253    This composition would have been difficult to implement as separate passes
254    since the hypothetical "tiling" and "unrolling" pass would need to somehow
255    differentiate between the parts of the loop produced by the previous pass
256    (both are the same operation, and it is likely undesirable to pollute the
257    operation with pass-specific information). Implementing passes that run the
258    combined transformation would have run into the combinatorial explosion
259    issue due to multiple possible transform compositions or into the need for
260    deep pass parameterization, the ultimate form of which is an ad-hoc dialect
261    to specify which transformations the pass should run. The transform dialect
262    provides a uniform, extensible mechanism for controlling transformations in
263    such cases.
264
265    The transform dialect is supposed to be consumed by an "interpreter" pass
266    that drives the application of transformations. To ensure extensibility and
267    composability, this pass is not expected to actually perform the
268    transformations specified by the ops. Instead, the transformations are
269    implemented by the transform ops themselves via `TransformOpInterface`. The
270    pass serves as the entry point, handles the flow of transform operations and
271    takes care of bookkeeping. As such, the transform dialect does not provide
272    the interpreter pass. Instead, it provides a set of utilities that can be
273    used by clients to define their own interpreter passes or as part of a more
274    complex pass. For example, the mapping between values in the transform IR
275    and operations in the payload IR, or the function that applies the
276    transformations specified by ops in the given block sequentially. Note that
277    a transform op may have regions with further transform ops in them, with
278    the op itself guiding how to dispatch the transformation control flow to
279    those regions. This approach allows clients to decide on the relative
280    location of the transform IR in their input (e.g., nested modules, separate
281    modules, optional regions to certain operations, etc.), register additional
282    transform operations and perform client-specific bookkeeping.
283
284    ## Effects on the Infrastructure
285
286    Although scoped to a single dialect, this functionality conceptually belongs
287    to the MLIR infrastructure. It aims to be minimally intrusive and opt-in.
288
289    Some infrastructural components may grow extra functionality to support the
290    transform dialect. In particular, the pattern infrastructure may add extra
291    hooks to identify the "main results" of a transformation or to notify
292    external observers about changes made to certain operations. These are not
293    expected to affect the existing uses of the infrastructure.
294
295    For the sake of reusability, transformations should be implemented as
296    utility functions that are called from the interface methods of transform
297    ops rather than having the methods directly act on the payload IR.
298  }];
299
300  let name = "transform";
301  let cppNamespace = "::mlir::transform";
302  let emitAccessorPrefix = kEmitAccessorPrefix_Prefixed;
303
304  let dependentDialects = [
305    "::mlir::pdl::PDLDialect",
306    "::mlir::pdl_interp::PDLInterpDialect",
307  ];
308
309  let extraClassDeclaration = [{
310      /// Returns the named PDL constraint functions available in the dialect
311      /// as a map from their name to the function.
312      const ::llvm::StringMap<::mlir::PDLConstraintFunction> &
313      getPDLConstraintHooks() const;
314
315    private:
316      template <typename OpTy>
317      void addOperationIfNotRegistered() {
318        Optional<RegisteredOperationName> opName =
319            RegisteredOperationName::lookup(OpTy::getOperationName(),
320                                            getContext());
321        if (!opName)
322          return addOperations<OpTy>();
323
324        if (opName->getTypeID() == TypeID::get<OpTy>())
325          return;
326
327        llvm::errs() << "error: extensible dialect operation '"
328                     << OpTy::getOperationName()
329                     << "' is already registered with a mismatching TypeID";
330        abort();
331      }
332
333      /// Registers operations specified as template parameters with this
334      /// dialect. Checks that they implement the required interfaces.
335      template <typename... OpTys>
336      void addOperationsChecked() {
337        (void)std::initializer_list<int>{(addOperationIfNotRegistered<OpTys>(),
338                                          0)...};
339
340        #ifndef NDEBUG
341        (void)std::initializer_list<int>{
342          (detail::checkImplementsTransformInterface<OpTys>(getContext()),
343           0)...};
344        #endif // NDEBUG
345      }
346
347      template <typename, typename...>
348      friend class TransformDialectExtension;
349
350      /// Takes ownership of the named PDL constraint function from the given
351      /// map and makes them available for use by the operations in the dialect.
352      void mergeInPDLMatchHooks(
353          ::llvm::StringMap<::mlir::PDLConstraintFunction> &&constraintFns);
354
355      /// A container for PDL constraint function that can be used by
356      /// operations in this dialect.
357      PDLPatternModule pdlMatchHooks;
358  }];
359}
360
361// Base class for ops that belong to the tranfsorm dialect. Ops defined in
362// extensions of this dialect may also use this.
363class TransformDialectOp<string mnemonic, list<Trait> traits = []>
364    : Op<Transform_Dialect, mnemonic, traits>;
365
366// Trait for operations that may be top-level operations in Transform IR.
367// Operations must have one single-block region and must be usable without
368// operands. See the C++ definition of the trait for more information.
369def PossibleTopLevelTransformOpTrait
370    : NativeOpTrait<"PossibleTopLevelTransformOpTrait"> {
371  let cppNamespace = "::mlir::transform";
372}
373
374#endif // MLIR_DIALECT_TRANSFORM_IR_TRANSFORMDIALECT
375