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     applyPatternsAndFoldGreedily(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 
364 //===----------------------------------------------------------------------===//
365 // Recursive Rewrite Testing
366 /// This pattern is applied to the same operation multiple times, but has a
367 /// bounded recursion.
368 struct TestBoundedRecursiveRewrite
369     : public OpRewritePattern<TestRecursiveRewriteOp> {
370   using OpRewritePattern<TestRecursiveRewriteOp>::OpRewritePattern;
371 
372   LogicalResult matchAndRewrite(TestRecursiveRewriteOp op,
373                                 PatternRewriter &rewriter) const final {
374     // Decrement the depth of the op in-place.
375     rewriter.updateRootInPlace(op, [&] {
376       op.setAttr("depth",
377                  rewriter.getI64IntegerAttr(op.depth().getSExtValue() - 1));
378     });
379     return success();
380   }
381 
382   /// The conversion target handles bounding the recursion of this pattern.
383   bool hasBoundedRewriteRecursion() const final { return true; }
384 };
385 } // namespace
386 
387 namespace {
388 struct TestTypeConverter : public TypeConverter {
389   using TypeConverter::TypeConverter;
390   TestTypeConverter() { addConversion(convertType); }
391 
392   static LogicalResult convertType(Type t, SmallVectorImpl<Type> &results) {
393     // Drop I16 types.
394     if (t.isSignlessInteger(16))
395       return success();
396 
397     // Convert I64 to F64.
398     if (t.isSignlessInteger(64)) {
399       results.push_back(FloatType::getF64(t.getContext()));
400       return success();
401     }
402 
403     // Split F32 into F16,F16.
404     if (t.isF32()) {
405       results.assign(2, FloatType::getF16(t.getContext()));
406       return success();
407     }
408 
409     // Otherwise, convert the type directly.
410     results.push_back(t);
411     return success();
412   }
413 
414   /// Override the hook to materialize a conversion. This is necessary because
415   /// we generate 1->N type mappings.
416   Operation *materializeConversion(PatternRewriter &rewriter, Type resultType,
417                                    ArrayRef<Value> inputs,
418                                    Location loc) override {
419     return rewriter.create<TestCastOp>(loc, resultType, inputs);
420   }
421 };
422 
423 struct TestLegalizePatternDriver
424     : public PassWrapper<TestLegalizePatternDriver, OperationPass<ModuleOp>> {
425   /// The mode of conversion to use with the driver.
426   enum class ConversionMode { Analysis, Full, Partial };
427 
428   TestLegalizePatternDriver(ConversionMode mode) : mode(mode) {}
429 
430   void runOnOperation() override {
431     TestTypeConverter converter;
432     mlir::OwningRewritePatternList patterns;
433     populateWithGenerated(&getContext(), &patterns);
434     patterns.insert<
435         TestRegionRewriteBlockMovement, TestRegionRewriteUndo, TestCreateBlock,
436         TestCreateIllegalBlock, TestPassthroughInvalidOp, TestSplitReturnType,
437         TestChangeProducerTypeI32ToF32, TestChangeProducerTypeF32ToF64,
438         TestChangeProducerTypeF32ToInvalid, TestUpdateConsumerType,
439         TestNonRootReplacement, TestBoundedRecursiveRewrite>(&getContext());
440     patterns.insert<TestDropOpSignatureConversion>(&getContext(), converter);
441     mlir::populateFuncOpTypeConversionPattern(patterns, &getContext(),
442                                               converter);
443     mlir::populateCallOpTypeConversionPattern(patterns, &getContext(),
444                                               converter);
445 
446     // Define the conversion target used for the test.
447     ConversionTarget target(getContext());
448     target.addLegalOp<ModuleOp, ModuleTerminatorOp>();
449     target.addLegalOp<LegalOpA, LegalOpB, TestCastOp, TestValidOp,
450                       TerminatorOp>();
451     target
452         .addIllegalOp<ILLegalOpF, TestRegionBuilderOp, TestOpWithRegionFold>();
453     target.addDynamicallyLegalOp<TestReturnOp>([](TestReturnOp op) {
454       // Don't allow F32 operands.
455       return llvm::none_of(op.getOperandTypes(),
456                            [](Type type) { return type.isF32(); });
457     });
458     target.addDynamicallyLegalOp<FuncOp>(
459         [&](FuncOp op) { return converter.isSignatureLegal(op.getType()); });
460 
461     // Expect the type_producer/type_consumer operations to only operate on f64.
462     target.addDynamicallyLegalOp<TestTypeProducerOp>(
463         [](TestTypeProducerOp op) { return op.getType().isF64(); });
464     target.addDynamicallyLegalOp<TestTypeConsumerOp>([](TestTypeConsumerOp op) {
465       return op.getOperand().getType().isF64();
466     });
467 
468     // Check support for marking certain operations as recursively legal.
469     target.markOpRecursivelyLegal<FuncOp, ModuleOp>([](Operation *op) {
470       return static_cast<bool>(
471           op->getAttrOfType<UnitAttr>("test.recursively_legal"));
472     });
473 
474     // Mark the bound recursion operation as dynamically legal.
475     target.addDynamicallyLegalOp<TestRecursiveRewriteOp>(
476         [](TestRecursiveRewriteOp op) { return op.depth() == 0; });
477 
478     // Handle a partial conversion.
479     if (mode == ConversionMode::Partial) {
480       (void)applyPartialConversion(getOperation(), target, patterns,
481                                    &converter);
482       return;
483     }
484 
485     // Handle a full conversion.
486     if (mode == ConversionMode::Full) {
487       // Check support for marking unknown operations as dynamically legal.
488       target.markUnknownOpDynamicallyLegal([](Operation *op) {
489         return (bool)op->getAttrOfType<UnitAttr>("test.dynamically_legal");
490       });
491 
492       (void)applyFullConversion(getOperation(), target, patterns, &converter);
493       return;
494     }
495 
496     // Otherwise, handle an analysis conversion.
497     assert(mode == ConversionMode::Analysis);
498 
499     // Analyze the convertible operations.
500     DenseSet<Operation *> legalizedOps;
501     if (failed(applyAnalysisConversion(getOperation(), target, patterns,
502                                        legalizedOps, &converter)))
503       return signalPassFailure();
504 
505     // Emit remarks for each legalizable operation.
506     for (auto *op : legalizedOps)
507       op->emitRemark() << "op '" << op->getName() << "' is legalizable";
508   }
509 
510   /// The mode of conversion to use.
511   ConversionMode mode;
512 };
513 } // end anonymous namespace
514 
515 static llvm::cl::opt<TestLegalizePatternDriver::ConversionMode>
516     legalizerConversionMode(
517         "test-legalize-mode",
518         llvm::cl::desc("The legalization mode to use with the test driver"),
519         llvm::cl::init(TestLegalizePatternDriver::ConversionMode::Partial),
520         llvm::cl::values(
521             clEnumValN(TestLegalizePatternDriver::ConversionMode::Analysis,
522                        "analysis", "Perform an analysis conversion"),
523             clEnumValN(TestLegalizePatternDriver::ConversionMode::Full, "full",
524                        "Perform a full conversion"),
525             clEnumValN(TestLegalizePatternDriver::ConversionMode::Partial,
526                        "partial", "Perform a partial conversion")));
527 
528 //===----------------------------------------------------------------------===//
529 // ConversionPatternRewriter::getRemappedValue testing. This method is used
530 // to get the remapped value of an original value that was replaced using
531 // ConversionPatternRewriter.
532 namespace {
533 /// Converter that replaces a one-result one-operand OneVResOneVOperandOp1 with
534 /// a one-operand two-result OneVResOneVOperandOp1 by replicating its original
535 /// operand twice.
536 ///
537 /// Example:
538 ///   %1 = test.one_variadic_out_one_variadic_in1"(%0)
539 /// is replaced with:
540 ///   %1 = test.one_variadic_out_one_variadic_in1"(%0, %0)
541 struct OneVResOneVOperandOp1Converter
542     : public OpConversionPattern<OneVResOneVOperandOp1> {
543   using OpConversionPattern<OneVResOneVOperandOp1>::OpConversionPattern;
544 
545   LogicalResult
546   matchAndRewrite(OneVResOneVOperandOp1 op, ArrayRef<Value> operands,
547                   ConversionPatternRewriter &rewriter) const override {
548     auto origOps = op.getOperands();
549     assert(std::distance(origOps.begin(), origOps.end()) == 1 &&
550            "One operand expected");
551     Value origOp = *origOps.begin();
552     SmallVector<Value, 2> remappedOperands;
553     // Replicate the remapped original operand twice. Note that we don't used
554     // the remapped 'operand' since the goal is testing 'getRemappedValue'.
555     remappedOperands.push_back(rewriter.getRemappedValue(origOp));
556     remappedOperands.push_back(rewriter.getRemappedValue(origOp));
557 
558     rewriter.replaceOpWithNewOp<OneVResOneVOperandOp1>(op, op.getResultTypes(),
559                                                        remappedOperands);
560     return success();
561   }
562 };
563 
564 struct TestRemappedValue
565     : public mlir::PassWrapper<TestRemappedValue, FunctionPass> {
566   void runOnFunction() override {
567     mlir::OwningRewritePatternList patterns;
568     patterns.insert<OneVResOneVOperandOp1Converter>(&getContext());
569 
570     mlir::ConversionTarget target(getContext());
571     target.addLegalOp<ModuleOp, ModuleTerminatorOp, FuncOp, TestReturnOp>();
572     // We make OneVResOneVOperandOp1 legal only when it has more that one
573     // operand. This will trigger the conversion that will replace one-operand
574     // OneVResOneVOperandOp1 with two-operand OneVResOneVOperandOp1.
575     target.addDynamicallyLegalOp<OneVResOneVOperandOp1>(
576         [](Operation *op) -> bool {
577           return std::distance(op->operand_begin(), op->operand_end()) > 1;
578         });
579 
580     if (failed(mlir::applyFullConversion(getFunction(), target, patterns))) {
581       signalPassFailure();
582     }
583   }
584 };
585 } // end anonymous namespace
586 
587 namespace mlir {
588 void registerPatternsTestPass() {
589   mlir::PassRegistration<TestReturnTypeDriver>("test-return-type",
590                                                "Run return type functions");
591 
592   mlir::PassRegistration<TestPatternDriver>("test-patterns",
593                                             "Run test dialect patterns");
594 
595   mlir::PassRegistration<TestLegalizePatternDriver>(
596       "test-legalize-patterns", "Run test dialect legalization patterns", [] {
597         return std::make_unique<TestLegalizePatternDriver>(
598             legalizerConversionMode);
599       });
600 
601   PassRegistration<TestRemappedValue>(
602       "test-remapped-value",
603       "Test public remapped value mechanism in ConversionPatternRewriter");
604 }
605 } // namespace mlir
606