1# Dialect Conversion
2
3This document describes a framework in MLIR in which to perform operation
4conversions between, and within dialects. This framework allows for transforming
5illegal operations to those supported by a provided conversion target, via a set
6of pattern-based operation rewriting patterns.
7
8[TOC]
9
10The dialect conversion framework consists of the following components:
11
12*   A [Conversion Target](#conversion-target)
13*   A set of [Rewrite Patterns](#rewrite-pattern-specification)
14*   A [Type Converter](#type-conversion) (Optional)
15
16## Modes of Conversion
17
18When applying a conversion to a set of operations, there are several different
19conversion modes that may be selected from:
20
21*   Partial Conversion
22
23    -   A partial conversion will legalize as many operations to the target as
24        possible, but will allow pre-existing operations that were not
25        explicitly marked as "illegal" to remain unconverted. This allows for
26        partially lowering parts of the input in the presence of unknown
27        operations.
28    -   A partial conversion can be applied via `applyPartialConversion`.
29
30*   Full Conversion
31
32    -   A full conversion legalizes all input operations, and is only successful
33        if all operations are properly legalized to the given conversion target.
34        This ensures that only known operations will exist after the conversion
35        process.
36    -   A full conversion can be applied via `applyFullConversion`.
37
38*   Analysis Conversion
39
40    -   An analysis conversion will analyze which operations are legalizable to
41        the given conversion target if a conversion were to be applied. This is
42        done by performing a 'partial' conversion and recording which operations
43        would have been successfully converted if successful. Note that no
44        rewrites, or transformations, are actually applied to the input
45        operations.
46    -   An analysis conversion can be applied via `applyAnalysisConversion`.
47
48In all cases, the framework walks the operations in preorder, examining an op
49before the ops in any regions it has.
50
51## Conversion Target
52
53The conversion target is a formal definition of what is considered to be legal
54during the conversion process. The final operations generated by the conversion
55framework must be marked as legal on the `ConversionTarget` for the rewrite to
56be a success. Depending on the conversion mode, existing operations need not
57always be legal. Operations and dialects may be marked with any of the provided
58legality actions below:
59
60*   Legal
61
62    -   This action signals that every instance of a given operation is legal,
63        i.e. any combination of attributes, operands, types, etc. are valid.
64
65*   Dynamic
66
67    -   This action signals that only some instances of a given operation are
68        legal. This allows for defining fine-tune constraints, e.g. saying that
69        `arith.addi` is only legal when operating on 32-bit integers.
70
71*   Illegal
72
73    -   This action signals that no instance of a given operation is legal.
74        Operations marked as "illegal" must always be converted for the
75        conversion to be successful. This action also allows for selectively
76        marking specific operations as illegal in an otherwise legal dialect.
77
78An example conversion target is shown below:
79
80```c++
81struct MyTarget : public ConversionTarget {
82  MyTarget(MLIRContext &ctx) : ConversionTarget(ctx) {
83    //--------------------------------------------------------------------------
84    // Marking an operation as Legal:
85
86    /// Mark all operations within the LLVM dialect are legal.
87    addLegalDialect<LLVMDialect>();
88
89    /// Mark `arith.constant` op is always legal on this target.
90    addLegalOp<arith::ConstantOp>();
91
92    //--------------------------------------------------------------------------
93    // Marking an operation as dynamically legal.
94
95    /// Mark all operations within Affine dialect have dynamic legality
96    /// constraints.
97    addDynamicallyLegalDialect<AffineDialect>([](Operation *op) { ... });
98
99    /// Mark `std.return` as dynamically legal, but provide a specific legality
100    /// callback.
101    addDynamicallyLegalOp<ReturnOp>([](ReturnOp op) { ... });
102
103    /// Treat unknown operations, i.e. those without a legalization action
104    /// directly set, as dynamically legal.
105    markUnknownOpDynamicallyLegal([](Operation *op) { ... });
106
107    //--------------------------------------------------------------------------
108    // Marking an operation as illegal.
109
110    /// All operations within the GPU dialect are illegal.
111    addIllegalDialect<GPUDialect>();
112
113    /// Mark `std.br` and `std.cond_br` as illegal.
114    addIllegalOp<BranchOp, CondBranchOp>();
115  }
116
117  /// Implement the default legalization handler to handle operations marked as
118  /// dynamically legal that were not provided with an explicit handler.
119  bool isDynamicallyLegal(Operation *op) override { ... }
120};
121```
122
123### Recursive Legality
124
125In some cases, it may be desirable to mark entire regions as legal. This
126provides an additional granularity of context to the concept of "legal". If an
127operation is marked recursively legal, either statically or dynamically, then
128all of the operations nested within are also considered legal even if they would
129otherwise be considered "illegal". An operation can be marked via
130`markOpRecursivelyLegal<>`:
131
132```c++
133ConversionTarget &target = ...;
134
135/// The operation must first be marked as `Legal` or `Dynamic`.
136target.addLegalOp<MyOp>(...);
137target.addDynamicallyLegalOp<MySecondOp>(...);
138
139/// Mark the operation as always recursively legal.
140target.markOpRecursivelyLegal<MyOp>();
141/// Mark optionally with a callback to allow selective marking.
142target.markOpRecursivelyLegal<MyOp, MySecondOp>([](Operation *op) { ... });
143/// Mark optionally with a callback to allow selective marking.
144target.markOpRecursivelyLegal<MyOp>([](MyOp op) { ... });
145```
146
147## Rewrite Pattern Specification
148
149After the conversion target has been defined, a set of legalization patterns
150must be provided to transform illegal operations into legal ones. The patterns
151supplied here have the same structure and restrictions as those described in the
152main [Pattern](PatternRewriter.md) documentation. The patterns provided do not
153need to generate operations that are directly legal on the target. The framework
154will automatically build a graph of conversions to convert non-legal operations
155into a set of legal ones.
156
157As an example, say you define a target that supports one operation: `foo.add`.
158When providing the following patterns: [`bar.add` -> `baz.add`, `baz.add` ->
159`foo.add`], the framework will automatically detect that it can legalize
160`bar.add` -> `foo.add` even though a direct conversion does not exist. This
161means that you don’t have to define a direct legalization pattern for `bar.add`
162-> `foo.add`.
163
164### Conversion Patterns
165
166Along with the general `RewritePattern` classes, the conversion framework
167provides a special type of rewrite pattern that can be used when a pattern
168relies on interacting with constructs specific to the conversion process, the
169`ConversionPattern`. For example, the conversion process does not necessarily
170update operations in-place and instead creates a mapping of events such as
171replacements and erasures, and only applies them when the entire conversion
172process is successful. Certain classes of patterns rely on using the
173updated/remapped operands of an operation, such as when the types of results
174defined by an operation have changed. The general Rewrite Patterns can no longer
175be used in these situations, as the types of the operands of the operation being
176matched will not correspond with those expected by the user. This pattern
177provides, as an additional argument to the `matchAndRewrite` and `rewrite`
178methods, the list of operands that the operation should use after conversion. If
179an operand was the result of a non-converted operation, for example if it was
180already legal, the original operand is used. This means that the operands
181provided always have a 1-1 non-null correspondence with the operands on the
182operation. The original operands of the operation are still intact and may be
183inspected as normal. These patterns also utilize a special `PatternRewriter`,
184`ConversionPatternRewriter`, that provides special hooks for use with the
185conversion infrastructure.
186
187```c++
188struct MyConversionPattern : public ConversionPattern {
189  /// The `matchAndRewrite` hooks on ConversionPatterns take an additional
190  /// `operands` parameter, containing the remapped operands of the original
191  /// operation.
192  virtual LogicalResult
193  matchAndRewrite(Operation *op, ArrayRef<Value> operands,
194                  ConversionPatternRewriter &rewriter) const;
195};
196```
197
198#### Type Safety
199
200The types of the remapped operands provided to a conversion pattern must be of a
201type expected by the pattern. The expected types of a pattern are determined by
202a provided [TypeConverter](#type-converter). If no type converter is provided,
203the types of the remapped operands are expected to match the types of the
204original operands. If a type converter is provided, the types of the remapped
205operands are expected to be legal as determined by the converter. If the
206remapped operand types are not of an expected type, and a materialization to the
207expected type could not be performed, the pattern fails application before the
208`matchAndRewrite` hook is invoked. This ensures that patterns do not have to
209explicitly ensure type safety, or sanitize the types of the incoming remapped
210operands. More information on type conversion is detailed in the
211[dedicated section](#type-conversion) below.
212
213## Type Conversion
214
215It is sometimes necessary as part of a conversion to convert the set types of
216being operated on. In these cases, a `TypeConverter` object may be defined that
217details how types should be converted when interfacing with a pattern. A
218`TypeConverter` may be used to convert the signatures of block arguments and
219regions, to define the expected inputs types of the pattern, and to reconcile
220type differences in general.
221
222### Type Converter
223
224The `TypeConverter` contains several hooks for detailing how to convert types,
225and how to materialize conversions between types in various situations. The two
226main aspects of the `TypeConverter` are conversion and materialization.
227
228A `conversion` describes how a given illegal source `Type` should be converted
229to N target types. If the source type is already "legal", it should convert to
230itself. Type conversions are specified via the `addConversion` method described
231below.
232
233A `materialization` describes how a set of values should be converted to a
234single value of a desired type. An important distinction with a `conversion` is
235that a `materialization` can produce IR, whereas a `conversion` cannot. These
236materializations are used by the conversion framework to ensure type safety
237during the conversion process. There are several types of materializations
238depending on the situation.
239
240*   Argument Materialization
241
242    -   An argument materialization is used when converting the type of a block
243        argument during a [signature conversion](#region-signature-conversion).
244
245*   Source Materialization
246
247    -   A source materialization converts from a value with a "legal" target
248        type, back to a specific source type. This is used when an operation is
249        "legal" during the conversion process, but contains a use of an illegal
250        type. This may happen during a conversion where some operations are
251        converted to those with different resultant types, but still retain
252        users of the original type system.
253    -   This materialization is used in the following situations:
254        *   When a block argument has been converted to a different type, but
255            the original argument still has users that will remain live after
256            the conversion process has finished.
257        *   When the result type of an operation has been converted to a
258            different type, but the original result still has users that will
259            remain live after the conversion process is finished.
260
261*   Target Materialization
262
263    -   A target materialization converts from a value with an "illegal" source
264        type, to a value of a "legal" type. This is used when a pattern expects
265        the remapped operands to be of a certain set of types, but the original
266        input operands have not been converted. This may happen during a
267        conversion where some operations are converted to those with different
268        resultant types, but still retain uses of the original type system.
269    -   This materialization is used in the following situations:
270        *   When the remapped operands of a
271            [conversion pattern](#conversion-patterns) are not legal for the
272            type conversion provided by the pattern.
273
274If a converted value is used by an operation that isn't converted, it needs a
275conversion back to the `source` type, hence source materialization; if an
276unconverted value is used by an operation that is being converted, it needs
277conversion to the `target` type, hence target materialization.
278
279As noted above, the conversion process guarantees that the type contract of the
280IR is preserved during the conversion. This means that the types of value uses
281will not implicitly change during the conversion process. When the type of a
282value definition, either block argument or operation result, is being changed,
283the users of that definition must also be updated during the conversion process.
284If they aren't, a type conversion must be materialized to ensure that a value of
285the expected type is still present within the IR. If a target materialization is
286required, but cannot be performed, the pattern application fails. If a source
287materialization is required, but cannot be performed, the entire conversion
288process fails.
289
290Several of the available hooks are detailed below:
291
292```c++
293class TypeConverter {
294 public:
295  /// Register a conversion function. A conversion function defines how a given
296  /// source type should be converted. A conversion function must be convertible
297  /// to any of the following forms(where `T` is a class derived from `Type`:
298  ///   * Optional<Type>(T)
299  ///     - This form represents a 1-1 type conversion. It should return nullptr
300  ///       or `llvm::None` to signify failure. If `llvm::None` is returned, the
301  ///       converter is allowed to try another conversion function to perform
302  ///       the conversion.
303  ///   * Optional<LogicalResult>(T, SmallVectorImpl<Type> &)
304  ///     - This form represents a 1-N type conversion. It should return
305  ///       `failure` or `llvm::None` to signify a failed conversion. If the new
306  ///       set of types is empty, the type is removed and any usages of the
307  ///       existing value are expected to be removed during conversion. If
308  ///       `llvm::None` is returned, the converter is allowed to try another
309  ///       conversion function to perform the conversion.
310  ///   * Optional<LogicalResult>(T, SmallVectorImpl<Type> &, ArrayRef<Type>)
311  ///     - This form represents a 1-N type conversion supporting recursive
312  ///       types. The first two arguments and the return value are the same as
313  ///       for the regular 1-N form. The third argument is contains is the
314  ///       "call stack" of the recursive conversion: it contains the list of
315  ///       types currently being converted, with the current type being the
316  ///       last one. If it is present more than once in the list, the
317  ///       conversion concerns a recursive type.
318  /// Note: When attempting to convert a type, e.g. via 'convertType', the
319  ///       mostly recently added conversions will be invoked first.
320  template <typename FnT,
321            typename T = typename llvm::function_traits<FnT>::template arg_t<0>>
322  void addConversion(FnT &&callback) {
323    registerConversion(wrapCallback<T>(std::forward<FnT>(callback)));
324  }
325
326  /// Register a materialization function, which must be convertible to the
327  /// following form:
328  ///   `Optional<Value> (OpBuilder &, T, ValueRange, Location)`,
329  ///   where `T` is any subclass of `Type`.
330  /// This function is responsible for creating an operation, using the
331  /// OpBuilder and Location provided, that "converts" a range of values into a
332  /// single value of the given type `T`. It must return a Value of the
333  /// converted type on success, an `llvm::None` if it failed but other
334  /// materialization can be attempted, and `nullptr` on unrecoverable failure.
335  /// It will only be called for (sub)types of `T`.
336  ///
337  /// This method registers a materialization that will be called when
338  /// converting an illegal block argument type, to a legal type.
339  template <typename FnT,
340            typename T = typename llvm::function_traits<FnT>::template arg_t<1>>
341  void addArgumentMaterialization(FnT &&callback) {
342    argumentMaterializations.emplace_back(
343        wrapMaterialization<T>(std::forward<FnT>(callback)));
344  }
345  /// This method registers a materialization that will be called when
346  /// converting a legal type to an illegal source type. This is used when
347  /// conversions to an illegal type must persist beyond the main conversion.
348  template <typename FnT,
349            typename T = typename llvm::function_traits<FnT>::template arg_t<1>>
350  void addSourceMaterialization(FnT &&callback) {
351    sourceMaterializations.emplace_back(
352        wrapMaterialization<T>(std::forward<FnT>(callback)));
353  }
354  /// This method registers a materialization that will be called when
355  /// converting type from an illegal, or source, type to a legal type.
356  template <typename FnT,
357            typename T = typename llvm::function_traits<FnT>::template arg_t<1>>
358  void addTargetMaterialization(FnT &&callback) {
359    targetMaterializations.emplace_back(
360        wrapMaterialization<T>(std::forward<FnT>(callback)));
361  }
362};
363```
364
365### Region Signature Conversion
366
367From the perspective of type conversion, the types of block arguments are a bit
368special. Throughout the conversion process, blocks may move between regions of
369different operations. Given this, the conversion of the types for blocks must be
370done explicitly via a conversion pattern. To convert the types of block
371arguments within a Region, a custom hook on the `ConversionPatternRewriter` must
372be invoked; `convertRegionTypes`. This hook uses a provided type converter to
373apply type conversions to all blocks within a given region, and all blocks that
374move into that region. As noted above, the conversions performed by this method
375use the argument materialization hook on the `TypeConverter`. This hook also
376takes an optional `TypeConverter::SignatureConversion` parameter that applies a
377custom conversion to the entry block of the region. The types of the entry block
378arguments are often tied semantically to details on the operation, e.g. FuncOp,
379AffineForOp, etc. To convert the signature of just the region entry block, and
380not any other blocks within the region, the `applySignatureConversion` hook may
381be used instead. A signature conversion, `TypeConverter::SignatureConversion`,
382can be built programmatically:
383
384```c++
385class SignatureConversion {
386public:
387    /// Remap an input of the original signature with a new set of types. The
388    /// new types are appended to the new signature conversion.
389    void addInputs(unsigned origInputNo, ArrayRef<Type> types);
390
391    /// Append new input types to the signature conversion, this should only be
392    /// used if the new types are not intended to remap an existing input.
393    void addInputs(ArrayRef<Type> types);
394
395    /// Remap an input of the original signature with a range of types in the
396    /// new signature.
397    void remapInput(unsigned origInputNo, unsigned newInputNo,
398                    unsigned newInputCount = 1);
399
400    /// Remap an input of the original signature to another `replacement`
401    /// value. This drops the original argument.
402    void remapInput(unsigned origInputNo, Value replacement);
403};
404```
405
406The `TypeConverter` provides several default utilities for signature conversion
407and legality checking:
408`convertSignatureArgs`/`convertBlockSignature`/`isLegal(Region *|Type)`.
409
410## Debugging
411
412To debug the execution of the dialect conversion framework,
413`-debug-only=dialect-conversion` may be used. This command line flag activates
414LLVM's debug logging infrastructure solely for the conversion framework. The
415output is formatted as a tree structure, mirroring the structure of the
416conversion process. This output contains all of the actions performed by the
417rewriter, how generated operations get legalized, and why they fail.
418
419Example output is shown below:
420
421```
422//===-------------------------------------------===//
423Legalizing operation : 'std.return'(0x608000002e20) {
424  "std.return"() : () -> ()
425
426  * Fold {
427  } -> FAILURE : unable to fold
428
429  * Pattern : 'std.return -> ()' {
430    ** Insert  : 'spv.Return'(0x6070000453e0)
431    ** Replace : 'std.return'(0x608000002e20)
432
433    //===-------------------------------------------===//
434    Legalizing operation : 'spv.Return'(0x6070000453e0) {
435      "spv.Return"() : () -> ()
436
437    } -> SUCCESS : operation marked legal by the target
438    //===-------------------------------------------===//
439  } -> SUCCESS : pattern applied successfully
440} -> SUCCESS
441//===-------------------------------------------===//
442```
443
444This output is describing the legalization of an `std.return` operation. We
445first try to legalize by folding the operation, but that is unsuccessful for
446`std.return`. From there, a pattern is applied that replaces the `std.return`
447with a `spv.Return`. The newly generated `spv.Return` is then processed for
448legalization, but is found to already legal as per the target.
449