diff options
author | River Riddle <riverriddle@google.com> | 2019-08-19 17:01:13 -0700 |
---|---|---|
committer | A. Unique TensorFlower <gardener@tensorflow.org> | 2019-08-19 17:01:46 -0700 |
commit | 774b37b89b88e063aa4df47bd2637cf51283740c (patch) | |
tree | bab5c5beb02156143ac56f2b6a77065c77493426 /mlir | |
parent | 2277b9fb5c235dccfbfb803fd81f3ffe3c09872a (diff) | |
download | bcm5719-llvm-774b37b89b88e063aa4df47bd2637cf51283740c.tar.gz bcm5719-llvm-774b37b89b88e063aa4df47bd2637cf51283740c.zip |
Add a DialectConversion document detailing the conversion infrastructure.
This is an important piece of the infrastructure that is missing proper high level documentation on usage.
PiperOrigin-RevId: 264275482
Diffstat (limited to 'mlir')
-rw-r--r-- | mlir/g3doc/DialectConversion.md | 229 | ||||
-rw-r--r-- | mlir/include/mlir/Transforms/DialectConversion.h | 4 |
2 files changed, 231 insertions, 2 deletions
diff --git a/mlir/g3doc/DialectConversion.md b/mlir/g3doc/DialectConversion.md new file mode 100644 index 00000000000..22137e48204 --- /dev/null +++ b/mlir/g3doc/DialectConversion.md @@ -0,0 +1,229 @@ +# 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. + +[TOC] + +To utilize the framework, a few things must be provided: + +* A [Conversion Target](#conversion-target) +* A set of [Rewrite Patterns](#rewrite-pattern-specification) +* A [Type Converter](#type-conversion) (Optional) + +## Modes of Conversion + +When applying a conversion to a set of operations, there are several conversion +modes that can be selected from: + +* Partial Conversion + + - A partial conversion will legalize as many operations to the target as + possible, but will allow pre-existing operations that were not + explicitly marked as `illegal` to remain unconverted. This allows for + partially lowering parts of the module in the presence of unknown + operations. + - A partial conversion can be applied via `applyPartialConversion`. + +* Full Conversion + + - A full conversion 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. + - A full conversion can be applied via `applyFullConversion`. + +* Analysis Conversion + + - An analysis conversion will analyze which operations are legalizable to + the given conversion target if a conversion were to be applied. Note + that no rewrites, or transformations, are actually applied to the input + operations. + - An analysis conversion can be applied via `applyAnalysisConversion`. + +## Conversion Target + +The conversion target is the formal definition of what is considered to be legal +during the conversion process. The final operations generated by the conversion +framework must be marked as legal on the `ConversionTarget` for the rewrite to +be a success. Existing operations need not always be legal though, see the +different conversion modes for why. Operations, and dialects, may be marked with +any of the provided legality actions below: + +* 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 + `addi` is only legal when operating on 32-bit integers. + - If a specific handler is not provided when setting the action, the + target must override the `isDynamicallyLegal` hook provided by + `ConversionTarget`. + +* Illegal + + - This action signals that no instance of a given operation is legal. + Operations marked as `illegal` must always be converted for the + conversion to be successful. This action also allows for selectively + marking specific operations, in an otherwise legal dialect, as illegal. + +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. + addLegalDialects<LLVMDialect>(); + + /// Mark `std.constant` op is always legal on this target. + addLegalOps<ConstantOp>(); + + //-------------------------------------------------------------------------- + // Marking an operation as dynamically legal. + + /// Mark all operations within Affine dialect have dynamic legality + /// constraints. + addDynamicallyLegalDialects<AffineDialect>(); + + /// Mark `std.return` as dynamically legal. + addDynamicallyLegalOp<ReturnOp>(); + + /// Mark `std.return` as dynamically legal, but provide a specific legality + /// callback. + addDynamicallyLegalOp<ReturnOp>([](ReturnOp op) { ... }); + + //-------------------------------------------------------------------------- + // Marking an operation as illegal. + + /// All operations within the GPU dialect are illegal. + addIllegalDialect<GPUDialect>(); + + /// Mark `std.br` and `std.cond_br` as illegal. + addIllegalOp<BranchOp, CondBranchOp>(); + } + + /// 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 { ... } +}; +``` + +## Rewrite Pattern Specification + +After the conversion target has been defined, a set of legalization patterns +must be provided to transform illegal operations into legal ones. The patterns +supplied here, that do not [require type changes](#conversion-patterns), are the +same as those described in the +[quickstart rewrites guide](QuickstartRewrites.md#adding-patterns). 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. + +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 +`baz.add` -> `foo.add` even though a direct conversion does not exist. This +means that you don’t have to define a direct legalization pattern for `bar.add` +-> `foo.add`. + +## Type Conversion + +It is sometimes necessary as part of a conversion to convert the set types of +being operated on. In these cases a `TypeConverter` object may be defined that +details how types should be converted. The `TypeConverter` is used by patterns, +and the general conversion infrastructure, to convert the signatures of blocks +and regions. + +### Type Converter + +As stated above, the `TypeConverter` contains several hooks for detailing how to +convert types, several of these hooks are detailed below: + +```c++ +class TypeConverter { + public: + /// This hook allows for converting a type. This function should return + /// failure if no valid conversion exists, success otherwise. If the new set + /// of types is empty, the type is removed and any usages of the existing + /// value are expected to be removed during conversion. + virtual LogicalResult convertType(Type t, SmallVectorImpl<Type> &results); + + /// This hook simplifies defining 1-1 type conversions. This function returns + /// the type to convert to on success, and a null type on failure. + virtual Type convertType(Type t); + + /// This hook allows for materializing a conversion from a set of types into + /// one result type by generating a cast operation of some kind. The generated + /// operation should produce one result, of 'resultType', with the provided + /// 'inputs' as operands. This hook must be overridden when a type conversion + /// results in more than one type, or if a type conversion may persist after + /// the conversion has finished. + virtual Operation *materializeConversion(PatternRewriter &rewriter, + Type resultType, + ArrayRef<Value *> inputs, + Location loc); +}; +``` + +### Conversion Patterns + +When type conversion comes into play, the general Rewrite Patterns can no longer +be used. This is due to the fact that the operands of the operation being +matched will not correspond with the operands of the correct type as determined +by `TypeConverter`. The operation rewrites on type boundaries must thus use a +special pattern, the `ConversionPattern`. This pattern provides, as an +additional argument to the `matchAndRewrite` and `rewrite` methods, the set of +remapped operands corresponding to the desired type. These patterns also utilize +a special `PatternRewriter`, `ConversionPatternRewriter`, that provides special +hooks for use with the conversion infrastructure. + +```c++ +struct MyConversionPattern : public ConversionPattern { + /// The `matchAndRewrite` hooks on ConversionPatterns take an additional + /// `operands` parameter, containing the remapped operands of the original + /// operation. + virtual PatternMatchResult + matchAndRewrite(Operation *op, ArrayRef<Value *> operands, + ConversionPatternRewriter &rewriter) const; +}; +``` + +### Region Signature Conversion + +From the perspective of type conversion, the entry block to a region is often +special. The types of the entry block arguments are often tied semantically to +details on the operation, e.g. FuncOp, AffineForOp, etc. Given this, the +conversion of the types for this block must be done explicitly via a conversion +pattern. To convert the signature of a region entry block, a custom hook on the +ConversionPatternRewriter must be invoked `applySignatureConversion`. A +signature conversion, `TypeConverter::SignatureConversion`, can be built +programmatically: + +```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); +}; +``` + +The `TypeConverter` provides several default utilities for signature conversion: +`convertSignatureArg`/`convertBlockSignature`. diff --git a/mlir/include/mlir/Transforms/DialectConversion.h b/mlir/include/mlir/Transforms/DialectConversion.h index bd7d021ddcd..3cdf14c3eb3 100644 --- a/mlir/include/mlir/Transforms/DialectConversion.h +++ b/mlir/include/mlir/Transforms/DialectConversion.h @@ -94,14 +94,14 @@ public: SmallVector<Type, 4> argTypes; }; - /// This hooks allows for converting a type. This function should return + /// This hook allows for converting a type. This function should return /// failure if no valid conversion exists, success otherwise. If the new set /// of types is empty, the type is removed and any usages of the existing /// value are expected to be removed during conversion. virtual LogicalResult convertType(Type t, SmallVectorImpl<Type> &results); /// This hook simplifies defining 1-1 type conversions. This function returns - /// the type convert to on success, and a null type on failure. + /// the type to convert to on success, and a null type on failure. virtual Type convertType(Type t) { return t; } /// Convert the given set of types, filling 'results' as necessary. This |