1fec6c5acSUday Bondhugula //===- TestPatterns.cpp - Test dialect pattern driver ---------------------===//
2fec6c5acSUday Bondhugula //
3fec6c5acSUday Bondhugula // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fec6c5acSUday Bondhugula // See https://llvm.org/LICENSE.txt for license information.
5fec6c5acSUday Bondhugula // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fec6c5acSUday Bondhugula //
7fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
8fec6c5acSUday Bondhugula 
9fec6c5acSUday Bondhugula #include "TestDialect.h"
10*a54f4eaeSMogball #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
1126f93d9fSAlex Zinenko #include "mlir/Dialect/StandardOps/IR/Ops.h"
12473bdaf2SAlex Zinenko #include "mlir/Dialect/StandardOps/Transforms/FuncConversions.h"
13c0a6318dSMatthias Springer #include "mlir/Dialect/Tensor/IR/Tensor.h"
142bf423b0SRob Suderman #include "mlir/IR/Matchers.h"
15fec6c5acSUday Bondhugula #include "mlir/Pass/Pass.h"
16fec6c5acSUday Bondhugula #include "mlir/Transforms/DialectConversion.h"
1726f93d9fSAlex Zinenko #include "mlir/Transforms/FoldUtils.h"
18b6eb26fdSRiver Riddle #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
199ba37b3bSJacques Pienaar 
20fec6c5acSUday Bondhugula using namespace mlir;
217776b19eSStephen Neuendorffer using namespace test;
22fec6c5acSUday Bondhugula 
23fec6c5acSUday Bondhugula // Native function for testing NativeCodeCall
24fec6c5acSUday Bondhugula static Value chooseOperand(Value input1, Value input2, BoolAttr choice) {
25fec6c5acSUday Bondhugula   return choice.getValue() ? input1 : input2;
26fec6c5acSUday Bondhugula }
27fec6c5acSUday Bondhugula 
2829429d1aSJacques Pienaar static void createOpI(PatternRewriter &rewriter, Location loc, Value input) {
2929429d1aSJacques Pienaar   rewriter.create<OpI>(loc, input);
30fec6c5acSUday Bondhugula }
31fec6c5acSUday Bondhugula 
32fec6c5acSUday Bondhugula static void handleNoResultOp(PatternRewriter &rewriter,
33fec6c5acSUday Bondhugula                              OpSymbolBindingNoResult op) {
34fec6c5acSUday Bondhugula   // Turn the no result op to a one-result op.
35fec6c5acSUday Bondhugula   rewriter.create<OpSymbolBindingB>(op.getLoc(), op.operand().getType(),
36fec6c5acSUday Bondhugula                                     op.operand());
37fec6c5acSUday Bondhugula }
38fec6c5acSUday Bondhugula 
3934b5482bSChia-hung Duan static bool getFirstI32Result(Operation *op, Value &value) {
4034b5482bSChia-hung Duan   if (!Type(op->getResult(0).getType()).isSignlessInteger(32))
4134b5482bSChia-hung Duan     return false;
4234b5482bSChia-hung Duan   value = op->getResult(0);
4334b5482bSChia-hung Duan   return true;
4434b5482bSChia-hung Duan }
4534b5482bSChia-hung Duan 
4634b5482bSChia-hung Duan static Value bindNativeCodeCallResult(Value value) { return value; }
4734b5482bSChia-hung Duan 
48d7314b3cSChia-hung Duan static SmallVector<Value, 2> bindMultipleNativeCodeCallResult(Value input1,
49d7314b3cSChia-hung Duan                                                               Value input2) {
50d7314b3cSChia-hung Duan   return SmallVector<Value, 2>({input2, input1});
51d7314b3cSChia-hung Duan }
52d7314b3cSChia-hung Duan 
5301641197SAlexEichenberger // Test that natives calls are only called once during rewrites.
5401641197SAlexEichenberger // OpM_Test will return Pi, increased by 1 for each subsequent calls.
5501641197SAlexEichenberger // This let us check the number of times OpM_Test was called by inspecting
5601641197SAlexEichenberger // the returned value in the MLIR output.
5701641197SAlexEichenberger static int64_t opMIncreasingValue = 314159265;
5801641197SAlexEichenberger static Attribute OpMTest(PatternRewriter &rewriter, Value val) {
5901641197SAlexEichenberger   int64_t i = opMIncreasingValue++;
6001641197SAlexEichenberger   return rewriter.getIntegerAttr(rewriter.getIntegerType(32), i);
6101641197SAlexEichenberger }
6201641197SAlexEichenberger 
63fec6c5acSUday Bondhugula namespace {
64fec6c5acSUday Bondhugula #include "TestPatterns.inc"
65fec6c5acSUday Bondhugula } // end anonymous namespace
66fec6c5acSUday Bondhugula 
67fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
68c484c7ddSChia-hung Duan // Test Reduce Pattern Interface
69c484c7ddSChia-hung Duan //===----------------------------------------------------------------------===//
70c484c7ddSChia-hung Duan 
717776b19eSStephen Neuendorffer void test::populateTestReductionPatterns(RewritePatternSet &patterns) {
72c484c7ddSChia-hung Duan   populateWithGenerated(patterns);
73c484c7ddSChia-hung Duan }
74c484c7ddSChia-hung Duan 
75c484c7ddSChia-hung Duan //===----------------------------------------------------------------------===//
76fec6c5acSUday Bondhugula // Canonicalizer Driver.
77fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
78fec6c5acSUday Bondhugula 
79fec6c5acSUday Bondhugula namespace {
8026f93d9fSAlex Zinenko struct FoldingPattern : public RewritePattern {
8126f93d9fSAlex Zinenko public:
8226f93d9fSAlex Zinenko   FoldingPattern(MLIRContext *context)
8326f93d9fSAlex Zinenko       : RewritePattern(TestOpInPlaceFoldAnchor::getOperationName(),
8426f93d9fSAlex Zinenko                        /*benefit=*/1, context) {}
8526f93d9fSAlex Zinenko 
8626f93d9fSAlex Zinenko   LogicalResult matchAndRewrite(Operation *op,
8726f93d9fSAlex Zinenko                                 PatternRewriter &rewriter) const override {
882b638ed5SKazuaki Ishizaki     // Exercise OperationFolder API for a single-result operation that is folded
8926f93d9fSAlex Zinenko     // upon construction. The operation being created through the folder has an
9026f93d9fSAlex Zinenko     // in-place folder, and it should be still present in the output.
9126f93d9fSAlex Zinenko     // Furthermore, the folder should not crash when attempting to recover the
92a23d0559SKazuaki Ishizaki     // (unchanged) operation result.
9326f93d9fSAlex Zinenko     OperationFolder folder(op->getContext());
9426f93d9fSAlex Zinenko     Value result = folder.create<TestOpInPlaceFold>(
9526f93d9fSAlex Zinenko         rewriter, op->getLoc(), rewriter.getIntegerType(32), op->getOperand(0),
9626f93d9fSAlex Zinenko         rewriter.getI32IntegerAttr(0));
9726f93d9fSAlex Zinenko     assert(result);
9826f93d9fSAlex Zinenko     rewriter.replaceOp(op, result);
9926f93d9fSAlex Zinenko     return success();
10026f93d9fSAlex Zinenko   }
10126f93d9fSAlex Zinenko };
10226f93d9fSAlex Zinenko 
103e4635e63SRiver Riddle /// This pattern creates a foldable operation at the entry point of the block.
104e4635e63SRiver Riddle /// This tests the situation where the operation folder will need to replace an
105e4635e63SRiver Riddle /// operation with a previously created constant that does not initially
106e4635e63SRiver Riddle /// dominate the operation to replace.
107e4635e63SRiver Riddle struct FolderInsertBeforePreviouslyFoldedConstantPattern
108e4635e63SRiver Riddle     : public OpRewritePattern<TestCastOp> {
109e4635e63SRiver Riddle public:
110e4635e63SRiver Riddle   using OpRewritePattern<TestCastOp>::OpRewritePattern;
111e4635e63SRiver Riddle 
112e4635e63SRiver Riddle   LogicalResult matchAndRewrite(TestCastOp op,
113e4635e63SRiver Riddle                                 PatternRewriter &rewriter) const override {
114e4635e63SRiver Riddle     if (!op->hasAttr("test_fold_before_previously_folded_op"))
115e4635e63SRiver Riddle       return failure();
116e4635e63SRiver Riddle     rewriter.setInsertionPointToStart(op->getBlock());
117e4635e63SRiver Riddle 
118*a54f4eaeSMogball     auto constOp = rewriter.create<arith::ConstantOp>(
119*a54f4eaeSMogball         op.getLoc(), rewriter.getBoolAttr(true));
120e4635e63SRiver Riddle     rewriter.replaceOpWithNewOp<TestCastOp>(op, rewriter.getI32Type(),
121e4635e63SRiver Riddle                                             Value(constOp));
122e4635e63SRiver Riddle     return success();
123e4635e63SRiver Riddle   }
124e4635e63SRiver Riddle };
125e4635e63SRiver Riddle 
12680aca1eaSRiver Riddle struct TestPatternDriver : public PassWrapper<TestPatternDriver, FunctionPass> {
127b5e22e6dSMehdi Amini   StringRef getArgument() const final { return "test-patterns"; }
128b5e22e6dSMehdi Amini   StringRef getDescription() const final { return "Run test dialect patterns"; }
129fec6c5acSUday Bondhugula   void runOnFunction() override {
130dc4e913bSChris Lattner     mlir::RewritePatternSet patterns(&getContext());
1311d909c9aSChris Lattner     populateWithGenerated(patterns);
132fec6c5acSUday Bondhugula 
133fec6c5acSUday Bondhugula     // Verify named pattern is generated with expected name.
134e4635e63SRiver Riddle     patterns.add<FoldingPattern, TestNamedPatternRule,
135e4635e63SRiver Riddle                  FolderInsertBeforePreviouslyFoldedConstantPattern>(
136e4635e63SRiver Riddle         &getContext());
137fec6c5acSUday Bondhugula 
138e21adfa3SRiver Riddle     (void)applyPatternsAndFoldGreedily(getFunction(), std::move(patterns));
139fec6c5acSUday Bondhugula   }
140fec6c5acSUday Bondhugula };
141fec6c5acSUday Bondhugula } // end anonymous namespace
142fec6c5acSUday Bondhugula 
143fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
144fec6c5acSUday Bondhugula // ReturnType Driver.
145fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
146fec6c5acSUday Bondhugula 
147fec6c5acSUday Bondhugula namespace {
148fec6c5acSUday Bondhugula // Generate ops for each instance where the type can be successfully inferred.
149fec6c5acSUday Bondhugula template <typename OpTy>
150fec6c5acSUday Bondhugula static void invokeCreateWithInferredReturnType(Operation *op) {
151fec6c5acSUday Bondhugula   auto *context = op->getContext();
152fec6c5acSUday Bondhugula   auto fop = op->getParentOfType<FuncOp>();
153fec6c5acSUday Bondhugula   auto location = UnknownLoc::get(context);
154fec6c5acSUday Bondhugula   OpBuilder b(op);
155fec6c5acSUday Bondhugula   b.setInsertionPointAfter(op);
156fec6c5acSUday Bondhugula 
157fec6c5acSUday Bondhugula   // Use permutations of 2 args as operands.
158fec6c5acSUday Bondhugula   assert(fop.getNumArguments() >= 2);
159fec6c5acSUday Bondhugula   for (int i = 0, e = fop.getNumArguments(); i < e; ++i) {
160fec6c5acSUday Bondhugula     for (int j = 0; j < e; ++j) {
161fec6c5acSUday Bondhugula       std::array<Value, 2> values = {{fop.getArgument(i), fop.getArgument(j)}};
162fec6c5acSUday Bondhugula       SmallVector<Type, 2> inferredReturnTypes;
1635eae715aSJacques Pienaar       if (succeeded(OpTy::inferReturnTypes(
1645eae715aSJacques Pienaar               context, llvm::None, values, op->getAttrDictionary(),
1655eae715aSJacques Pienaar               op->getRegions(), inferredReturnTypes))) {
166fec6c5acSUday Bondhugula         OperationState state(location, OpTy::getOperationName());
1679db53a18SRiver Riddle         // TODO: Expand to regions.
168bb1d976fSAlex Zinenko         OpTy::build(b, state, values, op->getAttrs());
169fec6c5acSUday Bondhugula         (void)b.createOperation(state);
170fec6c5acSUday Bondhugula       }
171fec6c5acSUday Bondhugula     }
172fec6c5acSUday Bondhugula   }
173fec6c5acSUday Bondhugula }
174fec6c5acSUday Bondhugula 
175fec6c5acSUday Bondhugula static void reifyReturnShape(Operation *op) {
176fec6c5acSUday Bondhugula   OpBuilder b(op);
177fec6c5acSUday Bondhugula 
178fec6c5acSUday Bondhugula   // Use permutations of 2 args as operands.
179fec6c5acSUday Bondhugula   auto shapedOp = cast<OpWithShapedTypeInferTypeInterfaceOp>(op);
180fec6c5acSUday Bondhugula   SmallVector<Value, 2> shapes;
181851d02f6SWenyi Zhao   if (failed(shapedOp.reifyReturnTypeShapes(b, op->getOperands(), shapes)) ||
1829b051703SMaheshRavishankar       !llvm::hasSingleElement(shapes))
183fec6c5acSUday Bondhugula     return;
1849b051703SMaheshRavishankar   for (auto it : llvm::enumerate(shapes)) {
185fec6c5acSUday Bondhugula     op->emitRemark() << "value " << it.index() << ": "
186fec6c5acSUday Bondhugula                      << it.value().getDefiningOp();
187fec6c5acSUday Bondhugula   }
1889b051703SMaheshRavishankar }
189fec6c5acSUday Bondhugula 
19080aca1eaSRiver Riddle struct TestReturnTypeDriver
19180aca1eaSRiver Riddle     : public PassWrapper<TestReturnTypeDriver, FunctionPass> {
192e2310704SJulian Gross   void getDependentDialects(DialectRegistry &registry) const override {
193c0a6318dSMatthias Springer     registry.insert<tensor::TensorDialect>();
194e2310704SJulian Gross   }
195b5e22e6dSMehdi Amini   StringRef getArgument() const final { return "test-return-type"; }
196b5e22e6dSMehdi Amini   StringRef getDescription() const final { return "Run return type functions"; }
197e2310704SJulian Gross 
198fec6c5acSUday Bondhugula   void runOnFunction() override {
199fec6c5acSUday Bondhugula     if (getFunction().getName() == "testCreateFunctions") {
200fec6c5acSUday Bondhugula       std::vector<Operation *> ops;
201fec6c5acSUday Bondhugula       // Collect ops to avoid triggering on inserted ops.
202fec6c5acSUday Bondhugula       for (auto &op : getFunction().getBody().front())
203fec6c5acSUday Bondhugula         ops.push_back(&op);
204fec6c5acSUday Bondhugula       // Generate test patterns for each, but skip terminator.
205fec6c5acSUday Bondhugula       for (auto *op : llvm::makeArrayRef(ops).drop_back()) {
206fec6c5acSUday Bondhugula         // Test create method of each of the Op classes below. The resultant
207fec6c5acSUday Bondhugula         // output would be in reverse order underneath `op` from which
208fec6c5acSUday Bondhugula         // the attributes and regions are used.
209fec6c5acSUday Bondhugula         invokeCreateWithInferredReturnType<OpWithInferTypeInterfaceOp>(op);
210fec6c5acSUday Bondhugula         invokeCreateWithInferredReturnType<
211fec6c5acSUday Bondhugula             OpWithShapedTypeInferTypeInterfaceOp>(op);
212fec6c5acSUday Bondhugula       };
213fec6c5acSUday Bondhugula       return;
214fec6c5acSUday Bondhugula     }
215fec6c5acSUday Bondhugula     if (getFunction().getName() == "testReifyFunctions") {
216fec6c5acSUday Bondhugula       std::vector<Operation *> ops;
217fec6c5acSUday Bondhugula       // Collect ops to avoid triggering on inserted ops.
218fec6c5acSUday Bondhugula       for (auto &op : getFunction().getBody().front())
219fec6c5acSUday Bondhugula         if (isa<OpWithShapedTypeInferTypeInterfaceOp>(op))
220fec6c5acSUday Bondhugula           ops.push_back(&op);
221fec6c5acSUday Bondhugula       // Generate test patterns for each, but skip terminator.
222fec6c5acSUday Bondhugula       for (auto *op : ops)
223fec6c5acSUday Bondhugula         reifyReturnShape(op);
224fec6c5acSUday Bondhugula     }
225fec6c5acSUday Bondhugula   }
226fec6c5acSUday Bondhugula };
227fec6c5acSUday Bondhugula } // end anonymous namespace
228fec6c5acSUday Bondhugula 
2299ba37b3bSJacques Pienaar namespace {
2309ba37b3bSJacques Pienaar struct TestDerivedAttributeDriver
2319ba37b3bSJacques Pienaar     : public PassWrapper<TestDerivedAttributeDriver, FunctionPass> {
232b5e22e6dSMehdi Amini   StringRef getArgument() const final { return "test-derived-attr"; }
233b5e22e6dSMehdi Amini   StringRef getDescription() const final {
234b5e22e6dSMehdi Amini     return "Run test derived attributes";
235b5e22e6dSMehdi Amini   }
2369ba37b3bSJacques Pienaar   void runOnFunction() override;
2379ba37b3bSJacques Pienaar };
2389ba37b3bSJacques Pienaar } // end anonymous namespace
2399ba37b3bSJacques Pienaar 
2409ba37b3bSJacques Pienaar void TestDerivedAttributeDriver::runOnFunction() {
2419ba37b3bSJacques Pienaar   getFunction().walk([](DerivedAttributeOpInterface dOp) {
2429ba37b3bSJacques Pienaar     auto dAttr = dOp.materializeDerivedAttributes();
2439ba37b3bSJacques Pienaar     if (!dAttr)
2449ba37b3bSJacques Pienaar       return;
2459ba37b3bSJacques Pienaar     for (auto d : dAttr)
2469ba37b3bSJacques Pienaar       dOp.emitRemark() << d.first << " = " << d.second;
2479ba37b3bSJacques Pienaar   });
2489ba37b3bSJacques Pienaar }
2499ba37b3bSJacques Pienaar 
250fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
251fec6c5acSUday Bondhugula // Legalization Driver.
252fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
253fec6c5acSUday Bondhugula 
254fec6c5acSUday Bondhugula namespace {
255fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
256fec6c5acSUday Bondhugula // Region-Block Rewrite Testing
257fec6c5acSUday Bondhugula 
258fec6c5acSUday Bondhugula /// This pattern is a simple pattern that inlines the first region of a given
259fec6c5acSUday Bondhugula /// operation into the parent region.
260fec6c5acSUday Bondhugula struct TestRegionRewriteBlockMovement : public ConversionPattern {
261fec6c5acSUday Bondhugula   TestRegionRewriteBlockMovement(MLIRContext *ctx)
262fec6c5acSUday Bondhugula       : ConversionPattern("test.region", 1, ctx) {}
263fec6c5acSUday Bondhugula 
264fec6c5acSUday Bondhugula   LogicalResult
265fec6c5acSUday Bondhugula   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
266fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const final {
267fec6c5acSUday Bondhugula     // Inline this region into the parent region.
268fec6c5acSUday Bondhugula     auto &parentRegion = *op->getParentRegion();
269b0750e2dSTres Popp     auto &opRegion = op->getRegion(0);
270fec6c5acSUday Bondhugula     if (op->getAttr("legalizer.should_clone"))
271b0750e2dSTres Popp       rewriter.cloneRegionBefore(opRegion, parentRegion, parentRegion.end());
272fec6c5acSUday Bondhugula     else
273b0750e2dSTres Popp       rewriter.inlineRegionBefore(opRegion, parentRegion, parentRegion.end());
274b0750e2dSTres Popp 
275b0750e2dSTres Popp     if (op->getAttr("legalizer.erase_old_blocks")) {
276b0750e2dSTres Popp       while (!opRegion.empty())
277b0750e2dSTres Popp         rewriter.eraseBlock(&opRegion.front());
278b0750e2dSTres Popp     }
279fec6c5acSUday Bondhugula 
280fec6c5acSUday Bondhugula     // Drop this operation.
281fec6c5acSUday Bondhugula     rewriter.eraseOp(op);
282fec6c5acSUday Bondhugula     return success();
283fec6c5acSUday Bondhugula   }
284fec6c5acSUday Bondhugula };
285fec6c5acSUday Bondhugula /// This pattern is a simple pattern that generates a region containing an
286fec6c5acSUday Bondhugula /// illegal operation.
287fec6c5acSUday Bondhugula struct TestRegionRewriteUndo : public RewritePattern {
288fec6c5acSUday Bondhugula   TestRegionRewriteUndo(MLIRContext *ctx)
289fec6c5acSUday Bondhugula       : RewritePattern("test.region_builder", 1, ctx) {}
290fec6c5acSUday Bondhugula 
291fec6c5acSUday Bondhugula   LogicalResult matchAndRewrite(Operation *op,
292fec6c5acSUday Bondhugula                                 PatternRewriter &rewriter) const final {
293fec6c5acSUday Bondhugula     // Create the region operation with an entry block containing arguments.
294fec6c5acSUday Bondhugula     OperationState newRegion(op->getLoc(), "test.region");
295fec6c5acSUday Bondhugula     newRegion.addRegion();
296fec6c5acSUday Bondhugula     auto *regionOp = rewriter.createOperation(newRegion);
297fec6c5acSUday Bondhugula     auto *entryBlock = rewriter.createBlock(&regionOp->getRegion(0));
298fec6c5acSUday Bondhugula     entryBlock->addArgument(rewriter.getIntegerType(64));
299fec6c5acSUday Bondhugula 
300fec6c5acSUday Bondhugula     // Add an explicitly illegal operation to ensure the conversion fails.
301fec6c5acSUday Bondhugula     rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getIntegerType(32));
302fec6c5acSUday Bondhugula     rewriter.create<TestValidOp>(op->getLoc(), ArrayRef<Value>());
303fec6c5acSUday Bondhugula 
304fec6c5acSUday Bondhugula     // Drop this operation.
305fec6c5acSUday Bondhugula     rewriter.eraseOp(op);
306fec6c5acSUday Bondhugula     return success();
307fec6c5acSUday Bondhugula   }
308fec6c5acSUday Bondhugula };
309f27f1e8cSAlex Zinenko /// A simple pattern that creates a block at the end of the parent region of the
310f27f1e8cSAlex Zinenko /// matched operation.
311f27f1e8cSAlex Zinenko struct TestCreateBlock : public RewritePattern {
312f27f1e8cSAlex Zinenko   TestCreateBlock(MLIRContext *ctx)
313f27f1e8cSAlex Zinenko       : RewritePattern("test.create_block", /*benefit=*/1, ctx) {}
314f27f1e8cSAlex Zinenko 
315f27f1e8cSAlex Zinenko   LogicalResult matchAndRewrite(Operation *op,
316f27f1e8cSAlex Zinenko                                 PatternRewriter &rewriter) const final {
317f27f1e8cSAlex Zinenko     Region &region = *op->getParentRegion();
318f27f1e8cSAlex Zinenko     Type i32Type = rewriter.getIntegerType(32);
319f27f1e8cSAlex Zinenko     rewriter.createBlock(&region, region.end(), {i32Type, i32Type});
320f27f1e8cSAlex Zinenko     rewriter.create<TerminatorOp>(op->getLoc());
321f27f1e8cSAlex Zinenko     rewriter.replaceOp(op, {});
322f27f1e8cSAlex Zinenko     return success();
323f27f1e8cSAlex Zinenko   }
324f27f1e8cSAlex Zinenko };
325f27f1e8cSAlex Zinenko 
326a23d0559SKazuaki Ishizaki /// A simple pattern that creates a block containing an invalid operation in
327f27f1e8cSAlex Zinenko /// order to trigger the block creation undo mechanism.
328f27f1e8cSAlex Zinenko struct TestCreateIllegalBlock : public RewritePattern {
329f27f1e8cSAlex Zinenko   TestCreateIllegalBlock(MLIRContext *ctx)
330f27f1e8cSAlex Zinenko       : RewritePattern("test.create_illegal_block", /*benefit=*/1, ctx) {}
331f27f1e8cSAlex Zinenko 
332f27f1e8cSAlex Zinenko   LogicalResult matchAndRewrite(Operation *op,
333f27f1e8cSAlex Zinenko                                 PatternRewriter &rewriter) const final {
334f27f1e8cSAlex Zinenko     Region &region = *op->getParentRegion();
335f27f1e8cSAlex Zinenko     Type i32Type = rewriter.getIntegerType(32);
336f27f1e8cSAlex Zinenko     rewriter.createBlock(&region, region.end(), {i32Type, i32Type});
337f27f1e8cSAlex Zinenko     // Create an illegal op to ensure the conversion fails.
338f27f1e8cSAlex Zinenko     rewriter.create<ILLegalOpF>(op->getLoc(), i32Type);
339f27f1e8cSAlex Zinenko     rewriter.create<TerminatorOp>(op->getLoc());
340f27f1e8cSAlex Zinenko     rewriter.replaceOp(op, {});
341f27f1e8cSAlex Zinenko     return success();
342f27f1e8cSAlex Zinenko   }
343f27f1e8cSAlex Zinenko };
344fec6c5acSUday Bondhugula 
3450816de16SRiver Riddle /// A simple pattern that tests the undo mechanism when replacing the uses of a
3460816de16SRiver Riddle /// block argument.
3470816de16SRiver Riddle struct TestUndoBlockArgReplace : public ConversionPattern {
3480816de16SRiver Riddle   TestUndoBlockArgReplace(MLIRContext *ctx)
3490816de16SRiver Riddle       : ConversionPattern("test.undo_block_arg_replace", /*benefit=*/1, ctx) {}
3500816de16SRiver Riddle 
3510816de16SRiver Riddle   LogicalResult
3520816de16SRiver Riddle   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
3530816de16SRiver Riddle                   ConversionPatternRewriter &rewriter) const final {
3540816de16SRiver Riddle     auto illegalOp =
3550816de16SRiver Riddle         rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getF32Type());
356e2b71610SRahul Joshi     rewriter.replaceUsesOfBlockArgument(op->getRegion(0).getArgument(0),
3570816de16SRiver Riddle                                         illegalOp);
3580816de16SRiver Riddle     rewriter.updateRootInPlace(op, [] {});
3590816de16SRiver Riddle     return success();
3600816de16SRiver Riddle   }
3610816de16SRiver Riddle };
3620816de16SRiver Riddle 
363df48026bSAlex Zinenko /// A rewrite pattern that tests the undo mechanism when erasing a block.
364df48026bSAlex Zinenko struct TestUndoBlockErase : public ConversionPattern {
365df48026bSAlex Zinenko   TestUndoBlockErase(MLIRContext *ctx)
366df48026bSAlex Zinenko       : ConversionPattern("test.undo_block_erase", /*benefit=*/1, ctx) {}
367df48026bSAlex Zinenko 
368df48026bSAlex Zinenko   LogicalResult
369df48026bSAlex Zinenko   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
370df48026bSAlex Zinenko                   ConversionPatternRewriter &rewriter) const final {
371df48026bSAlex Zinenko     Block *secondBlock = &*std::next(op->getRegion(0).begin());
372df48026bSAlex Zinenko     rewriter.setInsertionPointToStart(secondBlock);
373df48026bSAlex Zinenko     rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getF32Type());
374df48026bSAlex Zinenko     rewriter.eraseBlock(secondBlock);
375df48026bSAlex Zinenko     rewriter.updateRootInPlace(op, [] {});
376df48026bSAlex Zinenko     return success();
377df48026bSAlex Zinenko   }
378df48026bSAlex Zinenko };
379df48026bSAlex Zinenko 
380fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
381fec6c5acSUday Bondhugula // Type-Conversion Rewrite Testing
382fec6c5acSUday Bondhugula 
383fec6c5acSUday Bondhugula /// This patterns erases a region operation that has had a type conversion.
384fec6c5acSUday Bondhugula struct TestDropOpSignatureConversion : public ConversionPattern {
385fec6c5acSUday Bondhugula   TestDropOpSignatureConversion(MLIRContext *ctx, TypeConverter &converter)
38676f3c2f3SRiver Riddle       : ConversionPattern(converter, "test.drop_region_op", 1, ctx) {}
387fec6c5acSUday Bondhugula   LogicalResult
388fec6c5acSUday Bondhugula   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
389fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const override {
390fec6c5acSUday Bondhugula     Region &region = op->getRegion(0);
391fec6c5acSUday Bondhugula     Block *entry = &region.front();
392fec6c5acSUday Bondhugula 
393fec6c5acSUday Bondhugula     // Convert the original entry arguments.
3948d67d187SRiver Riddle     TypeConverter &converter = *getTypeConverter();
395fec6c5acSUday Bondhugula     TypeConverter::SignatureConversion result(entry->getNumArguments());
3968d67d187SRiver Riddle     if (failed(converter.convertSignatureArgs(entry->getArgumentTypes(),
3978d67d187SRiver Riddle                                               result)) ||
3988d67d187SRiver Riddle         failed(rewriter.convertRegionTypes(&region, converter, &result)))
399fec6c5acSUday Bondhugula       return failure();
400fec6c5acSUday Bondhugula 
401fec6c5acSUday Bondhugula     // Convert the region signature and just drop the operation.
402fec6c5acSUday Bondhugula     rewriter.eraseOp(op);
403fec6c5acSUday Bondhugula     return success();
404fec6c5acSUday Bondhugula   }
405fec6c5acSUday Bondhugula };
406fec6c5acSUday Bondhugula /// This pattern simply updates the operands of the given operation.
407fec6c5acSUday Bondhugula struct TestPassthroughInvalidOp : public ConversionPattern {
408fec6c5acSUday Bondhugula   TestPassthroughInvalidOp(MLIRContext *ctx)
409fec6c5acSUday Bondhugula       : ConversionPattern("test.invalid", 1, ctx) {}
410fec6c5acSUday Bondhugula   LogicalResult
411fec6c5acSUday Bondhugula   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
412fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const final {
413fec6c5acSUday Bondhugula     rewriter.replaceOpWithNewOp<TestValidOp>(op, llvm::None, operands,
414fec6c5acSUday Bondhugula                                              llvm::None);
415fec6c5acSUday Bondhugula     return success();
416fec6c5acSUday Bondhugula   }
417fec6c5acSUday Bondhugula };
418fec6c5acSUday Bondhugula /// This pattern handles the case of a split return value.
419fec6c5acSUday Bondhugula struct TestSplitReturnType : public ConversionPattern {
420fec6c5acSUday Bondhugula   TestSplitReturnType(MLIRContext *ctx)
421fec6c5acSUday Bondhugula       : ConversionPattern("test.return", 1, ctx) {}
422fec6c5acSUday Bondhugula   LogicalResult
423fec6c5acSUday Bondhugula   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
424fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const final {
425fec6c5acSUday Bondhugula     // Check for a return of F32.
426fec6c5acSUday Bondhugula     if (op->getNumOperands() != 1 || !op->getOperand(0).getType().isF32())
427fec6c5acSUday Bondhugula       return failure();
428fec6c5acSUday Bondhugula 
429fec6c5acSUday Bondhugula     // Check if the first operation is a cast operation, if it is we use the
430fec6c5acSUday Bondhugula     // results directly.
431fec6c5acSUday Bondhugula     auto *defOp = operands[0].getDefiningOp();
432fec6c5acSUday Bondhugula     if (auto packerOp = llvm::dyn_cast_or_null<TestCastOp>(defOp)) {
433fec6c5acSUday Bondhugula       rewriter.replaceOpWithNewOp<TestReturnOp>(op, packerOp.getOperands());
434fec6c5acSUday Bondhugula       return success();
435fec6c5acSUday Bondhugula     }
436fec6c5acSUday Bondhugula 
437fec6c5acSUday Bondhugula     // Otherwise, fail to match.
438fec6c5acSUday Bondhugula     return failure();
439fec6c5acSUday Bondhugula   }
440fec6c5acSUday Bondhugula };
441fec6c5acSUday Bondhugula 
442fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
443fec6c5acSUday Bondhugula // Multi-Level Type-Conversion Rewrite Testing
444fec6c5acSUday Bondhugula struct TestChangeProducerTypeI32ToF32 : public ConversionPattern {
445fec6c5acSUday Bondhugula   TestChangeProducerTypeI32ToF32(MLIRContext *ctx)
446fec6c5acSUday Bondhugula       : ConversionPattern("test.type_producer", 1, ctx) {}
447fec6c5acSUday Bondhugula   LogicalResult
448fec6c5acSUday Bondhugula   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
449fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const final {
450fec6c5acSUday Bondhugula     // If the type is I32, change the type to F32.
451fec6c5acSUday Bondhugula     if (!Type(*op->result_type_begin()).isSignlessInteger(32))
452fec6c5acSUday Bondhugula       return failure();
453fec6c5acSUday Bondhugula     rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getF32Type());
454fec6c5acSUday Bondhugula     return success();
455fec6c5acSUday Bondhugula   }
456fec6c5acSUday Bondhugula };
457fec6c5acSUday Bondhugula struct TestChangeProducerTypeF32ToF64 : public ConversionPattern {
458fec6c5acSUday Bondhugula   TestChangeProducerTypeF32ToF64(MLIRContext *ctx)
459fec6c5acSUday Bondhugula       : ConversionPattern("test.type_producer", 1, ctx) {}
460fec6c5acSUday Bondhugula   LogicalResult
461fec6c5acSUday Bondhugula   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
462fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const final {
463fec6c5acSUday Bondhugula     // If the type is F32, change the type to F64.
464fec6c5acSUday Bondhugula     if (!Type(*op->result_type_begin()).isF32())
465fec6c5acSUday Bondhugula       return rewriter.notifyMatchFailure(op, "expected single f32 operand");
466fec6c5acSUday Bondhugula     rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getF64Type());
467fec6c5acSUday Bondhugula     return success();
468fec6c5acSUday Bondhugula   }
469fec6c5acSUday Bondhugula };
470fec6c5acSUday Bondhugula struct TestChangeProducerTypeF32ToInvalid : public ConversionPattern {
471fec6c5acSUday Bondhugula   TestChangeProducerTypeF32ToInvalid(MLIRContext *ctx)
472fec6c5acSUday Bondhugula       : ConversionPattern("test.type_producer", 10, ctx) {}
473fec6c5acSUday Bondhugula   LogicalResult
474fec6c5acSUday Bondhugula   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
475fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const final {
476fec6c5acSUday Bondhugula     // Always convert to B16, even though it is not a legal type. This tests
477fec6c5acSUday Bondhugula     // that values are unmapped correctly.
478fec6c5acSUday Bondhugula     rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getBF16Type());
479fec6c5acSUday Bondhugula     return success();
480fec6c5acSUday Bondhugula   }
481fec6c5acSUday Bondhugula };
482fec6c5acSUday Bondhugula struct TestUpdateConsumerType : public ConversionPattern {
483fec6c5acSUday Bondhugula   TestUpdateConsumerType(MLIRContext *ctx)
484fec6c5acSUday Bondhugula       : ConversionPattern("test.type_consumer", 1, ctx) {}
485fec6c5acSUday Bondhugula   LogicalResult
486fec6c5acSUday Bondhugula   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
487fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const final {
488fec6c5acSUday Bondhugula     // Verify that the incoming operand has been successfully remapped to F64.
489fec6c5acSUday Bondhugula     if (!operands[0].getType().isF64())
490fec6c5acSUday Bondhugula       return failure();
491fec6c5acSUday Bondhugula     rewriter.replaceOpWithNewOp<TestTypeConsumerOp>(op, operands[0]);
492fec6c5acSUday Bondhugula     return success();
493fec6c5acSUday Bondhugula   }
494fec6c5acSUday Bondhugula };
495fec6c5acSUday Bondhugula 
496fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
497fec6c5acSUday Bondhugula // Non-Root Replacement Rewrite Testing
498fec6c5acSUday Bondhugula /// This pattern generates an invalid operation, but replaces it before the
499fec6c5acSUday Bondhugula /// pattern is finished. This checks that we don't need to legalize the
500fec6c5acSUday Bondhugula /// temporary op.
501fec6c5acSUday Bondhugula struct TestNonRootReplacement : public RewritePattern {
502fec6c5acSUday Bondhugula   TestNonRootReplacement(MLIRContext *ctx)
503fec6c5acSUday Bondhugula       : RewritePattern("test.replace_non_root", 1, ctx) {}
504fec6c5acSUday Bondhugula 
505fec6c5acSUday Bondhugula   LogicalResult matchAndRewrite(Operation *op,
506fec6c5acSUday Bondhugula                                 PatternRewriter &rewriter) const final {
507fec6c5acSUday Bondhugula     auto resultType = *op->result_type_begin();
508fec6c5acSUday Bondhugula     auto illegalOp = rewriter.create<ILLegalOpF>(op->getLoc(), resultType);
509fec6c5acSUday Bondhugula     auto legalOp = rewriter.create<LegalOpB>(op->getLoc(), resultType);
510fec6c5acSUday Bondhugula 
511fec6c5acSUday Bondhugula     rewriter.replaceOp(illegalOp, {legalOp});
512fec6c5acSUday Bondhugula     rewriter.replaceOp(op, {illegalOp});
513fec6c5acSUday Bondhugula     return success();
514fec6c5acSUday Bondhugula   }
515fec6c5acSUday Bondhugula };
516bd1ccfe6SRiver Riddle 
517bd1ccfe6SRiver Riddle //===----------------------------------------------------------------------===//
518bd1ccfe6SRiver Riddle // Recursive Rewrite Testing
519bd1ccfe6SRiver Riddle /// This pattern is applied to the same operation multiple times, but has a
520bd1ccfe6SRiver Riddle /// bounded recursion.
521bd1ccfe6SRiver Riddle struct TestBoundedRecursiveRewrite
522bd1ccfe6SRiver Riddle     : public OpRewritePattern<TestRecursiveRewriteOp> {
5232257e4a7SRiver Riddle   using OpRewritePattern<TestRecursiveRewriteOp>::OpRewritePattern;
5242257e4a7SRiver Riddle 
5252257e4a7SRiver Riddle   void initialize() {
526b99bd771SRiver Riddle     // The conversion target handles bounding the recursion of this pattern.
527b99bd771SRiver Riddle     setHasBoundedRewriteRecursion();
528b99bd771SRiver Riddle   }
529bd1ccfe6SRiver Riddle 
530bd1ccfe6SRiver Riddle   LogicalResult matchAndRewrite(TestRecursiveRewriteOp op,
531bd1ccfe6SRiver Riddle                                 PatternRewriter &rewriter) const final {
532bd1ccfe6SRiver Riddle     // Decrement the depth of the op in-place.
533bd1ccfe6SRiver Riddle     rewriter.updateRootInPlace(op, [&] {
5341ffc1aaaSChristian Sigg       op->setAttr("depth", rewriter.getI64IntegerAttr(op.depth() - 1));
535bd1ccfe6SRiver Riddle     });
536bd1ccfe6SRiver Riddle     return success();
537bd1ccfe6SRiver Riddle   }
538bd1ccfe6SRiver Riddle };
5395d5df06aSAlex Zinenko 
5405d5df06aSAlex Zinenko struct TestNestedOpCreationUndoRewrite
5415d5df06aSAlex Zinenko     : public OpRewritePattern<IllegalOpWithRegionAnchor> {
5425d5df06aSAlex Zinenko   using OpRewritePattern<IllegalOpWithRegionAnchor>::OpRewritePattern;
5435d5df06aSAlex Zinenko 
5445d5df06aSAlex Zinenko   LogicalResult matchAndRewrite(IllegalOpWithRegionAnchor op,
5455d5df06aSAlex Zinenko                                 PatternRewriter &rewriter) const final {
5465d5df06aSAlex Zinenko     // rewriter.replaceOpWithNewOp<IllegalOpWithRegion>(op);
5475d5df06aSAlex Zinenko     rewriter.replaceOpWithNewOp<IllegalOpWithRegion>(op);
5485d5df06aSAlex Zinenko     return success();
5495d5df06aSAlex Zinenko   };
5505d5df06aSAlex Zinenko };
551a360a978SMehdi Amini 
552a360a978SMehdi Amini // This pattern matches `test.blackhole` and delete this op and its producer.
553a360a978SMehdi Amini struct TestReplaceEraseOp : public OpRewritePattern<BlackHoleOp> {
554a360a978SMehdi Amini   using OpRewritePattern<BlackHoleOp>::OpRewritePattern;
555a360a978SMehdi Amini 
556a360a978SMehdi Amini   LogicalResult matchAndRewrite(BlackHoleOp op,
557a360a978SMehdi Amini                                 PatternRewriter &rewriter) const final {
558a360a978SMehdi Amini     Operation *producer = op.getOperand().getDefiningOp();
559a360a978SMehdi Amini     // Always erase the user before the producer, the framework should handle
560a360a978SMehdi Amini     // this correctly.
561a360a978SMehdi Amini     rewriter.eraseOp(op);
562a360a978SMehdi Amini     rewriter.eraseOp(producer);
563a360a978SMehdi Amini     return success();
564a360a978SMehdi Amini   };
565a360a978SMehdi Amini };
566ec03bbe8SVladislav Vinogradov 
567ec03bbe8SVladislav Vinogradov // This pattern replaces explicitly illegal op with explicitly legal op,
568ec03bbe8SVladislav Vinogradov // but in addition creates unregistered operation.
569ec03bbe8SVladislav Vinogradov struct TestCreateUnregisteredOp : public OpRewritePattern<ILLegalOpG> {
570ec03bbe8SVladislav Vinogradov   using OpRewritePattern<ILLegalOpG>::OpRewritePattern;
571ec03bbe8SVladislav Vinogradov 
572ec03bbe8SVladislav Vinogradov   LogicalResult matchAndRewrite(ILLegalOpG op,
573ec03bbe8SVladislav Vinogradov                                 PatternRewriter &rewriter) const final {
574ec03bbe8SVladislav Vinogradov     IntegerAttr attr = rewriter.getI32IntegerAttr(0);
575ec03bbe8SVladislav Vinogradov     Value val = rewriter.create<ConstantOp>(op->getLoc(), attr);
576ec03bbe8SVladislav Vinogradov     rewriter.replaceOpWithNewOp<LegalOpC>(op, val);
577ec03bbe8SVladislav Vinogradov     return success();
578ec03bbe8SVladislav Vinogradov   };
579ec03bbe8SVladislav Vinogradov };
580fec6c5acSUday Bondhugula } // namespace
581fec6c5acSUday Bondhugula 
582fec6c5acSUday Bondhugula namespace {
583fec6c5acSUday Bondhugula struct TestTypeConverter : public TypeConverter {
584fec6c5acSUday Bondhugula   using TypeConverter::TypeConverter;
5855c5dafc5SAlex Zinenko   TestTypeConverter() {
5865c5dafc5SAlex Zinenko     addConversion(convertType);
5874589dd92SRiver Riddle     addArgumentMaterialization(materializeCast);
5884589dd92SRiver Riddle     addSourceMaterialization(materializeCast);
5897cc79984SVladislav Vinogradov 
5907cc79984SVladislav Vinogradov     /// Materialize the cast for one-to-one conversion from i64 to f64.
5917cc79984SVladislav Vinogradov     const auto materializeOneToOneCast =
5927cc79984SVladislav Vinogradov         [](OpBuilder &builder, IntegerType resultType, ValueRange inputs,
5937cc79984SVladislav Vinogradov            Location loc) -> Optional<Value> {
5947cc79984SVladislav Vinogradov       if (resultType.getWidth() == 42 && inputs.size() == 1)
5957cc79984SVladislav Vinogradov         return builder.create<TestCastOp>(loc, resultType, inputs).getResult();
5967cc79984SVladislav Vinogradov       return llvm::None;
5977cc79984SVladislav Vinogradov     };
5987cc79984SVladislav Vinogradov     addArgumentMaterialization(materializeOneToOneCast);
5995c5dafc5SAlex Zinenko   }
600fec6c5acSUday Bondhugula 
601fec6c5acSUday Bondhugula   static LogicalResult convertType(Type t, SmallVectorImpl<Type> &results) {
602fec6c5acSUday Bondhugula     // Drop I16 types.
603fec6c5acSUday Bondhugula     if (t.isSignlessInteger(16))
604fec6c5acSUday Bondhugula       return success();
605fec6c5acSUday Bondhugula 
606fec6c5acSUday Bondhugula     // Convert I64 to F64.
607fec6c5acSUday Bondhugula     if (t.isSignlessInteger(64)) {
608fec6c5acSUday Bondhugula       results.push_back(FloatType::getF64(t.getContext()));
609fec6c5acSUday Bondhugula       return success();
610fec6c5acSUday Bondhugula     }
611fec6c5acSUday Bondhugula 
6125c5dafc5SAlex Zinenko     // Convert I42 to I43.
6135c5dafc5SAlex Zinenko     if (t.isInteger(42)) {
6141b97cdf8SRiver Riddle       results.push_back(IntegerType::get(t.getContext(), 43));
6155c5dafc5SAlex Zinenko       return success();
6165c5dafc5SAlex Zinenko     }
6175c5dafc5SAlex Zinenko 
618fec6c5acSUday Bondhugula     // Split F32 into F16,F16.
619fec6c5acSUday Bondhugula     if (t.isF32()) {
620fec6c5acSUday Bondhugula       results.assign(2, FloatType::getF16(t.getContext()));
621fec6c5acSUday Bondhugula       return success();
622fec6c5acSUday Bondhugula     }
623fec6c5acSUday Bondhugula 
624fec6c5acSUday Bondhugula     // Otherwise, convert the type directly.
625fec6c5acSUday Bondhugula     results.push_back(t);
626fec6c5acSUday Bondhugula     return success();
627fec6c5acSUday Bondhugula   }
628fec6c5acSUday Bondhugula 
6295c5dafc5SAlex Zinenko   /// Hook for materializing a conversion. This is necessary because we generate
6305c5dafc5SAlex Zinenko   /// 1->N type mappings.
6314589dd92SRiver Riddle   static Optional<Value> materializeCast(OpBuilder &builder, Type resultType,
6324589dd92SRiver Riddle                                          ValueRange inputs, Location loc) {
6335c5dafc5SAlex Zinenko     if (inputs.size() == 1)
6345c5dafc5SAlex Zinenko       return inputs[0];
6354589dd92SRiver Riddle     return builder.create<TestCastOp>(loc, resultType, inputs).getResult();
6365c5dafc5SAlex Zinenko   }
637fec6c5acSUday Bondhugula };
638fec6c5acSUday Bondhugula 
639fec6c5acSUday Bondhugula struct TestLegalizePatternDriver
64080aca1eaSRiver Riddle     : public PassWrapper<TestLegalizePatternDriver, OperationPass<ModuleOp>> {
641b5e22e6dSMehdi Amini   StringRef getArgument() const final { return "test-legalize-patterns"; }
642b5e22e6dSMehdi Amini   StringRef getDescription() const final {
643b5e22e6dSMehdi Amini     return "Run test dialect legalization patterns";
644b5e22e6dSMehdi Amini   }
645fec6c5acSUday Bondhugula   /// The mode of conversion to use with the driver.
646fec6c5acSUday Bondhugula   enum class ConversionMode { Analysis, Full, Partial };
647fec6c5acSUday Bondhugula 
648fec6c5acSUday Bondhugula   TestLegalizePatternDriver(ConversionMode mode) : mode(mode) {}
649fec6c5acSUday Bondhugula 
650ec03bbe8SVladislav Vinogradov   void getDependentDialects(DialectRegistry &registry) const override {
651ec03bbe8SVladislav Vinogradov     registry.insert<StandardOpsDialect>();
652ec03bbe8SVladislav Vinogradov   }
653ec03bbe8SVladislav Vinogradov 
654722f909fSRiver Riddle   void runOnOperation() override {
655fec6c5acSUday Bondhugula     TestTypeConverter converter;
656dc4e913bSChris Lattner     mlir::RewritePatternSet patterns(&getContext());
6571d909c9aSChris Lattner     populateWithGenerated(patterns);
658dc4e913bSChris Lattner     patterns
659dc4e913bSChris Lattner         .add<TestRegionRewriteBlockMovement, TestRegionRewriteUndo,
660dc4e913bSChris Lattner              TestCreateBlock, TestCreateIllegalBlock, TestUndoBlockArgReplace,
661dc4e913bSChris Lattner              TestUndoBlockErase, TestPassthroughInvalidOp, TestSplitReturnType,
662df48026bSAlex Zinenko              TestChangeProducerTypeI32ToF32, TestChangeProducerTypeF32ToF64,
663fec6c5acSUday Bondhugula              TestChangeProducerTypeF32ToInvalid, TestUpdateConsumerType,
6645d5df06aSAlex Zinenko              TestNonRootReplacement, TestBoundedRecursiveRewrite,
665ec03bbe8SVladislav Vinogradov              TestNestedOpCreationUndoRewrite, TestReplaceEraseOp,
666ec03bbe8SVladislav Vinogradov              TestCreateUnregisteredOp>(&getContext());
667dc4e913bSChris Lattner     patterns.add<TestDropOpSignatureConversion>(&getContext(), converter);
6683a506b31SChris Lattner     mlir::populateFuncOpTypeConversionPattern(patterns, converter);
6693a506b31SChris Lattner     mlir::populateCallOpTypeConversionPattern(patterns, converter);
670fec6c5acSUday Bondhugula 
671fec6c5acSUday Bondhugula     // Define the conversion target used for the test.
672fec6c5acSUday Bondhugula     ConversionTarget target(getContext());
673973ddb7dSMehdi Amini     target.addLegalOp<ModuleOp>();
674ec03bbe8SVladislav Vinogradov     target.addLegalOp<LegalOpA, LegalOpB, LegalOpC, TestCastOp, TestValidOp,
675f27f1e8cSAlex Zinenko                       TerminatorOp>();
676fec6c5acSUday Bondhugula     target
677fec6c5acSUday Bondhugula         .addIllegalOp<ILLegalOpF, TestRegionBuilderOp, TestOpWithRegionFold>();
678fec6c5acSUday Bondhugula     target.addDynamicallyLegalOp<TestReturnOp>([](TestReturnOp op) {
679fec6c5acSUday Bondhugula       // Don't allow F32 operands.
680fec6c5acSUday Bondhugula       return llvm::none_of(op.getOperandTypes(),
681fec6c5acSUday Bondhugula                            [](Type type) { return type.isF32(); });
682fec6c5acSUday Bondhugula     });
6838d67d187SRiver Riddle     target.addDynamicallyLegalOp<FuncOp>([&](FuncOp op) {
6848d67d187SRiver Riddle       return converter.isSignatureLegal(op.getType()) &&
6858d67d187SRiver Riddle              converter.isLegal(&op.getBody());
6868d67d187SRiver Riddle     });
687fec6c5acSUday Bondhugula 
688*a54f4eaeSMogball     // TestCreateUnregisteredOp creates `arith.constant` operation,
689ec03bbe8SVladislav Vinogradov     // which was not added to target intentionally to test
690ec03bbe8SVladislav Vinogradov     // correct error code from conversion driver.
691ec03bbe8SVladislav Vinogradov     target.addDynamicallyLegalOp<ILLegalOpG>([](ILLegalOpG) { return false; });
692ec03bbe8SVladislav Vinogradov 
693fec6c5acSUday Bondhugula     // Expect the type_producer/type_consumer operations to only operate on f64.
694fec6c5acSUday Bondhugula     target.addDynamicallyLegalOp<TestTypeProducerOp>(
695fec6c5acSUday Bondhugula         [](TestTypeProducerOp op) { return op.getType().isF64(); });
696fec6c5acSUday Bondhugula     target.addDynamicallyLegalOp<TestTypeConsumerOp>([](TestTypeConsumerOp op) {
697fec6c5acSUday Bondhugula       return op.getOperand().getType().isF64();
698fec6c5acSUday Bondhugula     });
699fec6c5acSUday Bondhugula 
700fec6c5acSUday Bondhugula     // Check support for marking certain operations as recursively legal.
701fec6c5acSUday Bondhugula     target.markOpRecursivelyLegal<FuncOp, ModuleOp>([](Operation *op) {
702fec6c5acSUday Bondhugula       return static_cast<bool>(
703fec6c5acSUday Bondhugula           op->getAttrOfType<UnitAttr>("test.recursively_legal"));
704fec6c5acSUday Bondhugula     });
705fec6c5acSUday Bondhugula 
706bd1ccfe6SRiver Riddle     // Mark the bound recursion operation as dynamically legal.
707bd1ccfe6SRiver Riddle     target.addDynamicallyLegalOp<TestRecursiveRewriteOp>(
708bd1ccfe6SRiver Riddle         [](TestRecursiveRewriteOp op) { return op.depth() == 0; });
709bd1ccfe6SRiver Riddle 
710fec6c5acSUday Bondhugula     // Handle a partial conversion.
711fec6c5acSUday Bondhugula     if (mode == ConversionMode::Partial) {
7128de482eaSLucy Fox       DenseSet<Operation *> unlegalizedOps;
713ec03bbe8SVladislav Vinogradov       if (failed(applyPartialConversion(
714ec03bbe8SVladislav Vinogradov               getOperation(), target, std::move(patterns), &unlegalizedOps))) {
715ec03bbe8SVladislav Vinogradov         getOperation()->emitRemark() << "applyPartialConversion failed";
716ec03bbe8SVladislav Vinogradov       }
7178de482eaSLucy Fox       // Emit remarks for each legalizable operation.
7188de482eaSLucy Fox       for (auto *op : unlegalizedOps)
7198de482eaSLucy Fox         op->emitRemark() << "op '" << op->getName() << "' is not legalizable";
720fec6c5acSUday Bondhugula       return;
721fec6c5acSUday Bondhugula     }
722fec6c5acSUday Bondhugula 
723fec6c5acSUday Bondhugula     // Handle a full conversion.
724fec6c5acSUday Bondhugula     if (mode == ConversionMode::Full) {
725fec6c5acSUday Bondhugula       // Check support for marking unknown operations as dynamically legal.
726fec6c5acSUday Bondhugula       target.markUnknownOpDynamicallyLegal([](Operation *op) {
727fec6c5acSUday Bondhugula         return (bool)op->getAttrOfType<UnitAttr>("test.dynamically_legal");
728fec6c5acSUday Bondhugula       });
729fec6c5acSUday Bondhugula 
730ec03bbe8SVladislav Vinogradov       if (failed(applyFullConversion(getOperation(), target,
731ec03bbe8SVladislav Vinogradov                                      std::move(patterns)))) {
732ec03bbe8SVladislav Vinogradov         getOperation()->emitRemark() << "applyFullConversion failed";
733ec03bbe8SVladislav Vinogradov       }
734fec6c5acSUday Bondhugula       return;
735fec6c5acSUday Bondhugula     }
736fec6c5acSUday Bondhugula 
737fec6c5acSUday Bondhugula     // Otherwise, handle an analysis conversion.
738fec6c5acSUday Bondhugula     assert(mode == ConversionMode::Analysis);
739fec6c5acSUday Bondhugula 
740fec6c5acSUday Bondhugula     // Analyze the convertible operations.
741fec6c5acSUday Bondhugula     DenseSet<Operation *> legalizedOps;
7423fffffa8SRiver Riddle     if (failed(applyAnalysisConversion(getOperation(), target,
7433fffffa8SRiver Riddle                                        std::move(patterns), legalizedOps)))
744fec6c5acSUday Bondhugula       return signalPassFailure();
745fec6c5acSUday Bondhugula 
746fec6c5acSUday Bondhugula     // Emit remarks for each legalizable operation.
747fec6c5acSUday Bondhugula     for (auto *op : legalizedOps)
748fec6c5acSUday Bondhugula       op->emitRemark() << "op '" << op->getName() << "' is legalizable";
749fec6c5acSUday Bondhugula   }
750fec6c5acSUday Bondhugula 
751fec6c5acSUday Bondhugula   /// The mode of conversion to use.
752fec6c5acSUday Bondhugula   ConversionMode mode;
753fec6c5acSUday Bondhugula };
754fec6c5acSUday Bondhugula } // end anonymous namespace
755fec6c5acSUday Bondhugula 
756fec6c5acSUday Bondhugula static llvm::cl::opt<TestLegalizePatternDriver::ConversionMode>
757fec6c5acSUday Bondhugula     legalizerConversionMode(
758fec6c5acSUday Bondhugula         "test-legalize-mode",
759fec6c5acSUday Bondhugula         llvm::cl::desc("The legalization mode to use with the test driver"),
760fec6c5acSUday Bondhugula         llvm::cl::init(TestLegalizePatternDriver::ConversionMode::Partial),
761fec6c5acSUday Bondhugula         llvm::cl::values(
762fec6c5acSUday Bondhugula             clEnumValN(TestLegalizePatternDriver::ConversionMode::Analysis,
763fec6c5acSUday Bondhugula                        "analysis", "Perform an analysis conversion"),
764fec6c5acSUday Bondhugula             clEnumValN(TestLegalizePatternDriver::ConversionMode::Full, "full",
765fec6c5acSUday Bondhugula                        "Perform a full conversion"),
766fec6c5acSUday Bondhugula             clEnumValN(TestLegalizePatternDriver::ConversionMode::Partial,
767fec6c5acSUday Bondhugula                        "partial", "Perform a partial conversion")));
768fec6c5acSUday Bondhugula 
769fec6c5acSUday Bondhugula //===----------------------------------------------------------------------===//
770fec6c5acSUday Bondhugula // ConversionPatternRewriter::getRemappedValue testing. This method is used
7715aacce3dSKazuaki Ishizaki // to get the remapped value of an original value that was replaced using
772fec6c5acSUday Bondhugula // ConversionPatternRewriter.
773fec6c5acSUday Bondhugula namespace {
774fec6c5acSUday Bondhugula /// Converter that replaces a one-result one-operand OneVResOneVOperandOp1 with
775fec6c5acSUday Bondhugula /// a one-operand two-result OneVResOneVOperandOp1 by replicating its original
776fec6c5acSUday Bondhugula /// operand twice.
777fec6c5acSUday Bondhugula ///
778fec6c5acSUday Bondhugula /// Example:
779fec6c5acSUday Bondhugula ///   %1 = test.one_variadic_out_one_variadic_in1"(%0)
780fec6c5acSUday Bondhugula /// is replaced with:
781fec6c5acSUday Bondhugula ///   %1 = test.one_variadic_out_one_variadic_in1"(%0, %0)
782fec6c5acSUday Bondhugula struct OneVResOneVOperandOp1Converter
783fec6c5acSUday Bondhugula     : public OpConversionPattern<OneVResOneVOperandOp1> {
784fec6c5acSUday Bondhugula   using OpConversionPattern<OneVResOneVOperandOp1>::OpConversionPattern;
785fec6c5acSUday Bondhugula 
786fec6c5acSUday Bondhugula   LogicalResult
787ef976337SRiver Riddle   matchAndRewrite(OneVResOneVOperandOp1 op, OpAdaptor adaptor,
788fec6c5acSUday Bondhugula                   ConversionPatternRewriter &rewriter) const override {
789fec6c5acSUday Bondhugula     auto origOps = op.getOperands();
790fec6c5acSUday Bondhugula     assert(std::distance(origOps.begin(), origOps.end()) == 1 &&
791fec6c5acSUday Bondhugula            "One operand expected");
792fec6c5acSUday Bondhugula     Value origOp = *origOps.begin();
793fec6c5acSUday Bondhugula     SmallVector<Value, 2> remappedOperands;
794fec6c5acSUday Bondhugula     // Replicate the remapped original operand twice. Note that we don't used
795fec6c5acSUday Bondhugula     // the remapped 'operand' since the goal is testing 'getRemappedValue'.
796fec6c5acSUday Bondhugula     remappedOperands.push_back(rewriter.getRemappedValue(origOp));
797fec6c5acSUday Bondhugula     remappedOperands.push_back(rewriter.getRemappedValue(origOp));
798fec6c5acSUday Bondhugula 
799fec6c5acSUday Bondhugula     rewriter.replaceOpWithNewOp<OneVResOneVOperandOp1>(op, op.getResultTypes(),
800fec6c5acSUday Bondhugula                                                        remappedOperands);
801fec6c5acSUday Bondhugula     return success();
802fec6c5acSUday Bondhugula   }
803fec6c5acSUday Bondhugula };
804fec6c5acSUday Bondhugula 
80580aca1eaSRiver Riddle struct TestRemappedValue
80680aca1eaSRiver Riddle     : public mlir::PassWrapper<TestRemappedValue, FunctionPass> {
807b5e22e6dSMehdi Amini   StringRef getArgument() const final { return "test-remapped-value"; }
808b5e22e6dSMehdi Amini   StringRef getDescription() const final {
809b5e22e6dSMehdi Amini     return "Test public remapped value mechanism in ConversionPatternRewriter";
810b5e22e6dSMehdi Amini   }
811fec6c5acSUday Bondhugula   void runOnFunction() override {
812dc4e913bSChris Lattner     mlir::RewritePatternSet patterns(&getContext());
813dc4e913bSChris Lattner     patterns.add<OneVResOneVOperandOp1Converter>(&getContext());
814fec6c5acSUday Bondhugula 
815fec6c5acSUday Bondhugula     mlir::ConversionTarget target(getContext());
816973ddb7dSMehdi Amini     target.addLegalOp<ModuleOp, FuncOp, TestReturnOp>();
817fec6c5acSUday Bondhugula     // We make OneVResOneVOperandOp1 legal only when it has more that one
818fec6c5acSUday Bondhugula     // operand. This will trigger the conversion that will replace one-operand
819fec6c5acSUday Bondhugula     // OneVResOneVOperandOp1 with two-operand OneVResOneVOperandOp1.
820fec6c5acSUday Bondhugula     target.addDynamicallyLegalOp<OneVResOneVOperandOp1>(
821fec6c5acSUday Bondhugula         [](Operation *op) -> bool {
822fec6c5acSUday Bondhugula           return std::distance(op->operand_begin(), op->operand_end()) > 1;
823fec6c5acSUday Bondhugula         });
824fec6c5acSUday Bondhugula 
8253fffffa8SRiver Riddle     if (failed(mlir::applyFullConversion(getFunction(), target,
8263fffffa8SRiver Riddle                                          std::move(patterns)))) {
827fec6c5acSUday Bondhugula       signalPassFailure();
828fec6c5acSUday Bondhugula     }
829fec6c5acSUday Bondhugula   }
830fec6c5acSUday Bondhugula };
831fec6c5acSUday Bondhugula } // end anonymous namespace
832fec6c5acSUday Bondhugula 
83380d7ac3bSRiver Riddle //===----------------------------------------------------------------------===//
83480d7ac3bSRiver Riddle // Test patterns without a specific root operation kind
83580d7ac3bSRiver Riddle //===----------------------------------------------------------------------===//
83680d7ac3bSRiver Riddle 
83780d7ac3bSRiver Riddle namespace {
83880d7ac3bSRiver Riddle /// This pattern matches and removes any operation in the test dialect.
83980d7ac3bSRiver Riddle struct RemoveTestDialectOps : public RewritePattern {
84076f3c2f3SRiver Riddle   RemoveTestDialectOps(MLIRContext *context)
84176f3c2f3SRiver Riddle       : RewritePattern(MatchAnyOpTypeTag(), /*benefit=*/1, context) {}
84280d7ac3bSRiver Riddle 
84380d7ac3bSRiver Riddle   LogicalResult matchAndRewrite(Operation *op,
84480d7ac3bSRiver Riddle                                 PatternRewriter &rewriter) const override {
84580d7ac3bSRiver Riddle     if (!isa<TestDialect>(op->getDialect()))
84680d7ac3bSRiver Riddle       return failure();
84780d7ac3bSRiver Riddle     rewriter.eraseOp(op);
84880d7ac3bSRiver Riddle     return success();
84980d7ac3bSRiver Riddle   }
85080d7ac3bSRiver Riddle };
85180d7ac3bSRiver Riddle 
85280d7ac3bSRiver Riddle struct TestUnknownRootOpDriver
85380d7ac3bSRiver Riddle     : public mlir::PassWrapper<TestUnknownRootOpDriver, FunctionPass> {
854b5e22e6dSMehdi Amini   StringRef getArgument() const final {
855b5e22e6dSMehdi Amini     return "test-legalize-unknown-root-patterns";
856b5e22e6dSMehdi Amini   }
857b5e22e6dSMehdi Amini   StringRef getDescription() const final {
858b5e22e6dSMehdi Amini     return "Test public remapped value mechanism in ConversionPatternRewriter";
859b5e22e6dSMehdi Amini   }
86080d7ac3bSRiver Riddle   void runOnFunction() override {
861dc4e913bSChris Lattner     mlir::RewritePatternSet patterns(&getContext());
86276f3c2f3SRiver Riddle     patterns.add<RemoveTestDialectOps>(&getContext());
86380d7ac3bSRiver Riddle 
86480d7ac3bSRiver Riddle     mlir::ConversionTarget target(getContext());
86580d7ac3bSRiver Riddle     target.addIllegalDialect<TestDialect>();
8663fffffa8SRiver Riddle     if (failed(
8673fffffa8SRiver Riddle             applyPartialConversion(getFunction(), target, std::move(patterns))))
86880d7ac3bSRiver Riddle       signalPassFailure();
86980d7ac3bSRiver Riddle   }
87080d7ac3bSRiver Riddle };
87180d7ac3bSRiver Riddle } // end anonymous namespace
87280d7ac3bSRiver Riddle 
8734589dd92SRiver Riddle //===----------------------------------------------------------------------===//
8744589dd92SRiver Riddle // Test type conversions
8754589dd92SRiver Riddle //===----------------------------------------------------------------------===//
8764589dd92SRiver Riddle 
8774589dd92SRiver Riddle namespace {
8784589dd92SRiver Riddle struct TestTypeConversionProducer
8794589dd92SRiver Riddle     : public OpConversionPattern<TestTypeProducerOp> {
8804589dd92SRiver Riddle   using OpConversionPattern<TestTypeProducerOp>::OpConversionPattern;
8814589dd92SRiver Riddle   LogicalResult
882ef976337SRiver Riddle   matchAndRewrite(TestTypeProducerOp op, OpAdaptor adaptor,
8834589dd92SRiver Riddle                   ConversionPatternRewriter &rewriter) const final {
8844589dd92SRiver Riddle     Type resultType = op.getType();
8854589dd92SRiver Riddle     if (resultType.isa<FloatType>())
8864589dd92SRiver Riddle       resultType = rewriter.getF64Type();
8874589dd92SRiver Riddle     else if (resultType.isInteger(16))
8884589dd92SRiver Riddle       resultType = rewriter.getIntegerType(64);
8894589dd92SRiver Riddle     else
8904589dd92SRiver Riddle       return failure();
8914589dd92SRiver Riddle 
8924589dd92SRiver Riddle     rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, resultType);
8934589dd92SRiver Riddle     return success();
8944589dd92SRiver Riddle   }
8954589dd92SRiver Riddle };
8964589dd92SRiver Riddle 
8970409eb28SAlex Zinenko /// Call signature conversion and then fail the rewrite to trigger the undo
8980409eb28SAlex Zinenko /// mechanism.
8990409eb28SAlex Zinenko struct TestSignatureConversionUndo
9000409eb28SAlex Zinenko     : public OpConversionPattern<TestSignatureConversionUndoOp> {
9010409eb28SAlex Zinenko   using OpConversionPattern<TestSignatureConversionUndoOp>::OpConversionPattern;
9020409eb28SAlex Zinenko 
9030409eb28SAlex Zinenko   LogicalResult
904ef976337SRiver Riddle   matchAndRewrite(TestSignatureConversionUndoOp op, OpAdaptor adaptor,
9050409eb28SAlex Zinenko                   ConversionPatternRewriter &rewriter) const final {
9060409eb28SAlex Zinenko     (void)rewriter.convertRegionTypes(&op->getRegion(0), *getTypeConverter());
9070409eb28SAlex Zinenko     return failure();
9080409eb28SAlex Zinenko   }
9090409eb28SAlex Zinenko };
9100409eb28SAlex Zinenko 
9110409eb28SAlex Zinenko /// Just forward the operands to the root op. This is essentially a no-op
9120409eb28SAlex Zinenko /// pattern that is used to trigger target materialization.
9130409eb28SAlex Zinenko struct TestTypeConsumerForward
9140409eb28SAlex Zinenko     : public OpConversionPattern<TestTypeConsumerOp> {
9150409eb28SAlex Zinenko   using OpConversionPattern<TestTypeConsumerOp>::OpConversionPattern;
9160409eb28SAlex Zinenko 
9170409eb28SAlex Zinenko   LogicalResult
918ef976337SRiver Riddle   matchAndRewrite(TestTypeConsumerOp op, OpAdaptor adaptor,
9190409eb28SAlex Zinenko                   ConversionPatternRewriter &rewriter) const final {
920ef976337SRiver Riddle     rewriter.updateRootInPlace(op,
921ef976337SRiver Riddle                                [&] { op->setOperands(adaptor.getOperands()); });
9220409eb28SAlex Zinenko     return success();
9230409eb28SAlex Zinenko   }
9240409eb28SAlex Zinenko };
9250409eb28SAlex Zinenko 
9265b91060dSAlex Zinenko struct TestTypeConversionAnotherProducer
9275b91060dSAlex Zinenko     : public OpRewritePattern<TestAnotherTypeProducerOp> {
9285b91060dSAlex Zinenko   using OpRewritePattern<TestAnotherTypeProducerOp>::OpRewritePattern;
9295b91060dSAlex Zinenko 
9305b91060dSAlex Zinenko   LogicalResult matchAndRewrite(TestAnotherTypeProducerOp op,
9315b91060dSAlex Zinenko                                 PatternRewriter &rewriter) const final {
9325b91060dSAlex Zinenko     rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, op.getType());
9335b91060dSAlex Zinenko     return success();
9345b91060dSAlex Zinenko   }
9355b91060dSAlex Zinenko };
9365b91060dSAlex Zinenko 
9374589dd92SRiver Riddle struct TestTypeConversionDriver
9384589dd92SRiver Riddle     : public PassWrapper<TestTypeConversionDriver, OperationPass<ModuleOp>> {
939f9dc2b70SMehdi Amini   void getDependentDialects(DialectRegistry &registry) const override {
940f9dc2b70SMehdi Amini     registry.insert<TestDialect>();
941f9dc2b70SMehdi Amini   }
942b5e22e6dSMehdi Amini   StringRef getArgument() const final {
943b5e22e6dSMehdi Amini     return "test-legalize-type-conversion";
944b5e22e6dSMehdi Amini   }
945b5e22e6dSMehdi Amini   StringRef getDescription() const final {
946b5e22e6dSMehdi Amini     return "Test various type conversion functionalities in DialectConversion";
947b5e22e6dSMehdi Amini   }
948f9dc2b70SMehdi Amini 
9494589dd92SRiver Riddle   void runOnOperation() override {
9504589dd92SRiver Riddle     // Initialize the type converter.
9514589dd92SRiver Riddle     TypeConverter converter;
9524589dd92SRiver Riddle 
9534589dd92SRiver Riddle     /// Add the legal set of type conversions.
9544589dd92SRiver Riddle     converter.addConversion([](Type type) -> Type {
9554589dd92SRiver Riddle       // Treat F64 as legal.
9564589dd92SRiver Riddle       if (type.isF64())
9574589dd92SRiver Riddle         return type;
9584589dd92SRiver Riddle       // Allow converting BF16/F16/F32 to F64.
9594589dd92SRiver Riddle       if (type.isBF16() || type.isF16() || type.isF32())
9604589dd92SRiver Riddle         return FloatType::getF64(type.getContext());
9614589dd92SRiver Riddle       // Otherwise, the type is illegal.
9624589dd92SRiver Riddle       return nullptr;
9634589dd92SRiver Riddle     });
9644589dd92SRiver Riddle     converter.addConversion([](IntegerType type, SmallVectorImpl<Type> &) {
9654589dd92SRiver Riddle       // Drop all integer types.
9664589dd92SRiver Riddle       return success();
9674589dd92SRiver Riddle     });
9684589dd92SRiver Riddle 
9694589dd92SRiver Riddle     /// Add the legal set of type materializations.
9704589dd92SRiver Riddle     converter.addSourceMaterialization([](OpBuilder &builder, Type resultType,
9714589dd92SRiver Riddle                                           ValueRange inputs,
9724589dd92SRiver Riddle                                           Location loc) -> Value {
9734589dd92SRiver Riddle       // Allow casting from F64 back to F32.
9744589dd92SRiver Riddle       if (!resultType.isF16() && inputs.size() == 1 &&
9754589dd92SRiver Riddle           inputs[0].getType().isF64())
9764589dd92SRiver Riddle         return builder.create<TestCastOp>(loc, resultType, inputs).getResult();
9774589dd92SRiver Riddle       // Allow producing an i32 or i64 from nothing.
9784589dd92SRiver Riddle       if ((resultType.isInteger(32) || resultType.isInteger(64)) &&
9794589dd92SRiver Riddle           inputs.empty())
9804589dd92SRiver Riddle         return builder.create<TestTypeProducerOp>(loc, resultType);
9814589dd92SRiver Riddle       // Allow producing an i64 from an integer.
9824589dd92SRiver Riddle       if (resultType.isa<IntegerType>() && inputs.size() == 1 &&
9834589dd92SRiver Riddle           inputs[0].getType().isa<IntegerType>())
9844589dd92SRiver Riddle         return builder.create<TestCastOp>(loc, resultType, inputs).getResult();
9854589dd92SRiver Riddle       // Otherwise, fail.
9864589dd92SRiver Riddle       return nullptr;
9874589dd92SRiver Riddle     });
9884589dd92SRiver Riddle 
9894589dd92SRiver Riddle     // Initialize the conversion target.
9904589dd92SRiver Riddle     mlir::ConversionTarget target(getContext());
9914589dd92SRiver Riddle     target.addDynamicallyLegalOp<TestTypeProducerOp>([](TestTypeProducerOp op) {
9924589dd92SRiver Riddle       return op.getType().isF64() || op.getType().isInteger(64);
9934589dd92SRiver Riddle     });
9944589dd92SRiver Riddle     target.addDynamicallyLegalOp<FuncOp>([&](FuncOp op) {
9954589dd92SRiver Riddle       return converter.isSignatureLegal(op.getType()) &&
9964589dd92SRiver Riddle              converter.isLegal(&op.getBody());
9974589dd92SRiver Riddle     });
9984589dd92SRiver Riddle     target.addDynamicallyLegalOp<TestCastOp>([&](TestCastOp op) {
9994589dd92SRiver Riddle       // Allow casts from F64 to F32.
10004589dd92SRiver Riddle       return (*op.operand_type_begin()).isF64() && op.getType().isF32();
10014589dd92SRiver Riddle     });
10024589dd92SRiver Riddle 
10034589dd92SRiver Riddle     // Initialize the set of rewrite patterns.
1004dc4e913bSChris Lattner     RewritePatternSet patterns(&getContext());
1005dc4e913bSChris Lattner     patterns.add<TestTypeConsumerForward, TestTypeConversionProducer,
10060409eb28SAlex Zinenko                  TestSignatureConversionUndo>(converter, &getContext());
1007dc4e913bSChris Lattner     patterns.add<TestTypeConversionAnotherProducer>(&getContext());
10083a506b31SChris Lattner     mlir::populateFuncOpTypeConversionPattern(patterns, converter);
10094589dd92SRiver Riddle 
10103fffffa8SRiver Riddle     if (failed(applyPartialConversion(getOperation(), target,
10113fffffa8SRiver Riddle                                       std::move(patterns))))
10124589dd92SRiver Riddle       signalPassFailure();
10134589dd92SRiver Riddle   }
10144589dd92SRiver Riddle };
10154589dd92SRiver Riddle } // end anonymous namespace
10164589dd92SRiver Riddle 
1017c8fb6ee3SRiver Riddle //===----------------------------------------------------------------------===//
1018c8fb6ee3SRiver Riddle // Test Block Merging
1019c8fb6ee3SRiver Riddle //===----------------------------------------------------------------------===//
1020c8fb6ee3SRiver Riddle 
1021e888886cSMaheshRavishankar namespace {
1022e888886cSMaheshRavishankar /// A rewriter pattern that tests that blocks can be merged.
1023e888886cSMaheshRavishankar struct TestMergeBlock : public OpConversionPattern<TestMergeBlocksOp> {
1024e888886cSMaheshRavishankar   using OpConversionPattern<TestMergeBlocksOp>::OpConversionPattern;
1025e888886cSMaheshRavishankar 
1026e888886cSMaheshRavishankar   LogicalResult
1027ef976337SRiver Riddle   matchAndRewrite(TestMergeBlocksOp op, OpAdaptor adaptor,
1028e888886cSMaheshRavishankar                   ConversionPatternRewriter &rewriter) const final {
1029e888886cSMaheshRavishankar     Block &firstBlock = op.body().front();
1030e888886cSMaheshRavishankar     Operation *branchOp = firstBlock.getTerminator();
1031e888886cSMaheshRavishankar     Block *secondBlock = &*(std::next(op.body().begin()));
1032e888886cSMaheshRavishankar     auto succOperands = branchOp->getOperands();
1033e888886cSMaheshRavishankar     SmallVector<Value, 2> replacements(succOperands);
1034e888886cSMaheshRavishankar     rewriter.eraseOp(branchOp);
1035e888886cSMaheshRavishankar     rewriter.mergeBlocks(secondBlock, &firstBlock, replacements);
1036e888886cSMaheshRavishankar     rewriter.updateRootInPlace(op, [] {});
1037e888886cSMaheshRavishankar     return success();
1038e888886cSMaheshRavishankar   }
1039e888886cSMaheshRavishankar };
1040e888886cSMaheshRavishankar 
1041e888886cSMaheshRavishankar /// A rewrite pattern to tests the undo mechanism of blocks being merged.
1042e888886cSMaheshRavishankar struct TestUndoBlocksMerge : public ConversionPattern {
1043e888886cSMaheshRavishankar   TestUndoBlocksMerge(MLIRContext *ctx)
1044e888886cSMaheshRavishankar       : ConversionPattern("test.undo_blocks_merge", /*benefit=*/1, ctx) {}
1045e888886cSMaheshRavishankar   LogicalResult
1046e888886cSMaheshRavishankar   matchAndRewrite(Operation *op, ArrayRef<Value> operands,
1047e888886cSMaheshRavishankar                   ConversionPatternRewriter &rewriter) const final {
1048e888886cSMaheshRavishankar     Block &firstBlock = op->getRegion(0).front();
1049e888886cSMaheshRavishankar     Operation *branchOp = firstBlock.getTerminator();
1050e888886cSMaheshRavishankar     Block *secondBlock = &*(std::next(op->getRegion(0).begin()));
1051e888886cSMaheshRavishankar     rewriter.setInsertionPointToStart(secondBlock);
1052e888886cSMaheshRavishankar     rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getF32Type());
1053e888886cSMaheshRavishankar     auto succOperands = branchOp->getOperands();
1054e888886cSMaheshRavishankar     SmallVector<Value, 2> replacements(succOperands);
1055e888886cSMaheshRavishankar     rewriter.eraseOp(branchOp);
1056e888886cSMaheshRavishankar     rewriter.mergeBlocks(secondBlock, &firstBlock, replacements);
1057e888886cSMaheshRavishankar     rewriter.updateRootInPlace(op, [] {});
1058e888886cSMaheshRavishankar     return success();
1059e888886cSMaheshRavishankar   }
1060e888886cSMaheshRavishankar };
1061e888886cSMaheshRavishankar 
1062e888886cSMaheshRavishankar /// A rewrite mechanism to inline the body of the op into its parent, when both
1063e888886cSMaheshRavishankar /// ops can have a single block.
1064e888886cSMaheshRavishankar struct TestMergeSingleBlockOps
1065e888886cSMaheshRavishankar     : public OpConversionPattern<SingleBlockImplicitTerminatorOp> {
1066e888886cSMaheshRavishankar   using OpConversionPattern<
1067e888886cSMaheshRavishankar       SingleBlockImplicitTerminatorOp>::OpConversionPattern;
1068e888886cSMaheshRavishankar 
1069e888886cSMaheshRavishankar   LogicalResult
1070ef976337SRiver Riddle   matchAndRewrite(SingleBlockImplicitTerminatorOp op, OpAdaptor adaptor,
1071e888886cSMaheshRavishankar                   ConversionPatternRewriter &rewriter) const final {
1072e888886cSMaheshRavishankar     SingleBlockImplicitTerminatorOp parentOp =
10730bf4a82aSChristian Sigg         op->getParentOfType<SingleBlockImplicitTerminatorOp>();
1074e888886cSMaheshRavishankar     if (!parentOp)
1075e888886cSMaheshRavishankar       return failure();
1076e888886cSMaheshRavishankar     Block &innerBlock = op.region().front();
1077e888886cSMaheshRavishankar     TerminatorOp innerTerminator =
1078e888886cSMaheshRavishankar         cast<TerminatorOp>(innerBlock.getTerminator());
10799c7b0c4aSRahul Joshi     rewriter.mergeBlockBefore(&innerBlock, op);
1080e888886cSMaheshRavishankar     rewriter.eraseOp(innerTerminator);
1081e888886cSMaheshRavishankar     rewriter.eraseOp(op);
1082e888886cSMaheshRavishankar     rewriter.updateRootInPlace(op, [] {});
1083e888886cSMaheshRavishankar     return success();
1084e888886cSMaheshRavishankar   }
1085e888886cSMaheshRavishankar };
1086e888886cSMaheshRavishankar 
1087e888886cSMaheshRavishankar struct TestMergeBlocksPatternDriver
1088e888886cSMaheshRavishankar     : public PassWrapper<TestMergeBlocksPatternDriver,
1089e888886cSMaheshRavishankar                          OperationPass<ModuleOp>> {
1090b5e22e6dSMehdi Amini   StringRef getArgument() const final { return "test-merge-blocks"; }
1091b5e22e6dSMehdi Amini   StringRef getDescription() const final {
1092b5e22e6dSMehdi Amini     return "Test Merging operation in ConversionPatternRewriter";
1093b5e22e6dSMehdi Amini   }
1094e888886cSMaheshRavishankar   void runOnOperation() override {
1095e888886cSMaheshRavishankar     MLIRContext *context = &getContext();
1096dc4e913bSChris Lattner     mlir::RewritePatternSet patterns(context);
1097dc4e913bSChris Lattner     patterns.add<TestMergeBlock, TestUndoBlocksMerge, TestMergeSingleBlockOps>(
1098e888886cSMaheshRavishankar         context);
1099e888886cSMaheshRavishankar     ConversionTarget target(*context);
1100973ddb7dSMehdi Amini     target.addLegalOp<FuncOp, ModuleOp, TerminatorOp, TestBranchOp,
1101973ddb7dSMehdi Amini                       TestTypeConsumerOp, TestTypeProducerOp, TestReturnOp>();
1102e888886cSMaheshRavishankar     target.addIllegalOp<ILLegalOpF>();
1103e888886cSMaheshRavishankar 
1104e888886cSMaheshRavishankar     /// Expect the op to have a single block after legalization.
1105e888886cSMaheshRavishankar     target.addDynamicallyLegalOp<TestMergeBlocksOp>(
1106e888886cSMaheshRavishankar         [&](TestMergeBlocksOp op) -> bool {
1107e888886cSMaheshRavishankar           return llvm::hasSingleElement(op.body());
1108e888886cSMaheshRavishankar         });
1109e888886cSMaheshRavishankar 
1110e888886cSMaheshRavishankar     /// Only allow `test.br` within test.merge_blocks op.
1111e888886cSMaheshRavishankar     target.addDynamicallyLegalOp<TestBranchOp>([&](TestBranchOp op) -> bool {
11120bf4a82aSChristian Sigg       return op->getParentOfType<TestMergeBlocksOp>();
1113e888886cSMaheshRavishankar     });
1114e888886cSMaheshRavishankar 
1115e888886cSMaheshRavishankar     /// Expect that all nested test.SingleBlockImplicitTerminator ops are
1116e888886cSMaheshRavishankar     /// inlined.
1117e888886cSMaheshRavishankar     target.addDynamicallyLegalOp<SingleBlockImplicitTerminatorOp>(
1118e888886cSMaheshRavishankar         [&](SingleBlockImplicitTerminatorOp op) -> bool {
11190bf4a82aSChristian Sigg           return !op->getParentOfType<SingleBlockImplicitTerminatorOp>();
1120e888886cSMaheshRavishankar         });
1121e888886cSMaheshRavishankar 
1122e888886cSMaheshRavishankar     DenseSet<Operation *> unlegalizedOps;
11233fffffa8SRiver Riddle     (void)applyPartialConversion(getOperation(), target, std::move(patterns),
1124e888886cSMaheshRavishankar                                  &unlegalizedOps);
1125e888886cSMaheshRavishankar     for (auto *op : unlegalizedOps)
1126e888886cSMaheshRavishankar       op->emitRemark() << "op '" << op->getName() << "' is not legalizable";
1127e888886cSMaheshRavishankar   }
1128e888886cSMaheshRavishankar };
1129e888886cSMaheshRavishankar } // namespace
1130e888886cSMaheshRavishankar 
11314589dd92SRiver Riddle //===----------------------------------------------------------------------===//
1132c8fb6ee3SRiver Riddle // Test Selective Replacement
1133c8fb6ee3SRiver Riddle //===----------------------------------------------------------------------===//
1134c8fb6ee3SRiver Riddle 
1135c8fb6ee3SRiver Riddle namespace {
1136c8fb6ee3SRiver Riddle /// A rewrite mechanism to inline the body of the op into its parent, when both
1137c8fb6ee3SRiver Riddle /// ops can have a single block.
1138c8fb6ee3SRiver Riddle struct TestSelectiveOpReplacementPattern : public OpRewritePattern<TestCastOp> {
1139c8fb6ee3SRiver Riddle   using OpRewritePattern<TestCastOp>::OpRewritePattern;
1140c8fb6ee3SRiver Riddle 
1141c8fb6ee3SRiver Riddle   LogicalResult matchAndRewrite(TestCastOp op,
1142c8fb6ee3SRiver Riddle                                 PatternRewriter &rewriter) const final {
1143c8fb6ee3SRiver Riddle     if (op.getNumOperands() != 2)
1144c8fb6ee3SRiver Riddle       return failure();
1145c8fb6ee3SRiver Riddle     OperandRange operands = op.getOperands();
1146c8fb6ee3SRiver Riddle 
1147c8fb6ee3SRiver Riddle     // Replace non-terminator uses with the first operand.
1148c8fb6ee3SRiver Riddle     rewriter.replaceOpWithIf(op, operands[0], [](OpOperand &operand) {
1149fe7c0d90SRiver Riddle       return operand.getOwner()->hasTrait<OpTrait::IsTerminator>();
1150c8fb6ee3SRiver Riddle     });
1151c8fb6ee3SRiver Riddle     // Replace everything else with the second operand if the operation isn't
1152c8fb6ee3SRiver Riddle     // dead.
1153c8fb6ee3SRiver Riddle     rewriter.replaceOp(op, op.getOperand(1));
1154c8fb6ee3SRiver Riddle     return success();
1155c8fb6ee3SRiver Riddle   }
1156c8fb6ee3SRiver Riddle };
1157c8fb6ee3SRiver Riddle 
1158c8fb6ee3SRiver Riddle struct TestSelectiveReplacementPatternDriver
1159c8fb6ee3SRiver Riddle     : public PassWrapper<TestSelectiveReplacementPatternDriver,
1160c8fb6ee3SRiver Riddle                          OperationPass<>> {
1161b5e22e6dSMehdi Amini   StringRef getArgument() const final {
1162b5e22e6dSMehdi Amini     return "test-pattern-selective-replacement";
1163b5e22e6dSMehdi Amini   }
1164b5e22e6dSMehdi Amini   StringRef getDescription() const final {
1165b5e22e6dSMehdi Amini     return "Test selective replacement in the PatternRewriter";
1166b5e22e6dSMehdi Amini   }
1167c8fb6ee3SRiver Riddle   void runOnOperation() override {
1168c8fb6ee3SRiver Riddle     MLIRContext *context = &getContext();
1169dc4e913bSChris Lattner     mlir::RewritePatternSet patterns(context);
1170dc4e913bSChris Lattner     patterns.add<TestSelectiveOpReplacementPattern>(context);
1171e21adfa3SRiver Riddle     (void)applyPatternsAndFoldGreedily(getOperation()->getRegions(),
1172c8fb6ee3SRiver Riddle                                        std::move(patterns));
1173c8fb6ee3SRiver Riddle   }
1174c8fb6ee3SRiver Riddle };
1175c8fb6ee3SRiver Riddle } // namespace
1176c8fb6ee3SRiver Riddle 
1177c8fb6ee3SRiver Riddle //===----------------------------------------------------------------------===//
11784589dd92SRiver Riddle // PassRegistration
11794589dd92SRiver Riddle //===----------------------------------------------------------------------===//
11804589dd92SRiver Riddle 
1181fec6c5acSUday Bondhugula namespace mlir {
118272c65b69SAlexander Belyaev namespace test {
1183fec6c5acSUday Bondhugula void registerPatternsTestPass() {
1184b5e22e6dSMehdi Amini   PassRegistration<TestReturnTypeDriver>();
1185fec6c5acSUday Bondhugula 
1186b5e22e6dSMehdi Amini   PassRegistration<TestDerivedAttributeDriver>();
11879ba37b3bSJacques Pienaar 
1188b5e22e6dSMehdi Amini   PassRegistration<TestPatternDriver>();
1189fec6c5acSUday Bondhugula 
1190b5e22e6dSMehdi Amini   PassRegistration<TestLegalizePatternDriver>([] {
1191b5e22e6dSMehdi Amini     return std::make_unique<TestLegalizePatternDriver>(legalizerConversionMode);
1192fec6c5acSUday Bondhugula   });
1193fec6c5acSUday Bondhugula 
1194b5e22e6dSMehdi Amini   PassRegistration<TestRemappedValue>();
119580d7ac3bSRiver Riddle 
1196b5e22e6dSMehdi Amini   PassRegistration<TestUnknownRootOpDriver>();
11974589dd92SRiver Riddle 
1198b5e22e6dSMehdi Amini   PassRegistration<TestTypeConversionDriver>();
1199e888886cSMaheshRavishankar 
1200b5e22e6dSMehdi Amini   PassRegistration<TestMergeBlocksPatternDriver>();
1201b5e22e6dSMehdi Amini   PassRegistration<TestSelectiveReplacementPatternDriver>();
1202fec6c5acSUday Bondhugula }
120372c65b69SAlexander Belyaev } // namespace test
1204fec6c5acSUday Bondhugula } // namespace mlir
1205