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