@@ -153,11 +153,11 @@ target.markOpRecursivelyLegal<MyOp>([](MyOp op) { ... });
153153
154154After the conversion target has been defined, a set of legalization patterns
155155must be provided to transform illegal operations into legal ones. The patterns
156- supplied here have the same structure and restrictions as those described in the
157- main [ Pattern] ( PatternRewriter.md ) documentation. The patterns provided do not
158- need to generate operations that are directly legal on the target. The framework
159- will automatically build a graph of conversions to convert non-legal operations
160- into a set of legal ones.
156+ supplied here have the same structure and similar restrictions as those
157+ described in the main [ Pattern] ( PatternRewriter.md ) documentation. The patterns
158+ provided do not need to generate operations that are directly legal on the
159+ target. The framework will automatically build a graph of conversions to convert
160+ non-legal operations into a set of legal ones.
161161
162162As an example, say you define a target that supports one operation: ` foo.add ` .
163163When providing the following patterns: [ ` bar.add ` -> ` baz.add ` , ` baz.add ` ->
@@ -171,23 +171,12 @@ means that you don’t have to define a direct legalization pattern for `bar.add
171171Along with the general ` RewritePattern ` classes, the conversion framework
172172provides a special type of rewrite pattern that can be used when a pattern
173173relies on interacting with constructs specific to the conversion process, the
174- ` ConversionPattern ` . For example, the conversion process does not necessarily
175- update operations in-place and instead creates a mapping of events such as
176- replacements and erasures, and only applies them when the entire conversion
177- process is successful. Certain classes of patterns rely on using the
178- updated/remapped operands of an operation, such as when the types of results
179- defined by an operation have changed. The general Rewrite Patterns can no longer
180- be used in these situations, as the types of the operands of the operation being
181- matched will not correspond with those expected by the user. This pattern
182- provides, as an additional argument to the ` matchAndRewrite ` method, the list
183- of operands that the operation should use after conversion. If an operand was
184- the result of a non-converted operation, for example if it was already legal,
185- the original operand is used. This means that the operands provided always have
186- a 1-1 non-null correspondence with the operands on the operation. The original
187- operands of the operation are still intact and may be inspected as normal.
188- These patterns also utilize a special ` PatternRewriter ` ,
189- ` ConversionPatternRewriter ` , that provides special hooks for use with the
190- conversion infrastructure.
174+ ` ConversionPattern ` .
175+
176+ #### Remapped Operands / Adaptor
177+ Conversion patterns have an additional ` operands ` / ` adaptor ` argument for the
178+ ` matchAndRewrite ` method. These operands correspond to the most recent
179+ replacement values of the respective operands of the matched operation.
191180
192181``` c++
193182struct MyConversionPattern : public ConversionPattern {
@@ -200,6 +189,128 @@ struct MyConversionPattern : public ConversionPattern {
200189};
201190```
202191
192+ Example:
193+
194+ ```mlir
195+ %0 = "test.foo"() : () -> i1 // matched by pattern A
196+ "test.bar"(%0) : (i1) -> () // matched by pattern B
197+ ```
198+
199+ Let's assume that the two patterns are applied back-to-back: first, pattern A
200+ replaces ` "test.foo" ` with ` "test.qux" ` , an op that has a different result
201+ type. The dialect conversion infrastructure has special support for such
202+ type-changing IR modifications.
203+
204+ ``` mlir
205+ %0 = "test.qux"() : () -> i2
206+ %r = builtin.unrealized_conversion_cast %0 : i2 to i1
207+ "test.bar"(%r) : (i1) -> ()
208+ ```
209+
210+ Simply swapping out the operand of ` "test.bar" ` during the ` replaceOp ` call
211+ would be unsafe, because that would change the type of operand and, therefore,
212+ potentially the semantics of the operation. Instead, the dialect conversion
213+ driver (conceptually) inserts a ` builtin.unrealized_conversion_cast ` op that
214+ connects the newly created ` "test.qux" ` op with the ` "test.bar" ` op, without
215+ changing the types of the latter one.
216+
217+ Now, the second pattern B is applied. The ` operands ` argument contains an SSA
218+ value with the most recent replacement type (` %0 ` with type ` i2 ` ), whereas
219+ querying the operand from the matched op still returns an SSA value with the
220+ original operand type ` i1 ` .
221+
222+ Note: If the conversion pattern is instantiated with a type converter, the
223+ ` operands ` argument contains SSA values whose types match the legalized operand
224+ types as per the type converter. See [ Type Safety] ( #type-safety ) for more
225+ details.
226+
227+ Note: The dialect conversion framework does not guarantee the presence of any
228+ particular value in the ` operands ` argument. The only thing that's guaranteed
229+ is the type of the ` operands ` SSA values. E.g., instead of the actual
230+ replacement values supplied to a ` replaceOp ` API call, ` operands ` may contain
231+ results of transitory ` builtin.unrealized_conversion_cast ` ops that were
232+ inserted by the conversion driver but typically fold away again throughout the
233+ conversion process.
234+
235+ #### Immediate vs. Delayed IR Modification
236+
237+ The dialect conversion driver can operate in two modes: (a) rollback mode
238+ (default ) and (b) no-rollback mode. This can be controlled by
239+ `ConversionConfig::allowPatternRollback`. When running in rollback mode, the
240+ driver is able backtrack and roll back already applied patterns when the
241+ current legalization path (sequence of pattern applications) gets stuck with
242+ unlegalizable operations.
243+
244+ When running in no-rollback mode, all IR modifications such as op replacement,
245+ op erasure, op insertion or in-place op modification are applied immediately.
246+
247+ When running in rollback mode, certain IR modifications are delayed to the end
248+ of the conversion process. For example, a ` ConversionPatternRewriter::eraseOp `
249+ API call does not immediately erase the op, but just marks it for erasure. The
250+ op will stay visible to patterns and IR traversals throughout the conversion
251+ process. As another example, ` replaceOp ` and ` replaceAllUsesWith ` does not
252+ immediately update users of the original SSA values. This step is also delayed
253+ to the end of the conversion process.
254+
255+ Delaying certain IR modifications has two benefits: (1) pattern rollback is
256+ simpler because fewer IR modifications must be rolled back, (2) pointers of
257+ erased operations / blocks are preserved upon rollback, and (3) patterns can
258+ still access/traverse the original IR to some degree. However, additional
259+ bookkeeping in the form of complex internal C++ data structures is required to
260+ support pattern rollback. Running in rollback mode has a significant toll on
261+ compilation time, is error-prone and makes debugging conversion passes more
262+ complicated. Therefore, programmers are encouraged to run in no-rollback mode
263+ when possible.
264+
265+ The following table gives an overview of which IR changes are applied in a
266+ delayed fasion in rollback mode.
267+
268+ | Type | Rollback Mode | No-rollback Mode |
269+ | ------------------------------------------------------- | ----------------- | ---------------- |
270+ | Op Insertion / Movement (` create ` /` insert ` ) | Immediate | Immediate |
271+ | Op Replacement (` replaceOp ` ) | Delayed | Immediate |
272+ | Op Erasure (` eraseOp ` ) | Delayed | Immediate |
273+ | Op Modification (` modifyOpInPlace ` ) | Immediate | Immediate |
274+ | Value Replacement (` replaceAllUsesWith ` ) | Delayed | Immediate |
275+ | Block Insertion (` createBlock ` ) | Immediate | Immediate |
276+ | Block Replacement | Not supported | Not supported |
277+ | Block Erasure | Partly delayed | Immediate |
278+ | Block Signature Conversion (` applySignatureConversion ` ) | Partially delayed | Immediate |
279+ | Region / Block Inlining (` inlineBlockBefore ` , etc.) | Partially delayed | Immediate |
280+
281+ Value replacement is delayed and has different semantics in rollback mode:
282+ Since the actual replacement is delayed to the end of the conversion process,
283+ additional uses of the replaced value can be created after the
284+ ` replaceAllUsesWith ` call. Those uses will also be replaced at the end of the
285+ conversion process.
286+
287+ Block replacement is not supported in either mode, because the rewriter
288+ infrastructure currently has no API for replacing blocks: there is no overload
289+ of ` replaceAllUsesWith ` that accepts ` Block * ` .
290+
291+ Block erasure is partly delayed in rollback mode: the block is detached from
292+ the IR graph, but not memory for the block is not released until the end of the
293+ conversion process. This mechanism ensures that block pointers do not change
294+ when a block erasure is rolled back.
295+
296+ Block signature conversion is a combination of block insertion, op insertion,
297+ value replacement and block erasure. In rollback mode, the first two steps are
298+ immediate, but the last two steps are delayed.
299+
300+ Region / block inlining is a combination of block / op insertion and
301+ (optionally) value replacement. In rollback mode, the insertion steps are
302+ immediate, but the replacement step is delayed.
303+
304+ Note: When running in rollback mode, the conversion driver inserts fewer
305+ transitory ` builtin.unrealized_conversion_cast ` ops. Such ops are needed less
306+ frequently because certain IR modifications are delayed, making it unnecessary
307+ to connect old (not yet rewritten) and new (already rewritten) IR in a
308+ type-safe way. This has a negative effect on the debugging experience: when
309+ dumping IR throughout the conversion process, users see a mixture of old and
310+ new IR, but the way they are connected is not always visibile in the IR. Some
311+ of that information is stored in internal C++ data structures that is not
312+ visibile during an IR dump.
313+
203314#### Type Safety
204315
205316The types of the remapped operands provided to a conversion pattern (through
0 commit comments