1 //===- TestPatterns.cpp - Test dialect pattern driver ---------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "TestDialect.h"
10 #include "mlir/Conversion/StandardToStandard/StandardToStandard.h"
11 #include "mlir/IR/PatternMatch.h"
12 #include "mlir/Pass/Pass.h"
13 #include "mlir/Transforms/DialectConversion.h"
14 using namespace mlir;
15 
16 // Native function for testing NativeCodeCall
17 static Value chooseOperand(Value input1, Value input2, BoolAttr choice) {
18   return choice.getValue() ? input1 : input2;
19 }
20 
21 static void createOpI(PatternRewriter &rewriter, Value input) {
22   rewriter.create<OpI>(rewriter.getUnknownLoc(), input);
23 }
24 
25 static void handleNoResultOp(PatternRewriter &rewriter,
26                              OpSymbolBindingNoResult op) {
27   // Turn the no result op to a one-result op.
28   rewriter.create<OpSymbolBindingB>(op.getLoc(), op.operand().getType(),
29                                     op.operand());
30 }
31 
32 namespace {
33 #include "TestPatterns.inc"
34 } // end anonymous namespace
35 
36 //===----------------------------------------------------------------------===//
37 // Canonicalizer Driver.
38 //===----------------------------------------------------------------------===//
39 
40 namespace {
41 struct TestPatternDriver : public PassWrapper<TestPatternDriver, FunctionPass> {
42   void runOnFunction() override {
43     mlir::OwningRewritePatternList patterns;
44     populateWithGenerated(&getContext(), &patterns);
45 
46     // Verify named pattern is generated with expected name.
47     patterns.insert<TestNamedPatternRule>(&getContext());
48 
49     applyPatternsGreedily(getFunction(), patterns);
50   }
51 };
52 } // end anonymous namespace
53 
54 //===----------------------------------------------------------------------===//
55 // ReturnType Driver.
56 //===----------------------------------------------------------------------===//
57 
58 namespace {
59 // Generate ops for each instance where the type can be successfully inferred.
60 template <typename OpTy>
61 static void invokeCreateWithInferredReturnType(Operation *op) {
62   auto *context = op->getContext();
63   auto fop = op->getParentOfType<FuncOp>();
64   auto location = UnknownLoc::get(context);
65   OpBuilder b(op);
66   b.setInsertionPointAfter(op);
67 
68   // Use permutations of 2 args as operands.
69   assert(fop.getNumArguments() >= 2);
70   for (int i = 0, e = fop.getNumArguments(); i < e; ++i) {
71     for (int j = 0; j < e; ++j) {
72       std::array<Value, 2> values = {{fop.getArgument(i), fop.getArgument(j)}};
73       SmallVector<Type, 2> inferredReturnTypes;
74       if (succeeded(OpTy::inferReturnTypes(context, llvm::None, values,
75                                            op->getAttrs(), op->getRegions(),
76                                            inferredReturnTypes))) {
77         OperationState state(location, OpTy::getOperationName());
78         // TODO(jpienaar): Expand to regions.
79         OpTy::build(&b, state, values, op->getAttrs());
80         (void)b.createOperation(state);
81       }
82     }
83   }
84 }
85 
86 static void reifyReturnShape(Operation *op) {
87   OpBuilder b(op);
88 
89   // Use permutations of 2 args as operands.
90   auto shapedOp = cast<OpWithShapedTypeInferTypeInterfaceOp>(op);
91   SmallVector<Value, 2> shapes;
92   if (failed(shapedOp.reifyReturnTypeShapes(b, shapes)))
93     return;
94   for (auto it : llvm::enumerate(shapes))
95     op->emitRemark() << "value " << it.index() << ": "
96                      << it.value().getDefiningOp();
97 }
98 
99 struct TestReturnTypeDriver
100     : public PassWrapper<TestReturnTypeDriver, FunctionPass> {
101   void runOnFunction() override {
102     if (getFunction().getName() == "testCreateFunctions") {
103       std::vector<Operation *> ops;
104       // Collect ops to avoid triggering on inserted ops.
105       for (auto &op : getFunction().getBody().front())
106         ops.push_back(&op);
107       // Generate test patterns for each, but skip terminator.
108       for (auto *op : llvm::makeArrayRef(ops).drop_back()) {
109         // Test create method of each of the Op classes below. The resultant
110         // output would be in reverse order underneath `op` from which
111         // the attributes and regions are used.
112         invokeCreateWithInferredReturnType<OpWithInferTypeInterfaceOp>(op);
113         invokeCreateWithInferredReturnType<
114             OpWithShapedTypeInferTypeInterfaceOp>(op);
115       };
116       return;
117     }
118     if (getFunction().getName() == "testReifyFunctions") {
119       std::vector<Operation *> ops;
120       // Collect ops to avoid triggering on inserted ops.
121       for (auto &op : getFunction().getBody().front())
122         if (isa<OpWithShapedTypeInferTypeInterfaceOp>(op))
123           ops.push_back(&op);
124       // Generate test patterns for each, but skip terminator.
125       for (auto *op : ops)
126         reifyReturnShape(op);
127     }
128   }
129 };
130 } // end anonymous namespace
131 
132 //===----------------------------------------------------------------------===//
133 // Legalization Driver.
134 //===----------------------------------------------------------------------===//
135 
136 namespace {
137 //===----------------------------------------------------------------------===//
138 // Region-Block Rewrite Testing
139 
140 /// This pattern is a simple pattern that inlines the first region of a given
141 /// operation into the parent region.
142 struct TestRegionRewriteBlockMovement : public ConversionPattern {
143   TestRegionRewriteBlockMovement(MLIRContext *ctx)
144       : ConversionPattern("test.region", 1, ctx) {}
145 
146   LogicalResult
147   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
148                   ConversionPatternRewriter &rewriter) const final {
149     // Inline this region into the parent region.
150     auto &parentRegion = *op->getParentRegion();
151     if (op->getAttr("legalizer.should_clone"))
152       rewriter.cloneRegionBefore(op->getRegion(0), parentRegion,
153                                  parentRegion.end());
154     else
155       rewriter.inlineRegionBefore(op->getRegion(0), parentRegion,
156                                   parentRegion.end());
157 
158     // Drop this operation.
159     rewriter.eraseOp(op);
160     return success();
161   }
162 };
163 /// This pattern is a simple pattern that generates a region containing an
164 /// illegal operation.
165 struct TestRegionRewriteUndo : public RewritePattern {
166   TestRegionRewriteUndo(MLIRContext *ctx)
167       : RewritePattern("test.region_builder", 1, ctx) {}
168 
169   LogicalResult matchAndRewrite(Operation *op,
170                                 PatternRewriter &rewriter) const final {
171     // Create the region operation with an entry block containing arguments.
172     OperationState newRegion(op->getLoc(), "test.region");
173     newRegion.addRegion();
174     auto *regionOp = rewriter.createOperation(newRegion);
175     auto *entryBlock = rewriter.createBlock(&regionOp->getRegion(0));
176     entryBlock->addArgument(rewriter.getIntegerType(64));
177 
178     // Add an explicitly illegal operation to ensure the conversion fails.
179     rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getIntegerType(32));
180     rewriter.create<TestValidOp>(op->getLoc(), ArrayRef<Value>());
181 
182     // Drop this operation.
183     rewriter.eraseOp(op);
184     return success();
185   }
186 };
187 /// A simple pattern that creates a block at the end of the parent region of the
188 /// matched operation.
189 struct TestCreateBlock : public RewritePattern {
190   TestCreateBlock(MLIRContext *ctx)
191       : RewritePattern("test.create_block", /*benefit=*/1, ctx) {}
192 
193   LogicalResult matchAndRewrite(Operation *op,
194                                 PatternRewriter &rewriter) const final {
195     Region &region = *op->getParentRegion();
196     Type i32Type = rewriter.getIntegerType(32);
197     rewriter.createBlock(&region, region.end(), {i32Type, i32Type});
198     rewriter.create<TerminatorOp>(op->getLoc());
199     rewriter.replaceOp(op, {});
200     return success();
201   }
202 };
203 
204 /// A simple pattern that creates a block containing an invalid operaiton in
205 /// order to trigger the block creation undo mechanism.
206 struct TestCreateIllegalBlock : public RewritePattern {
207   TestCreateIllegalBlock(MLIRContext *ctx)
208       : RewritePattern("test.create_illegal_block", /*benefit=*/1, ctx) {}
209 
210   LogicalResult matchAndRewrite(Operation *op,
211                                 PatternRewriter &rewriter) const final {
212     Region &region = *op->getParentRegion();
213     Type i32Type = rewriter.getIntegerType(32);
214     rewriter.createBlock(&region, region.end(), {i32Type, i32Type});
215     // Create an illegal op to ensure the conversion fails.
216     rewriter.create<ILLegalOpF>(op->getLoc(), i32Type);
217     rewriter.create<TerminatorOp>(op->getLoc());
218     rewriter.replaceOp(op, {});
219     return success();
220   }
221 };
222 
223 //===----------------------------------------------------------------------===//
224 // Type-Conversion Rewrite Testing
225 
226 /// This patterns erases a region operation that has had a type conversion.
227 struct TestDropOpSignatureConversion : public ConversionPattern {
228   TestDropOpSignatureConversion(MLIRContext *ctx, TypeConverter &converter)
229       : ConversionPattern("test.drop_region_op", 1, ctx), converter(converter) {
230   }
231   LogicalResult
232   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
233                   ConversionPatternRewriter &rewriter) const override {
234     Region &region = op->getRegion(0);
235     Block *entry = &region.front();
236 
237     // Convert the original entry arguments.
238     TypeConverter::SignatureConversion result(entry->getNumArguments());
239     for (unsigned i = 0, e = entry->getNumArguments(); i != e; ++i)
240       if (failed(converter.convertSignatureArg(
241               i, entry->getArgument(i).getType(), result)))
242         return failure();
243 
244     // Convert the region signature and just drop the operation.
245     rewriter.applySignatureConversion(&region, result);
246     rewriter.eraseOp(op);
247     return success();
248   }
249 
250   /// The type converter to use when rewriting the signature.
251   TypeConverter &converter;
252 };
253 /// This pattern simply updates the operands of the given operation.
254 struct TestPassthroughInvalidOp : public ConversionPattern {
255   TestPassthroughInvalidOp(MLIRContext *ctx)
256       : ConversionPattern("test.invalid", 1, ctx) {}
257   LogicalResult
258   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
259                   ConversionPatternRewriter &rewriter) const final {
260     rewriter.replaceOpWithNewOp<TestValidOp>(op, llvm::None, operands,
261                                              llvm::None);
262     return success();
263   }
264 };
265 /// This pattern handles the case of a split return value.
266 struct TestSplitReturnType : public ConversionPattern {
267   TestSplitReturnType(MLIRContext *ctx)
268       : ConversionPattern("test.return", 1, ctx) {}
269   LogicalResult
270   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
271                   ConversionPatternRewriter &rewriter) const final {
272     // Check for a return of F32.
273     if (op->getNumOperands() != 1 || !op->getOperand(0).getType().isF32())
274       return failure();
275 
276     // Check if the first operation is a cast operation, if it is we use the
277     // results directly.
278     auto *defOp = operands[0].getDefiningOp();
279     if (auto packerOp = llvm::dyn_cast_or_null<TestCastOp>(defOp)) {
280       rewriter.replaceOpWithNewOp<TestReturnOp>(op, packerOp.getOperands());
281       return success();
282     }
283 
284     // Otherwise, fail to match.
285     return failure();
286   }
287 };
288 
289 //===----------------------------------------------------------------------===//
290 // Multi-Level Type-Conversion Rewrite Testing
291 struct TestChangeProducerTypeI32ToF32 : public ConversionPattern {
292   TestChangeProducerTypeI32ToF32(MLIRContext *ctx)
293       : ConversionPattern("test.type_producer", 1, ctx) {}
294   LogicalResult
295   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
296                   ConversionPatternRewriter &rewriter) const final {
297     // If the type is I32, change the type to F32.
298     if (!Type(*op->result_type_begin()).isSignlessInteger(32))
299       return failure();
300     rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getF32Type());
301     return success();
302   }
303 };
304 struct TestChangeProducerTypeF32ToF64 : public ConversionPattern {
305   TestChangeProducerTypeF32ToF64(MLIRContext *ctx)
306       : ConversionPattern("test.type_producer", 1, ctx) {}
307   LogicalResult
308   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
309                   ConversionPatternRewriter &rewriter) const final {
310     // If the type is F32, change the type to F64.
311     if (!Type(*op->result_type_begin()).isF32())
312       return rewriter.notifyMatchFailure(op, "expected single f32 operand");
313     rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getF64Type());
314     return success();
315   }
316 };
317 struct TestChangeProducerTypeF32ToInvalid : public ConversionPattern {
318   TestChangeProducerTypeF32ToInvalid(MLIRContext *ctx)
319       : ConversionPattern("test.type_producer", 10, ctx) {}
320   LogicalResult
321   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
322                   ConversionPatternRewriter &rewriter) const final {
323     // Always convert to B16, even though it is not a legal type. This tests
324     // that values are unmapped correctly.
325     rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getBF16Type());
326     return success();
327   }
328 };
329 struct TestUpdateConsumerType : public ConversionPattern {
330   TestUpdateConsumerType(MLIRContext *ctx)
331       : ConversionPattern("test.type_consumer", 1, ctx) {}
332   LogicalResult
333   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
334                   ConversionPatternRewriter &rewriter) const final {
335     // Verify that the incoming operand has been successfully remapped to F64.
336     if (!operands[0].getType().isF64())
337       return failure();
338     rewriter.replaceOpWithNewOp<TestTypeConsumerOp>(op, operands[0]);
339     return success();
340   }
341 };
342 
343 //===----------------------------------------------------------------------===//
344 // Non-Root Replacement Rewrite Testing
345 /// This pattern generates an invalid operation, but replaces it before the
346 /// pattern is finished. This checks that we don't need to legalize the
347 /// temporary op.
348 struct TestNonRootReplacement : public RewritePattern {
349   TestNonRootReplacement(MLIRContext *ctx)
350       : RewritePattern("test.replace_non_root", 1, ctx) {}
351 
352   LogicalResult matchAndRewrite(Operation *op,
353                                 PatternRewriter &rewriter) const final {
354     auto resultType = *op->result_type_begin();
355     auto illegalOp = rewriter.create<ILLegalOpF>(op->getLoc(), resultType);
356     auto legalOp = rewriter.create<LegalOpB>(op->getLoc(), resultType);
357 
358     rewriter.replaceOp(illegalOp, {legalOp});
359     rewriter.replaceOp(op, {illegalOp});
360     return success();
361   }
362 };
363 } // namespace
364 
365 namespace {
366 struct TestTypeConverter : public TypeConverter {
367   using TypeConverter::TypeConverter;
368   TestTypeConverter() { addConversion(convertType); }
369 
370   static LogicalResult convertType(Type t, SmallVectorImpl<Type> &results) {
371     // Drop I16 types.
372     if (t.isSignlessInteger(16))
373       return success();
374 
375     // Convert I64 to F64.
376     if (t.isSignlessInteger(64)) {
377       results.push_back(FloatType::getF64(t.getContext()));
378       return success();
379     }
380 
381     // Split F32 into F16,F16.
382     if (t.isF32()) {
383       results.assign(2, FloatType::getF16(t.getContext()));
384       return success();
385     }
386 
387     // Otherwise, convert the type directly.
388     results.push_back(t);
389     return success();
390   }
391 
392   /// Override the hook to materialize a conversion. This is necessary because
393   /// we generate 1->N type mappings.
394   Operation *materializeConversion(PatternRewriter &rewriter, Type resultType,
395                                    ArrayRef<Value> inputs,
396                                    Location loc) override {
397     return rewriter.create<TestCastOp>(loc, resultType, inputs);
398   }
399 };
400 
401 struct TestLegalizePatternDriver
402     : public PassWrapper<TestLegalizePatternDriver, OperationPass<ModuleOp>> {
403   /// The mode of conversion to use with the driver.
404   enum class ConversionMode { Analysis, Full, Partial };
405 
406   TestLegalizePatternDriver(ConversionMode mode) : mode(mode) {}
407 
408   void runOnOperation() override {
409     TestTypeConverter converter;
410     mlir::OwningRewritePatternList patterns;
411     populateWithGenerated(&getContext(), &patterns);
412     patterns.insert<
413         TestRegionRewriteBlockMovement, TestRegionRewriteUndo, TestCreateBlock,
414         TestCreateIllegalBlock, TestPassthroughInvalidOp, TestSplitReturnType,
415         TestChangeProducerTypeI32ToF32, TestChangeProducerTypeF32ToF64,
416         TestChangeProducerTypeF32ToInvalid, TestUpdateConsumerType,
417         TestNonRootReplacement>(&getContext());
418     patterns.insert<TestDropOpSignatureConversion>(&getContext(), converter);
419     mlir::populateFuncOpTypeConversionPattern(patterns, &getContext(),
420                                               converter);
421     mlir::populateCallOpTypeConversionPattern(patterns, &getContext(),
422                                               converter);
423 
424     // Define the conversion target used for the test.
425     ConversionTarget target(getContext());
426     target.addLegalOp<ModuleOp, ModuleTerminatorOp>();
427     target.addLegalOp<LegalOpA, LegalOpB, TestCastOp, TestValidOp,
428                       TerminatorOp>();
429     target
430         .addIllegalOp<ILLegalOpF, TestRegionBuilderOp, TestOpWithRegionFold>();
431     target.addDynamicallyLegalOp<TestReturnOp>([](TestReturnOp op) {
432       // Don't allow F32 operands.
433       return llvm::none_of(op.getOperandTypes(),
434                            [](Type type) { return type.isF32(); });
435     });
436     target.addDynamicallyLegalOp<FuncOp>(
437         [&](FuncOp op) { return converter.isSignatureLegal(op.getType()); });
438 
439     // Expect the type_producer/type_consumer operations to only operate on f64.
440     target.addDynamicallyLegalOp<TestTypeProducerOp>(
441         [](TestTypeProducerOp op) { return op.getType().isF64(); });
442     target.addDynamicallyLegalOp<TestTypeConsumerOp>([](TestTypeConsumerOp op) {
443       return op.getOperand().getType().isF64();
444     });
445 
446     // Check support for marking certain operations as recursively legal.
447     target.markOpRecursivelyLegal<FuncOp, ModuleOp>([](Operation *op) {
448       return static_cast<bool>(
449           op->getAttrOfType<UnitAttr>("test.recursively_legal"));
450     });
451 
452     // Handle a partial conversion.
453     if (mode == ConversionMode::Partial) {
454       (void)applyPartialConversion(getOperation(), target, patterns,
455                                    &converter);
456       return;
457     }
458 
459     // Handle a full conversion.
460     if (mode == ConversionMode::Full) {
461       // Check support for marking unknown operations as dynamically legal.
462       target.markUnknownOpDynamicallyLegal([](Operation *op) {
463         return (bool)op->getAttrOfType<UnitAttr>("test.dynamically_legal");
464       });
465 
466       (void)applyFullConversion(getOperation(), target, patterns, &converter);
467       return;
468     }
469 
470     // Otherwise, handle an analysis conversion.
471     assert(mode == ConversionMode::Analysis);
472 
473     // Analyze the convertible operations.
474     DenseSet<Operation *> legalizedOps;
475     if (failed(applyAnalysisConversion(getOperation(), target, patterns,
476                                        legalizedOps, &converter)))
477       return signalPassFailure();
478 
479     // Emit remarks for each legalizable operation.
480     for (auto *op : legalizedOps)
481       op->emitRemark() << "op '" << op->getName() << "' is legalizable";
482   }
483 
484   /// The mode of conversion to use.
485   ConversionMode mode;
486 };
487 } // end anonymous namespace
488 
489 static llvm::cl::opt<TestLegalizePatternDriver::ConversionMode>
490     legalizerConversionMode(
491         "test-legalize-mode",
492         llvm::cl::desc("The legalization mode to use with the test driver"),
493         llvm::cl::init(TestLegalizePatternDriver::ConversionMode::Partial),
494         llvm::cl::values(
495             clEnumValN(TestLegalizePatternDriver::ConversionMode::Analysis,
496                        "analysis", "Perform an analysis conversion"),
497             clEnumValN(TestLegalizePatternDriver::ConversionMode::Full, "full",
498                        "Perform a full conversion"),
499             clEnumValN(TestLegalizePatternDriver::ConversionMode::Partial,
500                        "partial", "Perform a partial conversion")));
501 
502 //===----------------------------------------------------------------------===//
503 // ConversionPatternRewriter::getRemappedValue testing. This method is used
504 // to get the remapped value of an original value that was replaced using
505 // ConversionPatternRewriter.
506 namespace {
507 /// Converter that replaces a one-result one-operand OneVResOneVOperandOp1 with
508 /// a one-operand two-result OneVResOneVOperandOp1 by replicating its original
509 /// operand twice.
510 ///
511 /// Example:
512 ///   %1 = test.one_variadic_out_one_variadic_in1"(%0)
513 /// is replaced with:
514 ///   %1 = test.one_variadic_out_one_variadic_in1"(%0, %0)
515 struct OneVResOneVOperandOp1Converter
516     : public OpConversionPattern<OneVResOneVOperandOp1> {
517   using OpConversionPattern<OneVResOneVOperandOp1>::OpConversionPattern;
518 
519   LogicalResult
520   matchAndRewrite(OneVResOneVOperandOp1 op, ArrayRef<Value> operands,
521                   ConversionPatternRewriter &rewriter) const override {
522     auto origOps = op.getOperands();
523     assert(std::distance(origOps.begin(), origOps.end()) == 1 &&
524            "One operand expected");
525     Value origOp = *origOps.begin();
526     SmallVector<Value, 2> remappedOperands;
527     // Replicate the remapped original operand twice. Note that we don't used
528     // the remapped 'operand' since the goal is testing 'getRemappedValue'.
529     remappedOperands.push_back(rewriter.getRemappedValue(origOp));
530     remappedOperands.push_back(rewriter.getRemappedValue(origOp));
531 
532     rewriter.replaceOpWithNewOp<OneVResOneVOperandOp1>(op, op.getResultTypes(),
533                                                        remappedOperands);
534     return success();
535   }
536 };
537 
538 struct TestRemappedValue
539     : public mlir::PassWrapper<TestRemappedValue, FunctionPass> {
540   void runOnFunction() override {
541     mlir::OwningRewritePatternList patterns;
542     patterns.insert<OneVResOneVOperandOp1Converter>(&getContext());
543 
544     mlir::ConversionTarget target(getContext());
545     target.addLegalOp<ModuleOp, ModuleTerminatorOp, FuncOp, TestReturnOp>();
546     // We make OneVResOneVOperandOp1 legal only when it has more that one
547     // operand. This will trigger the conversion that will replace one-operand
548     // OneVResOneVOperandOp1 with two-operand OneVResOneVOperandOp1.
549     target.addDynamicallyLegalOp<OneVResOneVOperandOp1>(
550         [](Operation *op) -> bool {
551           return std::distance(op->operand_begin(), op->operand_end()) > 1;
552         });
553 
554     if (failed(mlir::applyFullConversion(getFunction(), target, patterns))) {
555       signalPassFailure();
556     }
557   }
558 };
559 } // end anonymous namespace
560 
561 namespace mlir {
562 void registerPatternsTestPass() {
563   mlir::PassRegistration<TestReturnTypeDriver>("test-return-type",
564                                                "Run return type functions");
565 
566   mlir::PassRegistration<TestPatternDriver>("test-patterns",
567                                             "Run test dialect patterns");
568 
569   mlir::PassRegistration<TestLegalizePatternDriver>(
570       "test-legalize-patterns", "Run test dialect legalization patterns", [] {
571         return std::make_unique<TestLegalizePatternDriver>(
572             legalizerConversionMode);
573       });
574 
575   PassRegistration<TestRemappedValue>(
576       "test-remapped-value",
577       "Test public remapped value mechanism in ConversionPatternRewriter");
578 }
579 } // namespace mlir
580