summaryrefslogtreecommitdiff
path: root/mlir/test/lib/Dialect/Test/TestPatterns.cpp
AgeCommit message (Collapse)Author
2025-11-05[mlir] Dialect Conversion: Add support for post-order legalization order ↵Matthias Springer
(#166292) By default, the dialect conversion driver processes operations in pre-order: the initial worklist is populated pre-order. (New/modified operations are immediately legalized recursively.) This commit adds a new API for selective post-order legalization. Patterns can request an operation / region legalization via `ConversionPatternRewriter::legalize`. They can call these helper functions on nested regions before rewriting the operation itself. Note: In rollback mode, a failed recursive legalization typically leads to a conversion failure. Since recursive legalization is performed by separate pattern applications, there is no way for the original pattern to recover from such a failure.
2025-11-03[mlir][Transforms] Dialect Conversion: Convert entry block only (#165180)Matthias Springer
When converting a function, convert only the entry block signature. The remaining block signatures should be converted by the respective branching ops. The `FuncToLLVM` / `ControlFlowToLLVM` patterns already use that design. ```c++ struct BranchOpLowering : public ConvertOpToLLVMPattern<cf::BranchOp> { LogicalResult matchAndRewrite(cf::BranchOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter) const override { // Convert successor block. SmallVector<Value> flattenedAdaptor = flattenValues(adaptor.getOperands()); FailureOr<Block *> convertedBlock = getConvertedBlock(rewriter, getTypeConverter(), op, op.getSuccessor(), TypeRange(ValueRange(flattenedAdaptor))); // ... } }; ``` This is consistent with the fact that operations from unreachable blocks are not put on the initial worklist. With this change, parent ops are no longer recursively legalized when inserting a block, simplifying the conversion driver a bit. Note for LLVM integration: If you are seeing failures, make sure to: - Drop `converter.isLegal(&op.getBody())` when checking the legality of a function op. Only the entry block signature / function type should be taken into account. - If you need to convert all reachable blocks and are using `cf` branching ops, add `populateCFStructuralTypeConversionsAndLegality`. - If you need to convert all reachable blocks and are using custom branching ops, implement and populate custom structural type conversion patterns, similar to `populateCFStructuralTypeConversionsAndLegality`.
2025-10-29[mlir][CF] Add structural type conversion patterns (#165629)Matthias Springer
Add structural type conversion patterns for CF dialect ops. These patterns are similar to the SCF structural type conversion patterns. This commit adds missing functionality and is in preparation of #165180, which changes the way blocks are converted. (Only entry blocks are converted.)
2025-10-22[mlir] Switch uses of deprecated .create methods to free function. NFC. ↵Jakub Kuderski
(#164635) See https://discourse.llvm.org/t/psa-opty-create-now-with-100-more-tab-complete/87339.
2025-10-09[mlir] Execute same operand name constraints before user constraints. (#162526)Chenguang Wang
For a pattern like this: Pat<(MyOp $x, $x), (...), [(MyCheck $x)]>; The old implementation generates: Pat<(MyOp $x0, $x1), (...), [(MyCheck $x0), ($x0 == $x1)]>; This is not very straightforward, because the $x name appears in the source pattern; it's attempting to assume equality check will be performed as part of the source pattern matching. This commit moves the equality checks before the other constraints, i.e.: Pat<(MyOp $x0, $x1), (...), [($x0 == $x1), (MyCheck $x0)]>;
2025-09-15[mlir] Add base class type aliases for rewrites/conversions. NFC. (#158433)Jakub Kuderski
This is to simplify writing rewrite/conversion patterns that usually start with: ```c++ struct MyPattern : public OpRewritePattern<MyOp> { using OpRewritePattern::OpRewritePattern; ``` and allow for: ```c++ struct MyPattern : public OpRewritePattern<MyOp> { using Base::Base; ``` similar to how we enable it for pass classes.
2025-09-06[mlir][Transforms] Add support for ↵Matthias Springer
`ConversionPatternRewriter::replaceAllUsesWith` (#155244) This commit generalizes `replaceUsesOfBlockArgument` to `replaceAllUsesWith`. In rollback mode, the same restrictions keep applying: a value cannot be replaced multiple times and a call to `replaceAllUsesWith` will replace all current and future uses of the `from` value. `replaceAllUsesWith` is now fully supported and its behavior is consistent with the remaining dialect conversion API. Before this commit, `replaceAllUsesWith` was immediately reflected in the IR when running in rollback mode. After this commit, `replaceAllUsesWith` changes are materialized in a delayed fashion, at the end of the dialect conversion. This is consistent with the `replaceUsesOfBlockArgument` and `replaceOp` APIs. `replaceAllUsesExcept` etc. are still not supported and will be deactivated on the `ConversionPatternRewriter` (when running in rollback mode) in a follow-up commit. Note for LLVM integration: Replace `replaceUsesOfBlockArgument` with `replaceAllUsesWith`. If you are seeing failures, you may have patterns that use `replaceAllUsesWith` incorrectly (e.g., being called multiple times on the same value) or bypass the rewriter API entirely. E.g., such failures were mitigated in Flang by switching to the walk-patterns driver (#156171). You can temporarily reactivate the old behavior by calling `RewriterBase::replaceAllUsesWith`. However, note that that behavior is faulty in a dialect conversion. E.g., the base `RewriterBase::replaceAllUsesWith` implementation does not see uses of the `from` value that have not materialized yet and will, therefore, not replace them.
2025-08-27[mlir][Transforms] Dialect conversion: Context-aware type conversions (#140434)Matthias Springer
This commit adds support for context-aware type conversions: type conversion rules that can return different types depending on the IR. There is no change for existing (context-unaware) type conversion rules: ```c++ // Example: Conversion any integer type to f32. converter.addConversion([](IntegerType t) { return Float32Type::get(t.getContext()); } ``` There is now an additional overload to register context-aware type conversion rules: ```c++ // Example: Type conversion rule for integers, depending on the context: // Get the defining op of `v`, read its "increment" attribute and return an // integer with a bitwidth that is increased by "increment". converter.addConversion([](Value v) -> std::optional<Type> { auto intType = dyn_cast<IntegerType>(v.getType()); if (!intType) return std::nullopt; Operation *op = v.getDefiningOp(); if (!op) return std::nullopt; auto incrementAttr = op->getAttrOfType<IntegerAttr>("increment"); if (!incrementAttr) return std::nullopt; return IntegerType::get(v.getContext(), intType.getWidth() + incrementAttr.getInt()); }); ``` For performance reasons, the type converter caches the result of type conversions. This is no longer possible when there context-aware type conversions because each conversion could compute a different type depending on the context. There is no performance degradation when there are only context-unaware type conversions. Note: This commit just adds context-aware type conversions to the dialect conversion framework. There are many existing patterns that still call `converter.convertType(someValue.getType())`. These should be gradually updated in subsequent commits to call `converter.convertType(someValue)`. Co-authored-by: Markus Böck <markus.boeck02@gmail.com>
2025-08-18[mlir][Transforms] Dialect conversion: Add flag to dump materialization kind ↵Matthias Springer
(#119532) Add a debugging flag to the dialect conversion to dump the materialization kind. This flag is useful to find out whether a missing materialization rule is for source or target materializations. Also add missing test coverage for the `buildMaterializations` flag.
2025-08-15[mlir][Transforms] Turn 1:N -> 1:1 dispatch fatal error into match failure ↵Markus Böck
(#153605) Prior to this PR, the default behaviour of a conversion pattern which receives operands of a 1:N is to abort the compilation. This has historically been useful when the 1:N type conversion got merged into the dialect conversion as it allowed us to easily find patterns that should be capable of handling 1:N type conversions but didn't. However, this behaviour has the disadvantage of being non-composable: While the pattern in question cannot handle the 1:N type conversion, another pattern part of the set might, but doesn't get the chance as compilation is aborted. This PR fixes this behaviour by failing to match and instead of aborting, giving other patterns the chance to legalize an op. The implementation uses a reusable function called `dispatchTo1To1` to allow derived conversion patterns to also implement the behaviour.
2025-08-13[mlir][Transforms] Dialect Conversion Driver without Rollback (#151865)Matthias Springer
This commit improves the `allowPatternRollback` flag handling in the dialect conversion driver. Previously, this flag was used to merely detect cases that are incompatible with the new One-Shot Dialect Conversion driver. This commit implements the driver itself: when the flag is set to "false", all IR changes are materialized immediately, bypassing the `IRRewrite` and `ConversionValueMapping` infrastructure. A few selected test cases now run with both the old and the new driver. RFC: https://discourse.llvm.org/t/rfc-a-new-one-shot-dialect-conversion-driver/79083
2025-08-10[mlir] Enable disabling folding in dialect conversion (#152890)Jacques Pienaar
Previously this only happened post checking if the op is legal, but was done unconditionally post (and before other legalization patterns). Add option to not attempt folding and one to do so as last resort. Did consider but did not add a always attempt to fold option (which would have folded whether or not legal), but removed TODO about it.
2025-08-07[MLIR] Allow `constFoldBinaryOp` to fold `(T1, T1) -> T2` (#151410)Matthias Guenther
The `constFoldBinaryOp` helper function had limited support for different input and output types, but the static type of the underlying value (e.g. `APInt`) had to match between the inputs and the output. This worked fine for int comparisons of the form `(intN, intN) -> int1`, as the static type signature was `(APInt, APInt) -> APInt`. However, float comparisons map `(floatN, floatN) -> int1`, with a static type signature of `(APFloat, APFloat) -> APInt`. This use case wasn't supported by `constFoldBinaryOp`. `constFoldBinaryOp` now accepts an optional template argument overriding the return type in case it differs from the input type. If the new template argument isn't provided, the default behavior is unchanged (i.e. the return type will be assumed to match the input type). `constFoldUnaryOp` received similar changes in order to support folding non-cast ops of the form `(T1) -> T2` (e.g. a `sign` op mapping `(floatN) -> sint32`).
2025-08-07[mlir][Transforms] Make lookup without type converter unambiguous (#151747)Matthias Springer
When a conversion pattern is initialized without a type converter, the driver implementation currently looks up the most recently mapped value. This is undesirable because the most recently mapped value could be a materialization. I.e., the type of the value being looked up could depend on which other patterns have run before. Such an implementation makes the type conversion infrastructure fragile and unpredictable. The current implementation also contradicts the documentation in the markdown file. According to that documentation, the values provided by the adaptor should match the types of the operands of the match operation when running without a type converter. This mechanism is not desirable, either, for two reasons: 1. Some patterns have started to rely on receiving the most recently mapped value. Changing the behavior to the documented behavior will cause regressions. (And there would be no easy way to fix those without forcing the use of a type converter or extending the `getRemappedValue` API.) 2. It is more useful to receive the most recently mapped value. A value of the original operand type can be retrieved by using the operand of the matched operation. The adaptor is not needed at all in that case. To implement the new behavior, materializations are now annotated with a marker attribute. The marker is needed because not all `unrealized_conversion_cast` ops are materializations that act as "pure type conversions". E.g., when erasing an operation, its results are mapped to newly-created "out-of-thin-air values", which are materializations (with no input) that should be treated like regular replacement values during a lookup. This marker-based lookup strategy is also compatible with the One-Shot Dialect Conversion implementation strategy, which does not utilize the mapping infrastructure anymore and queries all necessary information by examining the IR.
2025-07-26[mlir][test] Turn `test-legalize-mode` into a pass option (#150767)Matthias Springer
The `test-legalize-mode` option is used only by the `test-legalize-patterns` pass.
2025-07-25[mlir][NFC] update `mlir` create APIs (34/n) (#150660)Maksim Levental
See https://github.com/llvm/llvm-project/pull/147168 for more info.
2025-07-25[mlir][NFC] update `mlir/Dialect` create APIs (28/n) (#150641)Maksim Levental
See https://github.com/llvm/llvm-project/pull/147168 for more info.
2025-07-25[mlir][NFC] Use `getDefiningOp<OpTy>()` instead of ↵Longsheng Mou
`dyn_cast<OpTy>(getDefiningOp())` (#150428) This PR uses `val.getDefiningOp<OpTy>()` to replace `dyn_cast<OpTy>(val.getDefiningOp())` , `dyn_cast_or_null<OpTy>(val.getDefiningOp())` and `dyn_cast_if_present<OpTy>(val.getDefiningOp())`.
2025-07-24[mlir][NFC] Use `hasOneBlock` instead of `llvm::hasSingleElement(region)` ↵Longsheng Mou
(#149809)
2025-06-25[mlir] Migrate away from std::nullopt (NFC) (#145523)Kazu Hirata
ArrayRef has a constructor that accepts std::nullopt. This constructor dates back to the days when we still had llvm::Optional. Since the use of std::nullopt outside the context of std::optional is kind of abuse and not intuitive to new comers, I would like to move away from the constructor and eventually remove it. This patch migrates away from std::nullopt in favor of ArrayRef<T>() where we use perfect forwarding. Note that {} would be ambiguous for perfect forwarding to work.
2025-06-23[mlir][Transforms] Add 1:N support to `replaceUsesOfBlockArgument` (#145171)Matthias Springer
This commit adds 1:N support to `ConversionPatternRewriter::replaceUsesOfBlockArgument`. This was one of the few remaining dialect conversion APIs that does not support 1:N conversions yet. This commit also reuses `replaceUsesOfBlockArgument` in the implementation of `applySignatureConversion`. This is in preparation of the One-Shot Dialect Conversion refactoring. The goal is to bring the `applySignatureConversion` implementation into a state where it works both with and without rollbacks. To that end, `applySignatureConversion` should not directly access the `mapping`.
2025-06-22[mlir] Migrate away from TypeRange(std::nullopt) (NFC) (#145246)Kazu Hirata
ArrayRef has a constructor that accepts std::nullopt. This constructor dates back to the days when we still had llvm::Optional. Since the use of std::nullopt outside the context of std::optional is kind of abuse and not intuitive to new comers, I would like to move away from the constructor and eventually remove it. One of the uses of std::nullopt is in a the constructors for TypeRange. This patch takes care of the migration where we need TypeRange() to facilitate perfect forwarding. Note that {} would be ambiguous for perfecting forwarding to work.
2025-04-24[mlir] add a fluent API to GreedyRewriterConfig (#137122)Oleksandr "Alex" Zinenko
This is similar to other configuration objects used across MLIR. Rename some fields to better reflect that they are no longer booleans. Reland 04d261101b4f229189463136a794e3e362a793af / #132253.
2025-04-18Revert "[mlir] add a fluent API to GreedyRewriterConfig (#132253)"Kazu Hirata
This reverts commit 63b8f1c9482ed0a964980df4aed89bef922b8078. Buildbot failure: https://lab.llvm.org/buildbot/#/builders/172/builds/12083/steps/5/logs/stdio I've reproduced the error with a release build (-DCMAKE_BUILD_TYPE=Release).
2025-04-18[mlir] add a fluent API to GreedyRewriterConfig (#132253)Oleksandr "Alex" Zinenko
This is similar to other configuration objects used across MLIR.
2025-03-31[MLIR][NFC] Fix incomplete boundary comments. (#133516)Han-Chung Wang
I observed that we have the boundary comments in the codebase like: ``` //===----------------------------------------------------------------------===// // ... //===----------------------------------------------------------------------===// ``` I also observed that there are incomplete boundary comments. The revision is generated by a script that completes the boundary comments. ``` //===----------------------------------------------------------------------===// // ... ... ``` Signed-off-by: hanhanW <hanhan0912@gmail.com>
2025-03-28[mlir][Transforms] Improve `replaceOpWithMultiple` API (#132608)Matthias Springer
This commit adds an additional overload to `replaceOpWithMultiple` that accepts additional container types. This has been brought up by users of the new `replaceOpWithMultiple` API. In particular, one missing container type was `SmallVector<SmallVector<Value>>`. The "default" `ArrayRef<ValueRange>` container type can lead to use-after-scope errors in cases such as: ```c++ // Compute the replacement value ranges. Some replacements are single // values, some are value ranges. SmallVector<ValueRange> repl; repl.push_back(someValueRange); // OK for (...) { // push_back(Value) triggers an implicit conversion to ValueRange, // which does not own the range. repl.push_back(someValue); // triggers use-after-scope later } rewriter.replaceOpWithMultiple(op, repl); ``` In this example, users should use `SmallVector<SmallVector<Value>> repl;`.
2025-03-15[mlir][Transforms] Dialect Conversion: Add 1:N support to `remapInput` (#131454)Matthias Springer
This commit adds 1:N support to `SignatureConversion::remapInputs`. This API allows users to replace a block argument with multiple replacement values. (And the block argument is dropped.) The API already supported "bbarg --> multiple bbargs" mappings, but "bbarg --> multiple SSA values" was missing. --------- Co-authored-by: Markus Böck <markus.boeck02@gmail.com>
2025-01-16[mlir][IR] Remove factory methods from `FloatType` (#123026)Matthias Springer
This commit removes convenience methods from `FloatType` to make it independent of concrete interface implementations. See discussion here: https://discourse.llvm.org/t/rethink-on-approach-to-low-precision-fp-types/82361 Note for LLVM integration: Replace `FloatType::getF32(` with `Float32Type::get(` etc.
2025-01-03[mlir][Transforms] Support 1:N mappings in `ConversionValueMapping` (#116524)Matthias Springer
This commit updates the internal `ConversionValueMapping` data structure in the dialect conversion driver to support 1:N replacements. This is the last major commit for adding 1:N support to the dialect conversion driver. Since #116470, the infrastructure already supports 1:N replacements. But the `ConversionValueMapping` still stored 1:1 value mappings. To that end, the driver inserted temporary argument materializations (converting N SSA values into 1 value). This is no longer the case. Argument materializations are now entirely gone. (They will be deleted from the type converter after some time, when we delete the old 1:N dialect conversion driver.) Note for LLVM integration: Replace all occurrences of `addArgumentMaterialization` (except for 1:N dialect conversion passes) with `addSourceMaterialization`. --------- Co-authored-by: Markus Böck <markus.boeck02@gmail.com>
2024-12-29[mlir][Transforms] Dialect Conversion: Add 1:N op replacement test case ↵Matthias Springer
(#121271) This commit adds a test case that performs two back-to-back 1:N replacements: `(i16) -> (i16, i16) -> ((i16, i16), (i16, i16))`. For the moment, 3 argument materializations are inserted. In the future (when the conversion value mapping supports 1:N), a single target materialization will be inserted. Addresses a [comment](https://github.com/llvm/llvm-project/pull/116524#discussion_r1894629711) in #116524.
2024-12-23[mlir][Transforms] Dialect Conversion: No target mat. for 1:N replacement ↵Matthias Springer
(#117513) During a 1:N replacement (`applySignatureConversion` or `replaceOpWithMultiple`), the dialect conversion driver used to insert two materializations: * Argument materialization: convert N replacement values to 1 SSA value of the original type `S`. * Target materialization: convert original type to legalized type `T`. The target materialization is unnecessary. Subsequent patterns receive the replacement values via their adaptors. These patterns have their own type converter. When they see a replacement value of type `S`, they will automatically insert a target materialization to type `T`. There is no reason to do this already during the 1:N replacement. (The functionality used to be duplicated in `remapValues` and `insertNTo1Materialization`.) Special case: If a subsequent pattern does not have a type converter, it does *not* insert any target materializations. That's because the absence of a type converter indicates that the pattern does not care about type legality. Therefore, it is correct to pass an SSA value of type `S` (or any other type) to the pattern. Note: Most patterns in `TestPatterns.cpp` run without a type converter. To make sure that the tests still behave the same, some of these patterns now have a type converter. This commit is in preparation of adding 1:N support to the conversion value mapping. Before making any further changes to the mapping infrastructure, I'd like to make sure that the code base around it (that uses the mapping) is robust.
2024-12-20[mlir] Enable decoupling two kinds of greedy behavior. (#104649)Jacques Pienaar
The greedy rewriter is used in many different flows and it has a lot of convenience (work list management, debugging actions, tracing, etc). But it combines two kinds of greedy behavior 1) how ops are matched, 2) folding wherever it can. These are independent forms of greedy and leads to inefficiency. E.g., cases where one need to create different phases in lowering and is required to applying patterns in specific order split across different passes. Using the driver one ends up needlessly retrying folding/having multiple rounds of folding attempts, where one final run would have sufficed. Of course folks can locally avoid this behavior by just building their own, but this is also a common requested feature that folks keep on working around locally in suboptimal ways. For downstream users, there should be no behavioral change. Updating from the deprecated should just be a find and replace (e.g., `find ./ -type f -exec sed -i 's|applyPatternsAndFoldGreedily|applyPatternsGreedily|g' {} \;` variety) as the API arguments hasn't changed between the two.
2024-11-30[mlir][Transforms] Add 1:N `matchAndRewrite` overload (#116470)Matthias Springer
This commit adds a new `matchAndRewrite` overload to `ConversionPattern` to support 1:N replacements. This is the first of two main PRs that merge the 1:1 and 1:N dialect conversion drivers. The existing `matchAndRewrite` function supports only 1:1 replacements, as can be seen from the `ArrayRef<Value>` parameter. ```c++ LogicalResult ConversionPattern::matchAndRewrite( Operation *op, ArrayRef<Value> operands /*adaptor values*/, ConversionPatternRewriter &rewriter) const; ``` This commit adds a `matchAndRewrite` overload that is called by the dialect conversion driver. By default, this new overload dispatches to the original 1:1 `matchAndRewrite` implementation. Existing `ConversionPattern`s do not need to be changed as long as there are no 1:N type conversions or value replacements. ```c++ LogicalResult ConversionPattern::matchAndRewrite( Operation *op, ArrayRef<ValueRange> operands /*adaptor values*/, ConversionPatternRewriter &rewriter) const { // Note: getOneToOneAdaptorOperands produces a fatal error if at least one // ValueRange has 0 or more than 1 value. return matchAndRewrite(op, getOneToOneAdaptorOperands(operands), rewriter); } ``` The `ConversionValueMapping`, which keeps track of value replacements and materializations, still does not support 1:N replacements. We still rely on argument materializations to convert N replacement values back into a single value. The `ConversionValueMapping` will be generalized to 1:N mappings in the second main PR. Before handing the adaptor values to a `ConversionPattern`, all argument materializations are "unpacked". The `ConversionPattern` receives N replacement values and does not see any argument materializations. This implementation strategy allows us to use the 1:N infrastructure/API in `ConversionPattern`s even though some functionality is still missing in the driver. This strategy was chosen to keep the sizes of the PRs smaller and to make it easier for downstream users to adapt to API changes. This commit also updates the the "decompose call graphs" transformation and the "sparse tensor codegen" transformation to use the new 1:N `ConversionPattern` API. Note for LLVM conversion: If you are using a type converter with 1:N type conversion rules or if your patterns are performing 1:N replacements (via `replaceOpWithMultiple` or `applySignatureConversion`), conversion pattern applications will start failing (fatal LLVM error) with this error message: `pattern 'name' does not support 1:N conversion`. The name of the failing pattern is shown in the error message. These patterns must be updated to the new 1:N `matchAndRewrite` API.
2024-11-23[mlir][Func] Support 1:N result type conversions in `func.call` conversion ↵Matthias Springer
(#117413) This commit adds support for 1:N result type conversions for `func.call` ops. In that case, argument materializations to the original result type should be inserted (via `replaceOpWithMultiple`). This commit is in preparation of merging the 1:1 and 1:N conversion drivers.
2024-10-31[mlir] Remove debug prints from test patternJakub Kuderski
2024-10-31[mlir] Add fast walk-based pattern rewrite driver (#113825)Jakub Kuderski
This is intended as a fast pattern rewrite driver for the cases when a simple walk gets the job done but we would still want to implement it in terms of rewrite patterns (that can be used with the greedy pattern rewrite driver downstream). The new driver is inspired by the discussion in https://github.com/llvm/llvm-project/pull/112454 and the LLVM Dev presentation from @matthias-springer earlier this week. This limitation comes with some limitations: * It does not repeat until a fixpoint or revisit ops modified in place or newly created ops. In general, it only walks forward (in the post-order). * `matchAndRewrite` can only erase the matched op or its descendants. This is verified under expensive checks. * It does not perform folding / DCE. We could probably relax some of these in the future without sacrificing too much performance.
2024-10-23[mlir][Transforms] Dialect Conversion: Simplify materialization fn result ↵Matthias Springer
type (#113031) This commit simplifies the result type of materialization functions. Previously: `std::optional<Value>` Now: `Value` The previous implementation allowed 3 possible return values: - Non-null value: The materialization function produced a valid materialization. - `std::nullopt`: The materialization function failed, but another materialization can be attempted. - `Value()`: The materialization failed and so should the dialect conversion. (Previously: Dialect conversion can roll back.) This commit removes the last variant. It is not particularly useful because the dialect conversion will fail anyway if all other materialization functions produced `std::nullopt`. Furthermore, in contrast to type conversions, at least one materialization callback is expected to succeed. In case of a failing type conversion, the current dialect conversion can roll back and try a different pattern. This also used to be the case for materializations, but that functionality was removed with #107109: failed materializations can no longer trigger a rollback. (They can just make the entire dialect conversion fail without rollback.) With this in mind, it is even less useful to have an additional error state for materialization functions. This commit is in preparation of merging the 1:1 and 1:N type converters. Target materializations will have to return multiple values instead of a single one. With this commit, we can keep the API simple: `SmallVector<Value>` instead of `std::optional<SmallVector<Value>>`. Note for LLVM integration: All 1:1 materializations should return `Value` instead of `std::optional<Value>`. Instead of `std::nullopt` return `Value()`.
2024-09-05[mlir][Transforms] Dialect conversion: Make materializations optional (#107109)Matthias Springer
This commit makes source/target/argument materializations (via the `TypeConverter` API) optional. By default (`ConversionConfig::buildMaterializations = true`), the dialect conversion infrastructure tries to legalize all unresolved materializations right after the main transformation process has succeeded. If at least one unresolved materialization fails to resolve, the dialect conversion fails. (With an error message such as `failed to legalize unresolved materialization ...`.) Automatic materializations through the `TypeConverter` API can now be deactivated. In that case, every unresolved materialization will show up as a `builtin.unrealized_conversion_cast` op in the output IR. There used to be a complex and error-prone analysis in the dialect conversion that predicted the future uses of unresolved materializations. Based on that logic, some casts (that were deemed to unnecessary) were folded. This analysis was needed because folding happened at a point of time when some IR changes (e.g., op replacements) had not materialized yet. This commit removes that analysis. Any folding of cast ops now happens after all other IR changes have been materialized and the uses can directly be queried from the IR. This simplifies the analysis significantly. And certain helper data structures such as `inverseMapping` are no longer needed for the analysis. The folding itself is done by `reconcileUnrealizedCasts` (which also exists as a standalone pass). After casts have been folded, the remaining casts are materialized through the `TypeConverter`, as usual. This last step can be deactivated in the `ConversionConfig`. `ConversionConfig::buildMaterializations = false` can be used to debug error messages such as `failed to legalize unresolved materialization ...`. (It is also useful in case automatic materializations are not needed.) The materializations that failed to resolve can then be seen as `builtin.unrealized_conversion_cast` ops in the resulting IR. (This is better than running with `-debug`, because `-debug` shows IR where some IR changes have not been materialized yet.) Note: This is a reupload of #104668, but with correct handling of cyclic unrealized_conversion_casts that may be generated by the dialect conversion.
2024-07-30[mlir][Transforms] Dialect conversion: Add missing "else if" branch (#101148)Matthias Springer
This code got lost in #97213 and there was no test for it. Add it back with an MLIR test. When a pattern is run without a type converter, we can assume that the new block argument types of a signature conversion are legal. That's because they were specified by the user. This won't work for 1->N conversions due to limitations in the dialect conversion infrastructure, so the original `FIXME` has to stay in place.
2024-07-25[mlir][Transforms] Dialect conversion: fix crash when converting detached ↵Matthias Springer
region (#100633) This commit fixes a crash in the dialect conversion when applying a signature conversion to a block inside of a detached region. This fixes an issue reported in https://github.com/llvm/llvm-project/pull/97213/files/4114d5be87596e11d86706a338248ebf05cf7150#r1691809730.
2024-06-10[mlir][Transforms] Dialect Conversion: Simplify block conversion API (#94866)Matthias Springer
This commit simplifies and improves documentation for the part of the `ConversionPatternRewriter` API that deals with signature conversions. There are now two public functions for signature conversion: * `applySignatureConversion` converts a single block signature. This function used to take a `Region *` (but converted only the entry block). It now takes a `Block *`. * `convertRegionTypes` converts all block signatures of a region. `convertNonEntryRegionTypes` is removed because it is not widely used and can easily be expressed with a call to `applySignatureConversion` inside a loop. (See `Detensorize.cpp` for an example.) Note: For consistency, `convertRegionTypes` could be renamed to `applySignatureConversion` (overload) in the future. (Or `applySignatureConversion` renamed to `convertBlockTypes`.) Also clarify when a type converter and/or signature conversion object is needed and for what purpose. Internal code refactoring (NFC) of `ConversionPatternRewriterImpl` (the part that deals with signature conversions). This part of the codebase was quite convoluted and unintuitive. From a functional perspective, this change is NFC. However, the public API changes, thus not marking as NFC. Note for LLVM integration: When you see `applySignatureConversion(region, ...)`, replace with `applySignatureConversion(region->front(), ...)`. In the unlikely case that you see `convertNonEntryRegionTypes`, apply the same changes as this commit did to `Detensorize.cpp`. --------- Co-authored-by: Markus Böck <markus.boeck02@gmail.com>
2024-04-23[MLIR] Harmonize the behavior of the folding API functions (#88508)Christian Ulmann
This commit changes `OpBuilder::tryFold` to behave more similarly to `Operation::fold`. Concretely, this ensures that even an in-place fold returns `success`. This is necessary to fix a bug in the dialect conversion that occurred when an in-place folding made an operation legal. The dialect conversion infrastructure did not check if the result of an in-place folding legalized the operation and just went ahead and tried to apply pattern anyways. The added test contains a simplified version of a breakage we observed downstream.
2024-04-22[mlir][test] Reorganize the test dialect (#89424)Jeff Niu
This PR massively reorganizes the Test dialect's source files. It moves manually-written op hooks into `TestOpDefs.cpp`, moves format custom directive parsers and printers into `TestFormatUtils`, adds missing comment blocks, and moves around where generated source files are included for types, attributes, enums, etc. into their own source file. This will hopefully help navigate the test dialect source code, but also speeds up compile time of the test dialect by putting generated source files into separate compilation units. This also sets up the test dialect to shard its op definitions, done in the next PR.
2024-04-02[mlir][IR] Trigger `notifyOperationReplaced` on `replaceAllOpUsesWith` (#84721)Matthias Springer
Before this change: `notifyOperationReplaced` was triggered when calling `RewriteBase::replaceOp`. After this change: `notifyOperationReplaced` is triggered when `RewriterBase::replaceAllOpUsesWith` or `RewriterBase::replaceOp` is called. Until now, every `notifyOperationReplaced` was always sent together with a `notifyOperationErased`, which made that `notifyOperationErased` callback irrelevant. More importantly, when a user called `RewriterBase::replaceAllOpUsesWith`+`RewriterBase::eraseOp` instead of `RewriterBase::replaceOp`, no `notifyOperationReplaced` callback was sent, even though the two notations are semantically equivalent. As an example, this can be a problem when applying patterns with the transform dialect because the `TrackingListener` will only see the `notifyOperationErased` callback and the payload op is dropped from the mappings. Note: It is still possible to write semantically equivalent code that does not trigger a `notifyOperationReplaced` (e.g., when op results are replaced one-by-one), but this commit already improves the situation a lot.
2024-03-08[mlir][Transforms] Add listener support to dialect conversion (#83425)Matthias Springer
This commit adds listener support to the dialect conversion. Similarly to the greedy pattern rewrite driver, an optional listener can be specified in the configuration object. Listeners are notified only if the dialect conversion succeeds. In case of a failure, where some IR changes are first performed and then rolled back, no notifications are sent. Due to the fact that some kinds of rewrite are reflected in the IR immediately and some in a delayed fashion, there are certain limitations when attaching a listener; these are documented in `ConversionConfig`. To summarize, users are always notified about all rewrites that happened, but the notifications are sent all at once at the very end, and not interleaved with the actual IR changes. This change is in preparation improvements to `transform.apply_conversion_patterns`, which currently invalidates all handles. In the future, it can use a listener to update handles accordingly, similar to `transform.apply_patterns`.
2024-03-07[mlir][IR] Make `replaceOp` / `replaceAllUsesWith` API consistent (#82629)Matthias Springer
* `replaceOp` replaces all uses of the original op and erases the old op. * `replaceAllUsesWith` replaces all uses of the original op/value/block. It does not erase any IR. This commit renames `replaceOpWithIf` to `replaceUsesWithIf`. `replaceOpWithIf` was a misnomer because the function never erases the original op. Similarly, `replaceOpWithinBlock` is renamed to `replaceUsesWithinBlock`. (No "operation replaced" is sent because the op is not erased.) Also improve comments.
2024-03-04[mlir][Transforms] Encapsulate dialect conversion options in ↵Matthias Springer
`ConversionConfig` (#83754) This commit adds a new `ConversionConfig` struct that allows users to customize the dialect conversion. This configuration is similar to `GreedyRewriteConfig` for the greedy pattern rewrite driver. A few existing options are moved to this objects, simplifying the dialect conversion API. This is a re-upload of #82250. The Windows build breakage was fixed in #83768. This reverts commit 60fbd6050107875956960c3ce35cf94b202d8675.
2024-03-02Revert "[mlir][Transforms] Encapsulate dialect conversion options in ↵Mehdi Amini
`ConversionConfig` (#83662) This reverts commit 5f1319bb385342c7ef4124b05b83b89ef8588ee8. A FIR test is broken on Windows
2024-02-28[mlir][Transforms] Dialect conversion: Assert when accessing erased ops (#83132)Matthias Springer
The dialect conversion maintains sets of "ignored" and "replaced" ops. This change simplifies the two sets, such that all nested ops are included. (This was previously not the case and sometimes only the parent op was included.) This change allows for more aggressive assertions to prevent incorrect rewriter API usage. E.g., accessing ops/blocks/regions within an erased op. A concrete example: I have seen conversion patterns in downstream projects where an op is replaced with a new op, and the region of the old op is afterwards inlined into the newly created op. This is invalid rewriter API usage: ops that were replaced/erased should not be accessed. Nested ops will be considered "ignored", even if they are moved to a different region after the region's parent op was erased (which is illegal API usage). Instead, create a new op, inline the regions, then replace the old op with the new op.