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