2019-08-19 17:01:13 -07:00
|
|
|
|
# Dialect Conversion
|
|
|
|
|
|
|
|
|
|
|
|
This document describes a framework in MLIR in which to perform operation
|
|
|
|
|
|
conversions between, and within dialects. This framework allows for transforming
|
|
|
|
|
|
illegal operations to those supported by a provided conversion target, via a set
|
|
|
|
|
|
of pattern-based operation rewriting patterns.
|
|
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
The dialect conversion framework consists of the following components:
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
* A [Conversion Target](#conversion-target)
|
|
|
|
|
|
* A set of [Rewrite Patterns](#rewrite-pattern-specification)
|
|
|
|
|
|
* A [Type Converter](#type-conversion) (Optional)
|
|
|
|
|
|
|
2022-08-02 13:22:40 +02:00
|
|
|
|
[TOC]
|
|
|
|
|
|
|
2019-08-19 17:01:13 -07:00
|
|
|
|
## Modes of Conversion
|
|
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
When applying a conversion to a set of operations, there are several different
|
|
|
|
|
|
conversion modes that may be selected from:
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
* Partial Conversion
|
|
|
|
|
|
|
|
|
|
|
|
- A partial conversion will legalize as many operations to the target as
|
|
|
|
|
|
possible, but will allow pre-existing operations that were not
|
2020-08-13 12:04:57 -07:00
|
|
|
|
explicitly marked as "illegal" to remain unconverted. This allows for
|
|
|
|
|
|
partially lowering parts of the input in the presence of unknown
|
2019-08-19 17:01:13 -07:00
|
|
|
|
operations.
|
|
|
|
|
|
- A partial conversion can be applied via `applyPartialConversion`.
|
|
|
|
|
|
|
|
|
|
|
|
* Full Conversion
|
|
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
- A full conversion legalizes all input operations, and is only successful
|
|
|
|
|
|
if all operations are properly legalized to the given conversion target.
|
|
|
|
|
|
This ensures that only known operations will exist after the conversion
|
|
|
|
|
|
process.
|
2019-08-19 17:01:13 -07:00
|
|
|
|
- A full conversion can be applied via `applyFullConversion`.
|
|
|
|
|
|
|
|
|
|
|
|
* Analysis Conversion
|
|
|
|
|
|
|
|
|
|
|
|
- An analysis conversion will analyze which operations are legalizable to
|
2020-08-13 12:04:57 -07:00
|
|
|
|
the given conversion target if a conversion were to be applied. This is
|
|
|
|
|
|
done by performing a 'partial' conversion and recording which operations
|
|
|
|
|
|
would have been successfully converted if successful. Note that no
|
|
|
|
|
|
rewrites, or transformations, are actually applied to the input
|
2019-08-19 17:01:13 -07:00
|
|
|
|
operations.
|
|
|
|
|
|
- An analysis conversion can be applied via `applyAnalysisConversion`.
|
|
|
|
|
|
|
2021-05-14 12:45:57 -07:00
|
|
|
|
In all cases, the framework walks the operations in preorder, examining an op
|
|
|
|
|
|
before the ops in any regions it has.
|
|
|
|
|
|
|
2019-08-19 17:01:13 -07:00
|
|
|
|
## Conversion Target
|
|
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
The conversion target is a formal definition of what is considered to be legal
|
2019-08-19 17:01:13 -07:00
|
|
|
|
during the conversion process. The final operations generated by the conversion
|
|
|
|
|
|
framework must be marked as legal on the `ConversionTarget` for the rewrite to
|
2020-08-13 12:04:57 -07:00
|
|
|
|
be a success. Depending on the conversion mode, existing operations need not
|
|
|
|
|
|
always be legal. Operations and dialects may be marked with any of the provided
|
|
|
|
|
|
legality actions below:
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
* Legal
|
|
|
|
|
|
|
|
|
|
|
|
- This action signals that every instance of a given operation is legal,
|
|
|
|
|
|
i.e. any combination of attributes, operands, types, etc. are valid.
|
|
|
|
|
|
|
|
|
|
|
|
* Dynamic
|
|
|
|
|
|
|
|
|
|
|
|
- This action signals that only some instances of a given operation are
|
|
|
|
|
|
legal. This allows for defining fine-tune constraints, e.g. saying that
|
2021-10-12 23:14:57 +00:00
|
|
|
|
`arith.addi` is only legal when operating on 32-bit integers.
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
* Illegal
|
|
|
|
|
|
|
|
|
|
|
|
- This action signals that no instance of a given operation is legal.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
Operations marked as "illegal" must always be converted for the
|
2019-08-19 17:01:13 -07:00
|
|
|
|
conversion to be successful. This action also allows for selectively
|
2019-11-15 09:48:54 -08:00
|
|
|
|
marking specific operations as illegal in an otherwise legal dialect.
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
2021-12-22 16:04:55 +05:30
|
|
|
|
Operations and dialects that are neither explicitly marked legal nor illegal are
|
|
|
|
|
|
separate from the above ("unknown" operations) and are treated differently, for
|
|
|
|
|
|
example, for the purposes of partial conversion as mentioned above.
|
|
|
|
|
|
|
2019-08-19 17:01:13 -07:00
|
|
|
|
An example conversion target is shown below:
|
|
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
|
struct MyTarget : public ConversionTarget {
|
|
|
|
|
|
MyTarget(MLIRContext &ctx) : ConversionTarget(ctx) {
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
// Marking an operation as Legal:
|
|
|
|
|
|
|
|
|
|
|
|
/// Mark all operations within the LLVM dialect are legal.
|
2020-12-02 00:00:36 -08:00
|
|
|
|
addLegalDialect<LLVMDialect>();
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
2021-10-14 16:55:33 +00:00
|
|
|
|
/// Mark `arith.constant` op is always legal on this target.
|
|
|
|
|
|
addLegalOp<arith::ConstantOp>();
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
// Marking an operation as dynamically legal.
|
|
|
|
|
|
|
|
|
|
|
|
/// Mark all operations within Affine dialect have dynamic legality
|
|
|
|
|
|
/// constraints.
|
2023-04-20 11:02:05 +09:00
|
|
|
|
addDynamicallyLegalDialect<affine::AffineDialect>(
|
|
|
|
|
|
[](Operation *op) { ... });
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
2022-02-26 14:49:54 -08:00
|
|
|
|
/// Mark `func.return` as dynamically legal, but provide a specific legality
|
2019-08-19 17:01:13 -07:00
|
|
|
|
/// callback.
|
2022-02-26 14:49:54 -08:00
|
|
|
|
addDynamicallyLegalOp<func::ReturnOp>([](func::ReturnOp op) { ... });
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
2020-01-27 19:04:55 -08:00
|
|
|
|
/// Treat unknown operations, i.e. those without a legalization action
|
|
|
|
|
|
/// directly set, as dynamically legal.
|
|
|
|
|
|
markUnknownOpDynamicallyLegal([](Operation *op) { ... });
|
|
|
|
|
|
|
2019-08-19 17:01:13 -07:00
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
// Marking an operation as illegal.
|
|
|
|
|
|
|
|
|
|
|
|
/// All operations within the GPU dialect are illegal.
|
|
|
|
|
|
addIllegalDialect<GPUDialect>();
|
|
|
|
|
|
|
2022-02-03 20:59:43 -08:00
|
|
|
|
/// Mark `cf.br` and `cf.cond_br` as illegal.
|
|
|
|
|
|
addIllegalOp<cf::BranchOp, cf::CondBranchOp>();
|
2019-08-19 17:01:13 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Implement the default legalization handler to handle operations marked as
|
|
|
|
|
|
/// dynamically legal that were not provided with an explicit handler.
|
|
|
|
|
|
bool isDynamicallyLegal(Operation *op) override { ... }
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2019-10-28 10:03:57 -07:00
|
|
|
|
### Recursive Legality
|
|
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
In some cases, it may be desirable to mark entire regions as legal. This
|
|
|
|
|
|
provides an additional granularity of context to the concept of "legal". If an
|
|
|
|
|
|
operation is marked recursively legal, either statically or dynamically, then
|
|
|
|
|
|
all of the operations nested within are also considered legal even if they would
|
|
|
|
|
|
otherwise be considered "illegal". An operation can be marked via
|
|
|
|
|
|
`markOpRecursivelyLegal<>`:
|
2019-10-28 10:03:57 -07:00
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
|
ConversionTarget &target = ...;
|
|
|
|
|
|
|
|
|
|
|
|
/// The operation must first be marked as `Legal` or `Dynamic`.
|
|
|
|
|
|
target.addLegalOp<MyOp>(...);
|
|
|
|
|
|
target.addDynamicallyLegalOp<MySecondOp>(...);
|
|
|
|
|
|
|
|
|
|
|
|
/// Mark the operation as always recursively legal.
|
|
|
|
|
|
target.markOpRecursivelyLegal<MyOp>();
|
|
|
|
|
|
/// Mark optionally with a callback to allow selective marking.
|
|
|
|
|
|
target.markOpRecursivelyLegal<MyOp, MySecondOp>([](Operation *op) { ... });
|
|
|
|
|
|
/// Mark optionally with a callback to allow selective marking.
|
|
|
|
|
|
target.markOpRecursivelyLegal<MyOp>([](MyOp op) { ... });
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2019-08-19 17:01:13 -07:00
|
|
|
|
## Rewrite Pattern Specification
|
|
|
|
|
|
|
|
|
|
|
|
After the conversion target has been defined, a set of legalization patterns
|
2020-08-13 12:05:04 -07:00
|
|
|
|
must be provided to transform illegal operations into legal ones. The patterns
|
|
|
|
|
|
supplied here have the same structure and restrictions as those described in the
|
|
|
|
|
|
main [Pattern](PatternRewriter.md) documentation. The patterns provided do not
|
|
|
|
|
|
need to generate operations that are directly legal on the target. The framework
|
|
|
|
|
|
will automatically build a graph of conversions to convert non-legal operations
|
|
|
|
|
|
into a set of legal ones.
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
As an example, say you define a target that supports one operation: `foo.add`.
|
|
|
|
|
|
When providing the following patterns: [`bar.add` -> `baz.add`, `baz.add` ->
|
|
|
|
|
|
`foo.add`], the framework will automatically detect that it can legalize
|
2020-05-26 00:48:06 -06:00
|
|
|
|
`bar.add` -> `foo.add` even though a direct conversion does not exist. This
|
2019-08-19 17:01:13 -07:00
|
|
|
|
means that you don’t have to define a direct legalization pattern for `bar.add`
|
|
|
|
|
|
-> `foo.add`.
|
|
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
### Conversion Patterns
|
|
|
|
|
|
|
|
|
|
|
|
Along with the general `RewritePattern` classes, the conversion framework
|
|
|
|
|
|
provides a special type of rewrite pattern that can be used when a pattern
|
|
|
|
|
|
relies on interacting with constructs specific to the conversion process, the
|
|
|
|
|
|
`ConversionPattern`. For example, the conversion process does not necessarily
|
|
|
|
|
|
update operations in-place and instead creates a mapping of events such as
|
|
|
|
|
|
replacements and erasures, and only applies them when the entire conversion
|
|
|
|
|
|
process is successful. Certain classes of patterns rely on using the
|
|
|
|
|
|
updated/remapped operands of an operation, such as when the types of results
|
|
|
|
|
|
defined by an operation have changed. The general Rewrite Patterns can no longer
|
|
|
|
|
|
be used in these situations, as the types of the operands of the operation being
|
|
|
|
|
|
matched will not correspond with those expected by the user. This pattern
|
2025-03-07 08:43:01 +01:00
|
|
|
|
provides, as an additional argument to the `matchAndRewrite` method, the list
|
|
|
|
|
|
of operands that the operation should use after conversion. If an operand was
|
|
|
|
|
|
the result of a non-converted operation, for example if it was already legal,
|
|
|
|
|
|
the original operand is used. This means that the operands provided always have
|
|
|
|
|
|
a 1-1 non-null correspondence with the operands on the operation. The original
|
|
|
|
|
|
operands of the operation are still intact and may be inspected as normal.
|
|
|
|
|
|
These patterns also utilize a special `PatternRewriter`,
|
2020-08-13 12:04:57 -07:00
|
|
|
|
`ConversionPatternRewriter`, that provides special hooks for use with the
|
|
|
|
|
|
conversion infrastructure.
|
2019-10-18 02:40:06 -07:00
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
```c++
|
|
|
|
|
|
struct MyConversionPattern : public ConversionPattern {
|
|
|
|
|
|
/// The `matchAndRewrite` hooks on ConversionPatterns take an additional
|
|
|
|
|
|
/// `operands` parameter, containing the remapped operands of the original
|
|
|
|
|
|
/// operation.
|
|
|
|
|
|
virtual LogicalResult
|
|
|
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
|
|
|
|
|
ConversionPatternRewriter &rewriter) const;
|
|
|
|
|
|
};
|
|
|
|
|
|
```
|
2019-10-18 02:40:06 -07:00
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
#### Type Safety
|
|
|
|
|
|
|
|
|
|
|
|
The types of the remapped operands provided to a conversion pattern must be of a
|
|
|
|
|
|
type expected by the pattern. The expected types of a pattern are determined by
|
|
|
|
|
|
a provided [TypeConverter](#type-converter). If no type converter is provided,
|
|
|
|
|
|
the types of the remapped operands are expected to match the types of the
|
|
|
|
|
|
original operands. If a type converter is provided, the types of the remapped
|
|
|
|
|
|
operands are expected to be legal as determined by the converter. If the
|
|
|
|
|
|
remapped operand types are not of an expected type, and a materialization to the
|
|
|
|
|
|
expected type could not be performed, the pattern fails application before the
|
|
|
|
|
|
`matchAndRewrite` hook is invoked. This ensures that patterns do not have to
|
|
|
|
|
|
explicitly ensure type safety, or sanitize the types of the incoming remapped
|
|
|
|
|
|
operands. More information on type conversion is detailed in the
|
|
|
|
|
|
[dedicated section](#type-conversion) below.
|
2019-10-18 02:40:06 -07:00
|
|
|
|
|
2019-08-19 17:01:13 -07:00
|
|
|
|
## Type Conversion
|
|
|
|
|
|
|
|
|
|
|
|
It is sometimes necessary as part of a conversion to convert the set types of
|
2019-11-15 09:48:54 -08:00
|
|
|
|
being operated on. In these cases, a `TypeConverter` object may be defined that
|
2020-08-13 12:04:57 -07:00
|
|
|
|
details how types should be converted when interfacing with a pattern. A
|
|
|
|
|
|
`TypeConverter` may be used to convert the signatures of block arguments and
|
|
|
|
|
|
regions, to define the expected inputs types of the pattern, and to reconcile
|
|
|
|
|
|
type differences in general.
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
### Type Converter
|
|
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
The `TypeConverter` contains several hooks for detailing how to convert types,
|
|
|
|
|
|
and how to materialize conversions between types in various situations. The two
|
|
|
|
|
|
main aspects of the `TypeConverter` are conversion and materialization.
|
|
|
|
|
|
|
2024-11-28 12:30:54 +09:00
|
|
|
|
A `conversion` describes how a given source `Type` should be converted to N
|
|
|
|
|
|
target types. If the source type is converted to itself, we say it is a "legal"
|
|
|
|
|
|
type. Type conversions are specified via the `addConversion` method described
|
2020-08-13 12:04:57 -07:00
|
|
|
|
below.
|
|
|
|
|
|
|
2024-11-28 12:30:54 +09:00
|
|
|
|
A `materialization` describes how a list of values should be converted to a
|
|
|
|
|
|
list of values with specific types. An important distinction from a
|
|
|
|
|
|
`conversion` is that a `materialization` can produce IR, whereas a `conversion`
|
|
|
|
|
|
cannot. These materializations are used by the conversion framework to ensure
|
|
|
|
|
|
type safety during the conversion process. There are several types of
|
|
|
|
|
|
materializations depending on the situation.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
|
|
|
|
|
|
* Source Materialization
|
|
|
|
|
|
|
2024-11-28 12:30:54 +09:00
|
|
|
|
- A source materialization is used when a value was replaced with a value
|
|
|
|
|
|
of a different type, but there are still users that expects the original
|
|
|
|
|
|
("source") type at the end of the conversion process. A source
|
|
|
|
|
|
materialization converts the replacement value back to the source type.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
- This materialization is used in the following situations:
|
|
|
|
|
|
* When a block argument has been converted to a different type, but
|
|
|
|
|
|
the original argument still has users that will remain live after
|
|
|
|
|
|
the conversion process has finished.
|
2024-07-06 12:05:00 +02:00
|
|
|
|
* When a block argument has been dropped, but the argument still has
|
|
|
|
|
|
users that will remain live after the conversion process has
|
|
|
|
|
|
finished.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
* When the result type of an operation has been converted to a
|
|
|
|
|
|
different type, but the original result still has users that will
|
|
|
|
|
|
remain live after the conversion process is finished.
|
|
|
|
|
|
|
|
|
|
|
|
* Target Materialization
|
|
|
|
|
|
|
2024-11-28 12:30:54 +09:00
|
|
|
|
- A target materialization converts a value to the type that is expected
|
|
|
|
|
|
by a conversion pattern according to its type converter.
|
|
|
|
|
|
- A target materialization is used when a pattern expects the remapped
|
|
|
|
|
|
operands to be of a certain set of types, but the original input
|
|
|
|
|
|
operands have either not been replaced or been replaced with values of
|
|
|
|
|
|
a different type.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
|
|
|
|
|
|
If a converted value is used by an operation that isn't converted, it needs a
|
|
|
|
|
|
conversion back to the `source` type, hence source materialization; if an
|
|
|
|
|
|
unconverted value is used by an operation that is being converted, it needs
|
|
|
|
|
|
conversion to the `target` type, hence target materialization.
|
|
|
|
|
|
|
|
|
|
|
|
As noted above, the conversion process guarantees that the type contract of the
|
|
|
|
|
|
IR is preserved during the conversion. This means that the types of value uses
|
|
|
|
|
|
will not implicitly change during the conversion process. When the type of a
|
|
|
|
|
|
value definition, either block argument or operation result, is being changed,
|
|
|
|
|
|
the users of that definition must also be updated during the conversion process.
|
|
|
|
|
|
If they aren't, a type conversion must be materialized to ensure that a value of
|
2024-11-28 12:30:54 +09:00
|
|
|
|
the expected type is still present within the IR. If a materialization is
|
|
|
|
|
|
required, but cannot be performed, the entire conversion process fails.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
|
|
|
|
|
|
Several of the available hooks are detailed below:
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
|
class TypeConverter {
|
|
|
|
|
|
public:
|
2020-08-13 12:04:57 -07:00
|
|
|
|
/// Register a conversion function. A conversion function defines how a given
|
|
|
|
|
|
/// source type should be converted. A conversion function must be convertible
|
2020-02-18 15:56:33 -08:00
|
|
|
|
/// to any of the following forms(where `T` is a class derived from `Type`:
|
|
|
|
|
|
/// * Optional<Type>(T)
|
|
|
|
|
|
/// - This form represents a 1-1 type conversion. It should return nullptr
|
2022-12-04 19:58:32 -08:00
|
|
|
|
/// or `std::nullopt` to signify failure. If `std::nullopt` is returned, the
|
2020-02-18 15:56:33 -08:00
|
|
|
|
/// converter is allowed to try another conversion function to perform
|
|
|
|
|
|
/// the conversion.
|
|
|
|
|
|
/// * Optional<LogicalResult>(T, SmallVectorImpl<Type> &)
|
|
|
|
|
|
/// - This form represents a 1-N type conversion. It should return
|
2022-12-04 19:58:32 -08:00
|
|
|
|
/// `failure` or `std::nullopt` to signify a failed conversion. If the new
|
2020-02-18 15:56:33 -08:00
|
|
|
|
/// set of types is empty, the type is removed and any usages of the
|
|
|
|
|
|
/// existing value are expected to be removed during conversion. If
|
2022-12-04 19:58:32 -08:00
|
|
|
|
/// `std::nullopt` is returned, the converter is allowed to try another
|
2020-02-18 15:56:33 -08:00
|
|
|
|
/// conversion function to perform the conversion.
|
2021-11-22 13:19:36 +01:00
|
|
|
|
/// * Optional<LogicalResult>(T, SmallVectorImpl<Type> &, ArrayRef<Type>)
|
|
|
|
|
|
/// - This form represents a 1-N type conversion supporting recursive
|
|
|
|
|
|
/// types. The first two arguments and the return value are the same as
|
|
|
|
|
|
/// for the regular 1-N form. The third argument is contains is the
|
|
|
|
|
|
/// "call stack" of the recursive conversion: it contains the list of
|
|
|
|
|
|
/// types currently being converted, with the current type being the
|
|
|
|
|
|
/// last one. If it is present more than once in the list, the
|
|
|
|
|
|
/// conversion concerns a recursive type.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
/// Note: When attempting to convert a type, e.g. via 'convertType', the
|
|
|
|
|
|
/// mostly recently added conversions will be invoked first.
|
|
|
|
|
|
template <typename FnT,
|
|
|
|
|
|
typename T = typename llvm::function_traits<FnT>::template arg_t<0>>
|
|
|
|
|
|
void addConversion(FnT &&callback) {
|
|
|
|
|
|
registerConversion(wrapCallback<T>(std::forward<FnT>(callback)));
|
|
|
|
|
|
}
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
2024-07-06 12:05:00 +02:00
|
|
|
|
/// All of the following materializations require function objects that are
|
|
|
|
|
|
/// convertible to the following form:
|
|
|
|
|
|
/// `std::optional<Value>(OpBuilder &, T, ValueRange, Location)`,
|
|
|
|
|
|
/// where `T` is any subclass of `Type`. This function is responsible for
|
|
|
|
|
|
/// creating an operation, using the OpBuilder and Location provided, that
|
|
|
|
|
|
/// "casts" a range of values into a single value of the given type `T`. It
|
|
|
|
|
|
/// must return a Value of the converted type on success, an `std::nullopt` if
|
|
|
|
|
|
/// it failed but other materialization can be attempted, and `nullptr` on
|
|
|
|
|
|
/// unrecoverable failure. It will only be called for (sub)types of `T`.
|
|
|
|
|
|
/// Materialization functions must be provided when a type conversion may
|
|
|
|
|
|
/// persist after the conversion has finished.
|
|
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
/// This method registers a materialization that will be called when
|
2024-11-28 12:30:54 +09:00
|
|
|
|
/// converting a replacement value back to its original source type.
|
|
|
|
|
|
/// This is used when some uses of the original value persist beyond the main
|
|
|
|
|
|
/// conversion.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
template <typename FnT,
|
|
|
|
|
|
typename T = typename llvm::function_traits<FnT>::template arg_t<1>>
|
|
|
|
|
|
void addSourceMaterialization(FnT &&callback) {
|
|
|
|
|
|
sourceMaterializations.emplace_back(
|
2025-05-08 08:22:38 +02:00
|
|
|
|
wrapSourceMaterialization<T>(std::forward<FnT>(callback)));
|
2020-08-13 12:04:57 -07:00
|
|
|
|
}
|
2024-07-06 12:05:00 +02:00
|
|
|
|
|
2020-08-13 12:04:57 -07:00
|
|
|
|
/// This method registers a materialization that will be called when
|
2024-11-28 12:30:54 +09:00
|
|
|
|
/// converting a value to a target type according to a pattern's type
|
|
|
|
|
|
/// converter.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Note: Target materializations can optionally inspect the "original"
|
|
|
|
|
|
/// type. This type may be different from the type of the input value.
|
|
|
|
|
|
/// For example, let's assume that a conversion pattern "P1" replaced an SSA
|
|
|
|
|
|
/// value "v1" (type "t1") with "v2" (type "t2"). Then a different conversion
|
|
|
|
|
|
/// pattern "P2" matches an op that has "v1" as an operand. Let's furthermore
|
|
|
|
|
|
/// assume that "P2" determines that the converted target type of "t1" is
|
|
|
|
|
|
/// "t3", which may be different from "t2". In this example, the target
|
|
|
|
|
|
/// materialization will be invoked with: outputType = "t3", inputs = "v2",
|
|
|
|
|
|
/// originalType = "t1". Note that the original type "t1" cannot be recovered
|
|
|
|
|
|
/// from just "t3" and "v2"; that's why the originalType parameter exists.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Note: During a 1:N conversion, the result types can be a TypeRange. In
|
|
|
|
|
|
/// that case the materialization produces a SmallVector<Value>.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
template <typename FnT,
|
|
|
|
|
|
typename T = typename llvm::function_traits<FnT>::template arg_t<1>>
|
|
|
|
|
|
void addTargetMaterialization(FnT &&callback) {
|
|
|
|
|
|
targetMaterializations.emplace_back(
|
2025-05-08 08:22:38 +02:00
|
|
|
|
wrapTargetMaterialization<T>(std::forward<FnT>(callback)));
|
2020-08-13 12:04:57 -07:00
|
|
|
|
}
|
2019-08-19 17:01:13 -07:00
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2024-09-06 10:35:07 +02:00
|
|
|
|
Materializations through the type converter are optional. If the
|
|
|
|
|
|
`ConversionConfig::buildMaterializations` flag is set to "false", the dialect
|
|
|
|
|
|
conversion driver builds an `unrealized_conversion_cast` op instead of calling
|
|
|
|
|
|
the respective type converter callback whenever a materialization is required.
|
|
|
|
|
|
|
2019-08-19 17:01:13 -07:00
|
|
|
|
### Region Signature Conversion
|
|
|
|
|
|
|
2020-06-18 15:45:43 -07:00
|
|
|
|
From the perspective of type conversion, the types of block arguments are a bit
|
|
|
|
|
|
special. Throughout the conversion process, blocks may move between regions of
|
|
|
|
|
|
different operations. Given this, the conversion of the types for blocks must be
|
2024-06-10 21:49:52 +02:00
|
|
|
|
done explicitly via a conversion pattern.
|
|
|
|
|
|
|
|
|
|
|
|
To convert the types of block arguments within a Region, a custom hook on the
|
|
|
|
|
|
`ConversionPatternRewriter` must be invoked; `convertRegionTypes`. This hook
|
|
|
|
|
|
uses a provided type converter to apply type conversions to all blocks of a
|
2025-01-03 16:11:56 +01:00
|
|
|
|
given region. This hook also takes an optional
|
|
|
|
|
|
`TypeConverter::SignatureConversion` parameter that applies a custom conversion
|
|
|
|
|
|
to the entry block of the region. The types of the entry block arguments are
|
|
|
|
|
|
often tied semantically to the operation, e.g., `func::FuncOp`, `AffineForOp`,
|
|
|
|
|
|
etc.
|
2024-06-10 21:49:52 +02:00
|
|
|
|
|
|
|
|
|
|
To convert the signature of just one given block, the
|
|
|
|
|
|
`applySignatureConversion` hook can be used.
|
|
|
|
|
|
|
|
|
|
|
|
A signature conversion, `TypeConverter::SignatureConversion`, can be built
|
|
|
|
|
|
programmatically:
|
2019-08-19 17:01:13 -07:00
|
|
|
|
|
|
|
|
|
|
```c++
|
|
|
|
|
|
class SignatureConversion {
|
|
|
|
|
|
public:
|
|
|
|
|
|
/// Remap an input of the original signature with a new set of types. The
|
|
|
|
|
|
/// new types are appended to the new signature conversion.
|
|
|
|
|
|
void addInputs(unsigned origInputNo, ArrayRef<Type> types);
|
|
|
|
|
|
|
|
|
|
|
|
/// Append new input types to the signature conversion, this should only be
|
|
|
|
|
|
/// used if the new types are not intended to remap an existing input.
|
|
|
|
|
|
void addInputs(ArrayRef<Type> types);
|
|
|
|
|
|
|
|
|
|
|
|
/// Remap an input of the original signature with a range of types in the
|
|
|
|
|
|
/// new signature.
|
|
|
|
|
|
void remapInput(unsigned origInputNo, unsigned newInputNo,
|
|
|
|
|
|
unsigned newInputCount = 1);
|
2019-10-16 10:20:31 -07:00
|
|
|
|
|
|
|
|
|
|
/// Remap an input of the original signature to another `replacement`
|
|
|
|
|
|
/// value. This drops the original argument.
|
2019-12-23 14:45:01 -08:00
|
|
|
|
void remapInput(unsigned origInputNo, Value replacement);
|
2019-08-19 17:01:13 -07:00
|
|
|
|
};
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2020-06-18 15:45:43 -07:00
|
|
|
|
The `TypeConverter` provides several default utilities for signature conversion
|
|
|
|
|
|
and legality checking:
|
|
|
|
|
|
`convertSignatureArgs`/`convertBlockSignature`/`isLegal(Region *|Type)`.
|
2020-08-13 12:04:57 -07:00
|
|
|
|
|
|
|
|
|
|
## Debugging
|
|
|
|
|
|
|
|
|
|
|
|
To debug the execution of the dialect conversion framework,
|
|
|
|
|
|
`-debug-only=dialect-conversion` may be used. This command line flag activates
|
|
|
|
|
|
LLVM's debug logging infrastructure solely for the conversion framework. The
|
|
|
|
|
|
output is formatted as a tree structure, mirroring the structure of the
|
|
|
|
|
|
conversion process. This output contains all of the actions performed by the
|
|
|
|
|
|
rewriter, how generated operations get legalized, and why they fail.
|
|
|
|
|
|
|
|
|
|
|
|
Example output is shown below:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
//===-------------------------------------------===//
|
2022-02-26 14:49:54 -08:00
|
|
|
|
Legalizing operation : 'func.return'(0x608000002e20) {
|
|
|
|
|
|
"func.return"() : () -> ()
|
2020-08-13 12:04:57 -07:00
|
|
|
|
|
|
|
|
|
|
* Fold {
|
|
|
|
|
|
} -> FAILURE : unable to fold
|
|
|
|
|
|
|
2022-02-26 14:49:54 -08:00
|
|
|
|
* Pattern : 'func.return -> ()' {
|
2022-09-26 10:58:13 -04:00
|
|
|
|
** Insert : 'spirv.Return'(0x6070000453e0)
|
2022-02-26 14:49:54 -08:00
|
|
|
|
** Replace : 'func.return'(0x608000002e20)
|
2020-08-13 12:04:57 -07:00
|
|
|
|
|
|
|
|
|
|
//===-------------------------------------------===//
|
2022-09-26 10:58:13 -04:00
|
|
|
|
Legalizing operation : 'spirv.Return'(0x6070000453e0) {
|
|
|
|
|
|
"spirv.Return"() : () -> ()
|
2020-08-13 12:04:57 -07:00
|
|
|
|
|
|
|
|
|
|
} -> SUCCESS : operation marked legal by the target
|
|
|
|
|
|
//===-------------------------------------------===//
|
|
|
|
|
|
} -> SUCCESS : pattern applied successfully
|
|
|
|
|
|
} -> SUCCESS
|
|
|
|
|
|
//===-------------------------------------------===//
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2022-02-26 14:49:54 -08:00
|
|
|
|
This output is describing the legalization of an `func.return` operation. We
|
2020-08-13 12:04:57 -07:00
|
|
|
|
first try to legalize by folding the operation, but that is unsuccessful for
|
2022-02-26 14:49:54 -08:00
|
|
|
|
`func.return`. From there, a pattern is applied that replaces the `func.return`
|
2022-09-26 10:58:13 -04:00
|
|
|
|
with a `spirv.Return`. The newly generated `spirv.Return` is then processed for
|
2020-08-13 12:04:57 -07:00
|
|
|
|
legalization, but is found to already legal as per the target.
|