2019-05-23 15:11:19 -07:00
|
|
|
//===- TestPatterns.cpp - Test dialect pattern driver ---------------------===//
|
|
|
|
|
//
|
2020-01-26 03:58:30 +00:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
2019-12-23 09:35:36 -08:00
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2019-05-23 15:11:19 -07:00
|
|
|
//
|
2019-12-23 09:35:36 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-05-23 15:11:19 -07:00
|
|
|
|
|
|
|
|
#include "TestDialect.h"
|
2021-10-12 23:14:57 +00:00
|
|
|
#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
|
2020-05-06 17:39:23 +02:00
|
|
|
#include "mlir/Dialect/StandardOps/IR/Ops.h"
|
2020-05-13 00:30:54 +02:00
|
|
|
#include "mlir/Dialect/StandardOps/Transforms/FuncConversions.h"
|
2021-07-01 09:58:48 +09:00
|
|
|
#include "mlir/Dialect/Tensor/IR/Tensor.h"
|
2020-10-09 13:32:01 -07:00
|
|
|
#include "mlir/IR/Matchers.h"
|
2019-05-23 15:11:19 -07:00
|
|
|
#include "mlir/Pass/Pass.h"
|
2019-06-11 15:38:13 -07:00
|
|
|
#include "mlir/Transforms/DialectConversion.h"
|
2020-05-06 17:39:23 +02:00
|
|
|
#include "mlir/Transforms/FoldUtils.h"
|
2020-10-26 17:24:17 -07:00
|
|
|
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
|
2020-04-16 08:05:21 -07:00
|
|
|
|
2019-05-23 15:11:19 -07:00
|
|
|
using namespace mlir;
|
2020-09-24 11:54:46 -07:00
|
|
|
using namespace test;
|
2019-05-23 15:11:19 -07:00
|
|
|
|
2019-07-05 10:05:16 -07:00
|
|
|
// Native function for testing NativeCodeCall
|
2019-12-23 14:45:01 -08:00
|
|
|
static Value chooseOperand(Value input1, Value input2, BoolAttr choice) {
|
2019-07-05 10:05:16 -07:00
|
|
|
return choice.getValue() ? input1 : input2;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-11 14:06:16 -07:00
|
|
|
static void createOpI(PatternRewriter &rewriter, Location loc, Value input) {
|
|
|
|
|
rewriter.create<OpI>(loc, input);
|
2019-10-17 08:39:13 -07:00
|
|
|
}
|
|
|
|
|
|
2020-01-14 14:06:12 +01:00
|
|
|
static void handleNoResultOp(PatternRewriter &rewriter,
|
|
|
|
|
OpSymbolBindingNoResult op) {
|
2019-10-17 09:01:56 -07:00
|
|
|
// Turn the no result op to a one-result op.
|
2021-10-20 07:08:36 -07:00
|
|
|
rewriter.create<OpSymbolBindingB>(op.getLoc(), op.getOperand().getType(),
|
|
|
|
|
op.getOperand());
|
2019-10-17 09:01:56 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-16 13:34:10 +08:00
|
|
|
static bool getFirstI32Result(Operation *op, Value &value) {
|
|
|
|
|
if (!Type(op->getResult(0).getType()).isSignlessInteger(32))
|
|
|
|
|
return false;
|
|
|
|
|
value = op->getResult(0);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Value bindNativeCodeCallResult(Value value) { return value; }
|
|
|
|
|
|
2021-07-21 11:23:06 +08:00
|
|
|
static SmallVector<Value, 2> bindMultipleNativeCodeCallResult(Value input1,
|
|
|
|
|
Value input2) {
|
|
|
|
|
return SmallVector<Value, 2>({input2, input1});
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-22 08:10:23 -07:00
|
|
|
// Test that natives calls are only called once during rewrites.
|
|
|
|
|
// OpM_Test will return Pi, increased by 1 for each subsequent calls.
|
|
|
|
|
// This let us check the number of times OpM_Test was called by inspecting
|
|
|
|
|
// the returned value in the MLIR output.
|
|
|
|
|
static int64_t opMIncreasingValue = 314159265;
|
|
|
|
|
static Attribute OpMTest(PatternRewriter &rewriter, Value val) {
|
|
|
|
|
int64_t i = opMIncreasingValue++;
|
|
|
|
|
return rewriter.getIntegerAttr(rewriter.getIntegerType(32), i);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-23 15:11:19 -07:00
|
|
|
namespace {
|
|
|
|
|
#include "TestPatterns.inc"
|
2019-06-11 15:38:13 -07:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
2021-06-02 07:00:19 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Test Reduce Pattern Interface
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2020-09-24 11:54:46 -07:00
|
|
|
void test::populateTestReductionPatterns(RewritePatternSet &patterns) {
|
2021-06-02 07:00:19 +08:00
|
|
|
populateWithGenerated(patterns);
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 15:38:13 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Canonicalizer Driver.
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-05-23 15:11:19 -07:00
|
|
|
|
2019-06-11 15:38:13 -07:00
|
|
|
namespace {
|
2020-05-06 17:39:23 +02:00
|
|
|
struct FoldingPattern : public RewritePattern {
|
|
|
|
|
public:
|
|
|
|
|
FoldingPattern(MLIRContext *context)
|
|
|
|
|
: RewritePattern(TestOpInPlaceFoldAnchor::getOperationName(),
|
|
|
|
|
/*benefit=*/1, context) {}
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(Operation *op,
|
|
|
|
|
PatternRewriter &rewriter) const override {
|
2021-01-07 02:35:29 +09:00
|
|
|
// Exercise OperationFolder API for a single-result operation that is folded
|
2020-05-06 17:39:23 +02:00
|
|
|
// upon construction. The operation being created through the folder has an
|
|
|
|
|
// in-place folder, and it should be still present in the output.
|
|
|
|
|
// Furthermore, the folder should not crash when attempting to recover the
|
2020-08-27 15:37:23 +09:00
|
|
|
// (unchanged) operation result.
|
2020-05-06 17:39:23 +02:00
|
|
|
OperationFolder folder(op->getContext());
|
|
|
|
|
Value result = folder.create<TestOpInPlaceFold>(
|
|
|
|
|
rewriter, op->getLoc(), rewriter.getIntegerType(32), op->getOperand(0),
|
|
|
|
|
rewriter.getI32IntegerAttr(0));
|
|
|
|
|
assert(result);
|
|
|
|
|
rewriter.replaceOp(op, result);
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-08-23 18:14:18 +00:00
|
|
|
/// This pattern creates a foldable operation at the entry point of the block.
|
|
|
|
|
/// This tests the situation where the operation folder will need to replace an
|
|
|
|
|
/// operation with a previously created constant that does not initially
|
|
|
|
|
/// dominate the operation to replace.
|
|
|
|
|
struct FolderInsertBeforePreviouslyFoldedConstantPattern
|
|
|
|
|
: public OpRewritePattern<TestCastOp> {
|
|
|
|
|
public:
|
|
|
|
|
using OpRewritePattern<TestCastOp>::OpRewritePattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(TestCastOp op,
|
|
|
|
|
PatternRewriter &rewriter) const override {
|
|
|
|
|
if (!op->hasAttr("test_fold_before_previously_folded_op"))
|
|
|
|
|
return failure();
|
|
|
|
|
rewriter.setInsertionPointToStart(op->getBlock());
|
|
|
|
|
|
2021-10-12 23:14:57 +00:00
|
|
|
auto constOp = rewriter.create<arith::ConstantOp>(
|
|
|
|
|
op.getLoc(), rewriter.getBoolAttr(true));
|
2021-08-23 18:14:18 +00:00
|
|
|
rewriter.replaceOpWithNewOp<TestCastOp>(op, rewriter.getI32Type(),
|
|
|
|
|
Value(constOp));
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-07 13:56:16 -07:00
|
|
|
struct TestPatternDriver : public PassWrapper<TestPatternDriver, FunctionPass> {
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final { return "test-patterns"; }
|
|
|
|
|
StringRef getDescription() const final { return "Run test dialect patterns"; }
|
2019-06-11 15:38:13 -07:00
|
|
|
void runOnFunction() override {
|
2021-03-22 16:58:34 -07:00
|
|
|
mlir::RewritePatternSet patterns(&getContext());
|
2021-03-21 10:38:35 -07:00
|
|
|
populateWithGenerated(patterns);
|
2019-06-11 15:38:13 -07:00
|
|
|
|
|
|
|
|
// Verify named pattern is generated with expected name.
|
2021-08-23 18:14:18 +00:00
|
|
|
patterns.add<FoldingPattern, TestNamedPatternRule,
|
|
|
|
|
FolderInsertBeforePreviouslyFoldedConstantPattern>(
|
|
|
|
|
&getContext());
|
2019-06-11 15:38:13 -07:00
|
|
|
|
2021-02-04 14:57:50 -08:00
|
|
|
(void)applyPatternsAndFoldGreedily(getFunction(), std::move(patterns));
|
2019-06-11 15:38:13 -07:00
|
|
|
}
|
2019-05-23 15:11:19 -07:00
|
|
|
};
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
2019-09-29 17:28:29 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// ReturnType Driver.
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2020-01-14 14:06:12 +01:00
|
|
|
namespace {
|
2020-02-28 10:59:34 -08:00
|
|
|
// Generate ops for each instance where the type can be successfully inferred.
|
2020-01-08 18:48:38 -08:00
|
|
|
template <typename OpTy>
|
2020-02-28 10:59:34 -08:00
|
|
|
static void invokeCreateWithInferredReturnType(Operation *op) {
|
2020-01-08 18:48:38 -08:00
|
|
|
auto *context = op->getContext();
|
|
|
|
|
auto fop = op->getParentOfType<FuncOp>();
|
|
|
|
|
auto location = UnknownLoc::get(context);
|
|
|
|
|
OpBuilder b(op);
|
|
|
|
|
b.setInsertionPointAfter(op);
|
|
|
|
|
|
|
|
|
|
// Use permutations of 2 args as operands.
|
|
|
|
|
assert(fop.getNumArguments() >= 2);
|
|
|
|
|
for (int i = 0, e = fop.getNumArguments(); i < e; ++i) {
|
|
|
|
|
for (int j = 0; j < e; ++j) {
|
2020-02-04 10:34:42 -08:00
|
|
|
std::array<Value, 2> values = {{fop.getArgument(i), fop.getArgument(j)}};
|
2020-02-28 10:59:34 -08:00
|
|
|
SmallVector<Type, 2> inferredReturnTypes;
|
2020-05-06 13:48:36 -07:00
|
|
|
if (succeeded(OpTy::inferReturnTypes(
|
|
|
|
|
context, llvm::None, values, op->getAttrDictionary(),
|
|
|
|
|
op->getRegions(), inferredReturnTypes))) {
|
2020-01-08 18:48:38 -08:00
|
|
|
OperationState state(location, OpTy::getOperationName());
|
2020-07-07 01:35:23 -07:00
|
|
|
// TODO: Expand to regions.
|
2020-04-23 16:02:46 +02:00
|
|
|
OpTy::build(b, state, values, op->getAttrs());
|
2020-01-08 18:48:38 -08:00
|
|
|
(void)b.createOperation(state);
|
2019-12-06 10:52:38 -08:00
|
|
|
}
|
2019-09-29 17:28:29 -07:00
|
|
|
}
|
|
|
|
|
}
|
2020-01-08 18:48:38 -08:00
|
|
|
}
|
2019-09-29 17:28:29 -07:00
|
|
|
|
2020-02-28 08:37:09 -08:00
|
|
|
static void reifyReturnShape(Operation *op) {
|
|
|
|
|
OpBuilder b(op);
|
|
|
|
|
|
|
|
|
|
// Use permutations of 2 args as operands.
|
|
|
|
|
auto shapedOp = cast<OpWithShapedTypeInferTypeInterfaceOp>(op);
|
|
|
|
|
SmallVector<Value, 2> shapes;
|
2021-05-19 02:11:33 +00:00
|
|
|
if (failed(shapedOp.reifyReturnTypeShapes(b, op->getOperands(), shapes)) ||
|
2021-03-29 10:57:23 -07:00
|
|
|
!llvm::hasSingleElement(shapes))
|
2020-02-28 08:37:09 -08:00
|
|
|
return;
|
2021-03-29 10:57:23 -07:00
|
|
|
for (auto it : llvm::enumerate(shapes)) {
|
2020-02-28 08:37:09 -08:00
|
|
|
op->emitRemark() << "value " << it.index() << ": "
|
|
|
|
|
<< it.value().getDefiningOp();
|
2021-03-29 10:57:23 -07:00
|
|
|
}
|
2020-02-28 08:37:09 -08:00
|
|
|
}
|
|
|
|
|
|
2020-04-07 13:56:16 -07:00
|
|
|
struct TestReturnTypeDriver
|
|
|
|
|
: public PassWrapper<TestReturnTypeDriver, FunctionPass> {
|
2021-02-10 13:53:11 +01:00
|
|
|
void getDependentDialects(DialectRegistry ®istry) const override {
|
2021-07-01 09:58:48 +09:00
|
|
|
registry.insert<tensor::TensorDialect>();
|
2021-02-10 13:53:11 +01:00
|
|
|
}
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final { return "test-return-type"; }
|
|
|
|
|
StringRef getDescription() const final { return "Run return type functions"; }
|
2021-02-10 13:53:11 +01:00
|
|
|
|
2019-09-29 17:28:29 -07:00
|
|
|
void runOnFunction() override {
|
2020-01-08 18:48:38 -08:00
|
|
|
if (getFunction().getName() == "testCreateFunctions") {
|
|
|
|
|
std::vector<Operation *> ops;
|
|
|
|
|
// Collect ops to avoid triggering on inserted ops.
|
|
|
|
|
for (auto &op : getFunction().getBody().front())
|
|
|
|
|
ops.push_back(&op);
|
|
|
|
|
// Generate test patterns for each, but skip terminator.
|
|
|
|
|
for (auto *op : llvm::makeArrayRef(ops).drop_back()) {
|
|
|
|
|
// Test create method of each of the Op classes below. The resultant
|
|
|
|
|
// output would be in reverse order underneath `op` from which
|
|
|
|
|
// the attributes and regions are used.
|
2020-02-28 10:59:34 -08:00
|
|
|
invokeCreateWithInferredReturnType<OpWithInferTypeInterfaceOp>(op);
|
|
|
|
|
invokeCreateWithInferredReturnType<
|
|
|
|
|
OpWithShapedTypeInferTypeInterfaceOp>(op);
|
2020-01-08 18:48:38 -08:00
|
|
|
};
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-02-28 08:37:09 -08:00
|
|
|
if (getFunction().getName() == "testReifyFunctions") {
|
|
|
|
|
std::vector<Operation *> ops;
|
|
|
|
|
// Collect ops to avoid triggering on inserted ops.
|
|
|
|
|
for (auto &op : getFunction().getBody().front())
|
|
|
|
|
if (isa<OpWithShapedTypeInferTypeInterfaceOp>(op))
|
|
|
|
|
ops.push_back(&op);
|
|
|
|
|
// Generate test patterns for each, but skip terminator.
|
|
|
|
|
for (auto *op : ops)
|
|
|
|
|
reifyReturnShape(op);
|
|
|
|
|
}
|
2019-09-29 17:28:29 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
2020-04-16 08:05:21 -07:00
|
|
|
namespace {
|
|
|
|
|
struct TestDerivedAttributeDriver
|
|
|
|
|
: public PassWrapper<TestDerivedAttributeDriver, FunctionPass> {
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final { return "test-derived-attr"; }
|
|
|
|
|
StringRef getDescription() const final {
|
|
|
|
|
return "Run test derived attributes";
|
|
|
|
|
}
|
2020-04-16 08:05:21 -07:00
|
|
|
void runOnFunction() override;
|
|
|
|
|
};
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
|
|
void TestDerivedAttributeDriver::runOnFunction() {
|
|
|
|
|
getFunction().walk([](DerivedAttributeOpInterface dOp) {
|
|
|
|
|
auto dAttr = dOp.materializeDerivedAttributes();
|
|
|
|
|
if (!dAttr)
|
|
|
|
|
return;
|
|
|
|
|
for (auto d : dAttr)
|
2021-11-18 05:23:32 +00:00
|
|
|
dOp.emitRemark() << d.getName().getValue() << " = " << d.getValue();
|
2020-04-16 08:05:21 -07:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 15:38:13 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Legalization Driver.
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2019-06-21 09:29:46 -07:00
|
|
|
|
2019-06-19 13:58:31 -07:00
|
|
|
namespace {
|
2019-09-16 10:37:48 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Region-Block Rewrite Testing
|
|
|
|
|
|
2019-06-19 13:58:31 -07:00
|
|
|
/// This pattern is a simple pattern that inlines the first region of a given
|
|
|
|
|
/// operation into the parent region.
|
|
|
|
|
struct TestRegionRewriteBlockMovement : public ConversionPattern {
|
|
|
|
|
TestRegionRewriteBlockMovement(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.region", 1, ctx) {}
|
|
|
|
|
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2019-12-23 14:45:01 -08:00
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
2019-07-18 12:04:57 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
2019-06-19 13:58:31 -07:00
|
|
|
// Inline this region into the parent region.
|
2019-08-09 20:07:25 -07:00
|
|
|
auto &parentRegion = *op->getParentRegion();
|
2020-11-19 14:27:58 +01:00
|
|
|
auto &opRegion = op->getRegion(0);
|
2019-10-08 15:44:34 -07:00
|
|
|
if (op->getAttr("legalizer.should_clone"))
|
2020-11-19 14:27:58 +01:00
|
|
|
rewriter.cloneRegionBefore(opRegion, parentRegion, parentRegion.end());
|
2019-10-08 15:44:34 -07:00
|
|
|
else
|
2020-11-19 14:27:58 +01:00
|
|
|
rewriter.inlineRegionBefore(opRegion, parentRegion, parentRegion.end());
|
|
|
|
|
|
|
|
|
|
if (op->getAttr("legalizer.erase_old_blocks")) {
|
|
|
|
|
while (!opRegion.empty())
|
|
|
|
|
rewriter.eraseBlock(&opRegion.front());
|
|
|
|
|
}
|
2019-06-19 13:58:31 -07:00
|
|
|
|
|
|
|
|
// Drop this operation.
|
2019-10-16 09:50:28 -07:00
|
|
|
rewriter.eraseOp(op);
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-06-19 13:58:31 -07:00
|
|
|
}
|
|
|
|
|
};
|
2019-07-17 14:45:53 -07:00
|
|
|
/// This pattern is a simple pattern that generates a region containing an
|
|
|
|
|
/// illegal operation.
|
|
|
|
|
struct TestRegionRewriteUndo : public RewritePattern {
|
|
|
|
|
TestRegionRewriteUndo(MLIRContext *ctx)
|
|
|
|
|
: RewritePattern("test.region_builder", 1, ctx) {}
|
|
|
|
|
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult matchAndRewrite(Operation *op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
2019-07-17 14:45:53 -07:00
|
|
|
// Create the region operation with an entry block containing arguments.
|
|
|
|
|
OperationState newRegion(op->getLoc(), "test.region");
|
|
|
|
|
newRegion.addRegion();
|
|
|
|
|
auto *regionOp = rewriter.createOperation(newRegion);
|
|
|
|
|
auto *entryBlock = rewriter.createBlock(®ionOp->getRegion(0));
|
|
|
|
|
entryBlock->addArgument(rewriter.getIntegerType(64));
|
|
|
|
|
|
|
|
|
|
// Add an explicitly illegal operation to ensure the conversion fails.
|
|
|
|
|
rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getIntegerType(32));
|
2019-12-23 14:45:01 -08:00
|
|
|
rewriter.create<TestValidOp>(op->getLoc(), ArrayRef<Value>());
|
2019-07-17 14:45:53 -07:00
|
|
|
|
|
|
|
|
// Drop this operation.
|
2019-10-16 09:50:28 -07:00
|
|
|
rewriter.eraseOp(op);
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-07-17 14:45:53 -07:00
|
|
|
}
|
|
|
|
|
};
|
2020-04-03 19:53:13 +02:00
|
|
|
/// A simple pattern that creates a block at the end of the parent region of the
|
|
|
|
|
/// matched operation.
|
|
|
|
|
struct TestCreateBlock : public RewritePattern {
|
|
|
|
|
TestCreateBlock(MLIRContext *ctx)
|
|
|
|
|
: RewritePattern("test.create_block", /*benefit=*/1, ctx) {}
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(Operation *op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
|
|
|
|
Region ®ion = *op->getParentRegion();
|
|
|
|
|
Type i32Type = rewriter.getIntegerType(32);
|
|
|
|
|
rewriter.createBlock(®ion, region.end(), {i32Type, i32Type});
|
|
|
|
|
rewriter.create<TerminatorOp>(op->getLoc());
|
|
|
|
|
rewriter.replaceOp(op, {});
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-08-27 15:37:23 +09:00
|
|
|
/// A simple pattern that creates a block containing an invalid operation in
|
2020-04-03 19:53:13 +02:00
|
|
|
/// order to trigger the block creation undo mechanism.
|
|
|
|
|
struct TestCreateIllegalBlock : public RewritePattern {
|
|
|
|
|
TestCreateIllegalBlock(MLIRContext *ctx)
|
|
|
|
|
: RewritePattern("test.create_illegal_block", /*benefit=*/1, ctx) {}
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(Operation *op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
|
|
|
|
Region ®ion = *op->getParentRegion();
|
|
|
|
|
Type i32Type = rewriter.getIntegerType(32);
|
|
|
|
|
rewriter.createBlock(®ion, region.end(), {i32Type, i32Type});
|
|
|
|
|
// Create an illegal op to ensure the conversion fails.
|
|
|
|
|
rewriter.create<ILLegalOpF>(op->getLoc(), i32Type);
|
|
|
|
|
rewriter.create<TerminatorOp>(op->getLoc());
|
|
|
|
|
rewriter.replaceOp(op, {});
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
2019-09-16 10:37:48 -07:00
|
|
|
|
2020-04-24 12:25:05 -07:00
|
|
|
/// A simple pattern that tests the undo mechanism when replacing the uses of a
|
|
|
|
|
/// block argument.
|
|
|
|
|
struct TestUndoBlockArgReplace : public ConversionPattern {
|
|
|
|
|
TestUndoBlockArgReplace(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.undo_block_arg_replace", /*benefit=*/1, ctx) {}
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
|
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
auto illegalOp =
|
|
|
|
|
rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getF32Type());
|
2020-07-10 17:07:29 -07:00
|
|
|
rewriter.replaceUsesOfBlockArgument(op->getRegion(0).getArgument(0),
|
2020-04-24 12:25:05 -07:00
|
|
|
illegalOp);
|
|
|
|
|
rewriter.updateRootInPlace(op, [] {});
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-20 16:00:27 +02:00
|
|
|
/// A rewrite pattern that tests the undo mechanism when erasing a block.
|
|
|
|
|
struct TestUndoBlockErase : public ConversionPattern {
|
|
|
|
|
TestUndoBlockErase(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.undo_block_erase", /*benefit=*/1, ctx) {}
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
|
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
Block *secondBlock = &*std::next(op->getRegion(0).begin());
|
|
|
|
|
rewriter.setInsertionPointToStart(secondBlock);
|
|
|
|
|
rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getF32Type());
|
|
|
|
|
rewriter.eraseBlock(secondBlock);
|
|
|
|
|
rewriter.updateRootInPlace(op, [] {});
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-09-16 10:37:48 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Type-Conversion Rewrite Testing
|
|
|
|
|
|
2019-11-13 10:27:21 -08:00
|
|
|
/// This patterns erases a region operation that has had a type conversion.
|
|
|
|
|
struct TestDropOpSignatureConversion : public ConversionPattern {
|
|
|
|
|
TestDropOpSignatureConversion(MLIRContext *ctx, TypeConverter &converter)
|
2021-03-23 13:44:14 -07:00
|
|
|
: ConversionPattern(converter, "test.drop_region_op", 1, ctx) {}
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2019-12-23 14:45:01 -08:00
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
2019-11-13 10:27:21 -08:00
|
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
|
|
|
Region ®ion = op->getRegion(0);
|
|
|
|
|
Block *entry = ®ion.front();
|
|
|
|
|
|
|
|
|
|
// Convert the original entry arguments.
|
2020-06-18 15:45:43 -07:00
|
|
|
TypeConverter &converter = *getTypeConverter();
|
2019-11-13 10:27:21 -08:00
|
|
|
TypeConverter::SignatureConversion result(entry->getNumArguments());
|
2020-06-18 15:45:43 -07:00
|
|
|
if (failed(converter.convertSignatureArgs(entry->getArgumentTypes(),
|
|
|
|
|
result)) ||
|
|
|
|
|
failed(rewriter.convertRegionTypes(®ion, converter, &result)))
|
2020-06-15 15:30:13 -07:00
|
|
|
return failure();
|
2019-11-13 10:27:21 -08:00
|
|
|
|
|
|
|
|
// Convert the region signature and just drop the operation.
|
2019-10-16 09:50:28 -07:00
|
|
|
rewriter.eraseOp(op);
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-06-19 13:58:31 -07:00
|
|
|
}
|
|
|
|
|
};
|
2019-06-28 11:28:30 -07:00
|
|
|
/// This pattern simply updates the operands of the given operation.
|
|
|
|
|
struct TestPassthroughInvalidOp : public ConversionPattern {
|
|
|
|
|
TestPassthroughInvalidOp(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.invalid", 1, ctx) {}
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2019-12-23 14:45:01 -08:00
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
2019-07-18 12:04:57 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
2019-06-28 11:28:30 -07:00
|
|
|
rewriter.replaceOpWithNewOp<TestValidOp>(op, llvm::None, operands,
|
|
|
|
|
llvm::None);
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-06-28 11:28:30 -07:00
|
|
|
}
|
|
|
|
|
};
|
2019-06-21 09:29:46 -07:00
|
|
|
/// This pattern handles the case of a split return value.
|
|
|
|
|
struct TestSplitReturnType : public ConversionPattern {
|
|
|
|
|
TestSplitReturnType(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.return", 1, ctx) {}
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2019-12-23 14:45:01 -08:00
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
2019-07-18 12:04:57 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
2019-06-21 09:29:46 -07:00
|
|
|
// Check for a return of F32.
|
2020-01-11 08:54:04 -08:00
|
|
|
if (op->getNumOperands() != 1 || !op->getOperand(0).getType().isF32())
|
2020-03-17 20:07:55 -07:00
|
|
|
return failure();
|
2019-06-21 09:29:46 -07:00
|
|
|
|
|
|
|
|
// Check if the first operation is a cast operation, if it is we use the
|
|
|
|
|
// results directly.
|
2020-01-11 08:54:04 -08:00
|
|
|
auto *defOp = operands[0].getDefiningOp();
|
2021-10-27 02:00:10 +00:00
|
|
|
if (auto packerOp =
|
|
|
|
|
llvm::dyn_cast_or_null<UnrealizedConversionCastOp>(defOp)) {
|
2019-12-07 10:35:01 -08:00
|
|
|
rewriter.replaceOpWithNewOp<TestReturnOp>(op, packerOp.getOperands());
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-06-21 09:29:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Otherwise, fail to match.
|
2020-03-17 20:07:55 -07:00
|
|
|
return failure();
|
2019-06-21 09:29:46 -07:00
|
|
|
}
|
|
|
|
|
};
|
2019-09-16 10:37:48 -07:00
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Multi-Level Type-Conversion Rewrite Testing
|
|
|
|
|
struct TestChangeProducerTypeI32ToF32 : public ConversionPattern {
|
|
|
|
|
TestChangeProducerTypeI32ToF32(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.type_producer", 1, ctx) {}
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2019-12-23 14:45:01 -08:00
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
2019-09-16 10:37:48 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
// If the type is I32, change the type to F32.
|
2020-01-10 14:48:24 -05:00
|
|
|
if (!Type(*op->result_type_begin()).isSignlessInteger(32))
|
2020-03-17 20:07:55 -07:00
|
|
|
return failure();
|
2019-09-16 10:37:48 -07:00
|
|
|
rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getF32Type());
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-09-16 10:37:48 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
struct TestChangeProducerTypeF32ToF64 : public ConversionPattern {
|
|
|
|
|
TestChangeProducerTypeF32ToF64(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.type_producer", 1, ctx) {}
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2019-12-23 14:45:01 -08:00
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
2019-09-16 10:37:48 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
// If the type is F32, change the type to F64.
|
2020-01-27 19:57:14 -08:00
|
|
|
if (!Type(*op->result_type_begin()).isF32())
|
2020-03-17 12:09:14 -07:00
|
|
|
return rewriter.notifyMatchFailure(op, "expected single f32 operand");
|
2019-09-16 10:37:48 -07:00
|
|
|
rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getF64Type());
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-09-16 10:37:48 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
struct TestChangeProducerTypeF32ToInvalid : public ConversionPattern {
|
|
|
|
|
TestChangeProducerTypeF32ToInvalid(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.type_producer", 10, ctx) {}
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2019-12-23 14:45:01 -08:00
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
2019-09-16 10:37:48 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
// Always convert to B16, even though it is not a legal type. This tests
|
|
|
|
|
// that values are unmapped correctly.
|
|
|
|
|
rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, rewriter.getBF16Type());
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-09-16 10:37:48 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
struct TestUpdateConsumerType : public ConversionPattern {
|
|
|
|
|
TestUpdateConsumerType(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.type_consumer", 1, ctx) {}
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2019-12-23 14:45:01 -08:00
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
2019-09-16 10:37:48 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
2019-11-20 05:37:49 -08:00
|
|
|
// Verify that the incoming operand has been successfully remapped to F64.
|
2020-01-11 08:54:04 -08:00
|
|
|
if (!operands[0].getType().isF64())
|
2020-03-17 20:07:55 -07:00
|
|
|
return failure();
|
2019-09-16 10:37:48 -07:00
|
|
|
rewriter.replaceOpWithNewOp<TestTypeConsumerOp>(op, operands[0]);
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-09-16 10:37:48 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-10-14 09:50:54 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Non-Root Replacement Rewrite Testing
|
|
|
|
|
/// This pattern generates an invalid operation, but replaces it before the
|
|
|
|
|
/// pattern is finished. This checks that we don't need to legalize the
|
|
|
|
|
/// temporary op.
|
|
|
|
|
struct TestNonRootReplacement : public RewritePattern {
|
|
|
|
|
TestNonRootReplacement(MLIRContext *ctx)
|
|
|
|
|
: RewritePattern("test.replace_non_root", 1, ctx) {}
|
|
|
|
|
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult matchAndRewrite(Operation *op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
2019-10-14 09:50:54 -07:00
|
|
|
auto resultType = *op->result_type_begin();
|
|
|
|
|
auto illegalOp = rewriter.create<ILLegalOpF>(op->getLoc(), resultType);
|
|
|
|
|
auto legalOp = rewriter.create<LegalOpB>(op->getLoc(), resultType);
|
|
|
|
|
|
|
|
|
|
rewriter.replaceOp(illegalOp, {legalOp});
|
|
|
|
|
rewriter.replaceOp(op, {illegalOp});
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-10-14 09:50:54 -07:00
|
|
|
}
|
|
|
|
|
};
|
2020-04-09 12:38:52 -07:00
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Recursive Rewrite Testing
|
|
|
|
|
/// This pattern is applied to the same operation multiple times, but has a
|
|
|
|
|
/// bounded recursion.
|
|
|
|
|
struct TestBoundedRecursiveRewrite
|
|
|
|
|
: public OpRewritePattern<TestRecursiveRewriteOp> {
|
2021-05-18 14:31:33 -07:00
|
|
|
using OpRewritePattern<TestRecursiveRewriteOp>::OpRewritePattern;
|
|
|
|
|
|
|
|
|
|
void initialize() {
|
2020-10-26 17:23:41 -07:00
|
|
|
// The conversion target handles bounding the recursion of this pattern.
|
|
|
|
|
setHasBoundedRewriteRecursion();
|
|
|
|
|
}
|
2020-04-09 12:38:52 -07:00
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(TestRecursiveRewriteOp op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
|
|
|
|
// Decrement the depth of the op in-place.
|
|
|
|
|
rewriter.updateRootInPlace(op, [&] {
|
2021-10-20 07:08:36 -07:00
|
|
|
op->setAttr("depth", rewriter.getI64IntegerAttr(op.getDepth() - 1));
|
2020-04-09 12:38:52 -07:00
|
|
|
});
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
2020-05-20 15:59:54 +02:00
|
|
|
|
|
|
|
|
struct TestNestedOpCreationUndoRewrite
|
|
|
|
|
: public OpRewritePattern<IllegalOpWithRegionAnchor> {
|
|
|
|
|
using OpRewritePattern<IllegalOpWithRegionAnchor>::OpRewritePattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(IllegalOpWithRegionAnchor op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
|
|
|
|
// rewriter.replaceOpWithNewOp<IllegalOpWithRegion>(op);
|
|
|
|
|
rewriter.replaceOpWithNewOp<IllegalOpWithRegion>(op);
|
|
|
|
|
return success();
|
|
|
|
|
};
|
|
|
|
|
};
|
2021-03-30 21:11:02 +00:00
|
|
|
|
|
|
|
|
// This pattern matches `test.blackhole` and delete this op and its producer.
|
|
|
|
|
struct TestReplaceEraseOp : public OpRewritePattern<BlackHoleOp> {
|
|
|
|
|
using OpRewritePattern<BlackHoleOp>::OpRewritePattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(BlackHoleOp op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
|
|
|
|
Operation *producer = op.getOperand().getDefiningOp();
|
|
|
|
|
// Always erase the user before the producer, the framework should handle
|
|
|
|
|
// this correctly.
|
|
|
|
|
rewriter.eraseOp(op);
|
|
|
|
|
rewriter.eraseOp(producer);
|
|
|
|
|
return success();
|
|
|
|
|
};
|
|
|
|
|
};
|
2021-08-20 14:25:42 +03:00
|
|
|
|
|
|
|
|
// This pattern replaces explicitly illegal op with explicitly legal op,
|
|
|
|
|
// but in addition creates unregistered operation.
|
|
|
|
|
struct TestCreateUnregisteredOp : public OpRewritePattern<ILLegalOpG> {
|
|
|
|
|
using OpRewritePattern<ILLegalOpG>::OpRewritePattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(ILLegalOpG op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
|
|
|
|
IntegerAttr attr = rewriter.getI32IntegerAttr(0);
|
|
|
|
|
Value val = rewriter.create<ConstantOp>(op->getLoc(), attr);
|
|
|
|
|
rewriter.replaceOpWithNewOp<LegalOpC>(op, val);
|
|
|
|
|
return success();
|
|
|
|
|
};
|
|
|
|
|
};
|
2019-06-19 13:58:31 -07:00
|
|
|
} // namespace
|
2019-05-27 20:04:56 -07:00
|
|
|
|
2019-06-11 15:38:13 -07:00
|
|
|
namespace {
|
2019-06-19 13:58:31 -07:00
|
|
|
struct TestTypeConverter : public TypeConverter {
|
|
|
|
|
using TypeConverter::TypeConverter;
|
2020-06-02 13:24:04 +02:00
|
|
|
TestTypeConverter() {
|
|
|
|
|
addConversion(convertType);
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
addArgumentMaterialization(materializeCast);
|
|
|
|
|
addSourceMaterialization(materializeCast);
|
2020-06-02 13:24:04 +02:00
|
|
|
}
|
2019-06-19 13:58:31 -07:00
|
|
|
|
2020-02-18 15:56:33 -08:00
|
|
|
static LogicalResult convertType(Type t, SmallVectorImpl<Type> &results) {
|
2019-06-19 13:58:31 -07:00
|
|
|
// Drop I16 types.
|
2020-01-10 14:48:24 -05:00
|
|
|
if (t.isSignlessInteger(16))
|
2019-06-19 13:58:31 -07:00
|
|
|
return success();
|
|
|
|
|
|
|
|
|
|
// Convert I64 to F64.
|
2020-01-10 14:48:24 -05:00
|
|
|
if (t.isSignlessInteger(64)) {
|
2019-06-19 13:58:31 -07:00
|
|
|
results.push_back(FloatType::getF64(t.getContext()));
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-02 13:24:04 +02:00
|
|
|
// Convert I42 to I43.
|
|
|
|
|
if (t.isInteger(42)) {
|
2020-12-17 12:24:45 -08:00
|
|
|
results.push_back(IntegerType::get(t.getContext(), 43));
|
2020-06-02 13:24:04 +02:00
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-21 09:29:46 -07:00
|
|
|
// Split F32 into F16,F16.
|
|
|
|
|
if (t.isF32()) {
|
|
|
|
|
results.assign(2, FloatType::getF16(t.getContext()));
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 13:58:31 -07:00
|
|
|
// Otherwise, convert the type directly.
|
|
|
|
|
results.push_back(t);
|
|
|
|
|
return success();
|
|
|
|
|
}
|
2019-06-21 09:29:46 -07:00
|
|
|
|
2020-06-02 13:24:04 +02:00
|
|
|
/// Hook for materializing a conversion. This is necessary because we generate
|
|
|
|
|
/// 1->N type mappings.
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
static Optional<Value> materializeCast(OpBuilder &builder, Type resultType,
|
|
|
|
|
ValueRange inputs, Location loc) {
|
|
|
|
|
return builder.create<TestCastOp>(loc, resultType, inputs).getResult();
|
2020-06-02 13:24:04 +02:00
|
|
|
}
|
2019-06-21 09:29:46 -07:00
|
|
|
};
|
|
|
|
|
|
2019-06-11 15:38:13 -07:00
|
|
|
struct TestLegalizePatternDriver
|
2020-04-07 13:56:16 -07:00
|
|
|
: public PassWrapper<TestLegalizePatternDriver, OperationPass<ModuleOp>> {
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final { return "test-legalize-patterns"; }
|
|
|
|
|
StringRef getDescription() const final {
|
|
|
|
|
return "Run test dialect legalization patterns";
|
|
|
|
|
}
|
2019-07-25 11:30:41 -07:00
|
|
|
/// The mode of conversion to use with the driver.
|
2019-09-16 10:37:48 -07:00
|
|
|
enum class ConversionMode { Analysis, Full, Partial };
|
2019-07-25 11:30:41 -07:00
|
|
|
|
|
|
|
|
TestLegalizePatternDriver(ConversionMode mode) : mode(mode) {}
|
|
|
|
|
|
2021-08-20 14:25:42 +03:00
|
|
|
void getDependentDialects(DialectRegistry ®istry) const override {
|
|
|
|
|
registry.insert<StandardOpsDialect>();
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-07 13:55:34 -07:00
|
|
|
void runOnOperation() override {
|
2019-07-20 19:05:41 -07:00
|
|
|
TestTypeConverter converter;
|
2021-03-22 16:58:34 -07:00
|
|
|
mlir::RewritePatternSet patterns(&getContext());
|
2021-03-21 10:38:35 -07:00
|
|
|
populateWithGenerated(patterns);
|
2021-03-22 16:58:34 -07:00
|
|
|
patterns
|
|
|
|
|
.add<TestRegionRewriteBlockMovement, TestRegionRewriteUndo,
|
|
|
|
|
TestCreateBlock, TestCreateIllegalBlock, TestUndoBlockArgReplace,
|
|
|
|
|
TestUndoBlockErase, TestPassthroughInvalidOp, TestSplitReturnType,
|
|
|
|
|
TestChangeProducerTypeI32ToF32, TestChangeProducerTypeF32ToF64,
|
|
|
|
|
TestChangeProducerTypeF32ToInvalid, TestUpdateConsumerType,
|
|
|
|
|
TestNonRootReplacement, TestBoundedRecursiveRewrite,
|
2021-08-20 14:25:42 +03:00
|
|
|
TestNestedOpCreationUndoRewrite, TestReplaceEraseOp,
|
|
|
|
|
TestCreateUnregisteredOp>(&getContext());
|
2021-03-22 16:58:34 -07:00
|
|
|
patterns.add<TestDropOpSignatureConversion>(&getContext(), converter);
|
2021-03-20 16:29:41 -07:00
|
|
|
mlir::populateFuncOpTypeConversionPattern(patterns, converter);
|
|
|
|
|
mlir::populateCallOpTypeConversionPattern(patterns, converter);
|
2019-05-27 20:04:56 -07:00
|
|
|
|
2019-07-18 18:20:03 -07:00
|
|
|
// Define the conversion target used for the test.
|
|
|
|
|
ConversionTarget target(getContext());
|
2021-03-11 23:58:02 +00:00
|
|
|
target.addLegalOp<ModuleOp>();
|
2021-08-20 14:25:42 +03:00
|
|
|
target.addLegalOp<LegalOpA, LegalOpB, LegalOpC, TestCastOp, TestValidOp,
|
2020-04-03 19:53:13 +02:00
|
|
|
TerminatorOp>();
|
2019-12-13 12:21:42 -08:00
|
|
|
target
|
|
|
|
|
.addIllegalOp<ILLegalOpF, TestRegionBuilderOp, TestOpWithRegionFold>();
|
2019-07-18 18:20:03 -07:00
|
|
|
target.addDynamicallyLegalOp<TestReturnOp>([](TestReturnOp op) {
|
|
|
|
|
// Don't allow F32 operands.
|
|
|
|
|
return llvm::none_of(op.getOperandTypes(),
|
|
|
|
|
[](Type type) { return type.isF32(); });
|
|
|
|
|
});
|
2020-06-18 15:45:43 -07:00
|
|
|
target.addDynamicallyLegalOp<FuncOp>([&](FuncOp op) {
|
|
|
|
|
return converter.isSignatureLegal(op.getType()) &&
|
|
|
|
|
converter.isLegal(&op.getBody());
|
|
|
|
|
});
|
2021-10-27 02:00:10 +00:00
|
|
|
target.addDynamicallyLegalOp<CallOp>(
|
|
|
|
|
[&](CallOp op) { return converter.isLegal(op); });
|
2019-07-25 11:30:41 -07:00
|
|
|
|
2021-10-12 23:14:57 +00:00
|
|
|
// TestCreateUnregisteredOp creates `arith.constant` operation,
|
2021-08-20 14:25:42 +03:00
|
|
|
// which was not added to target intentionally to test
|
|
|
|
|
// correct error code from conversion driver.
|
|
|
|
|
target.addDynamicallyLegalOp<ILLegalOpG>([](ILLegalOpG) { return false; });
|
|
|
|
|
|
2019-09-16 10:37:48 -07:00
|
|
|
// Expect the type_producer/type_consumer operations to only operate on f64.
|
|
|
|
|
target.addDynamicallyLegalOp<TestTypeProducerOp>(
|
|
|
|
|
[](TestTypeProducerOp op) { return op.getType().isF64(); });
|
|
|
|
|
target.addDynamicallyLegalOp<TestTypeConsumerOp>([](TestTypeConsumerOp op) {
|
2020-01-11 08:54:04 -08:00
|
|
|
return op.getOperand().getType().isF64();
|
2019-09-16 10:37:48 -07:00
|
|
|
});
|
|
|
|
|
|
2019-10-28 10:03:57 -07:00
|
|
|
// Check support for marking certain operations as recursively legal.
|
|
|
|
|
target.markOpRecursivelyLegal<FuncOp, ModuleOp>([](Operation *op) {
|
|
|
|
|
return static_cast<bool>(
|
|
|
|
|
op->getAttrOfType<UnitAttr>("test.recursively_legal"));
|
|
|
|
|
});
|
|
|
|
|
|
2020-04-09 12:38:52 -07:00
|
|
|
// Mark the bound recursion operation as dynamically legal.
|
|
|
|
|
target.addDynamicallyLegalOp<TestRecursiveRewriteOp>(
|
2021-10-20 07:08:36 -07:00
|
|
|
[](TestRecursiveRewriteOp op) { return op.getDepth() == 0; });
|
2020-04-09 12:38:52 -07:00
|
|
|
|
2019-07-25 11:30:41 -07:00
|
|
|
// Handle a partial conversion.
|
|
|
|
|
if (mode == ConversionMode::Partial) {
|
2020-04-30 09:47:19 -07:00
|
|
|
DenseSet<Operation *> unlegalizedOps;
|
2021-08-20 14:25:42 +03:00
|
|
|
if (failed(applyPartialConversion(
|
|
|
|
|
getOperation(), target, std::move(patterns), &unlegalizedOps))) {
|
|
|
|
|
getOperation()->emitRemark() << "applyPartialConversion failed";
|
|
|
|
|
}
|
2020-04-30 09:47:19 -07:00
|
|
|
// Emit remarks for each legalizable operation.
|
|
|
|
|
for (auto *op : unlegalizedOps)
|
|
|
|
|
op->emitRemark() << "op '" << op->getName() << "' is not legalizable";
|
2019-07-25 11:30:41 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-16 10:37:48 -07:00
|
|
|
// Handle a full conversion.
|
|
|
|
|
if (mode == ConversionMode::Full) {
|
2020-01-27 19:04:55 -08:00
|
|
|
// Check support for marking unknown operations as dynamically legal.
|
|
|
|
|
target.markUnknownOpDynamicallyLegal([](Operation *op) {
|
|
|
|
|
return (bool)op->getAttrOfType<UnitAttr>("test.dynamically_legal");
|
|
|
|
|
});
|
|
|
|
|
|
2021-08-20 14:25:42 +03:00
|
|
|
if (failed(applyFullConversion(getOperation(), target,
|
|
|
|
|
std::move(patterns)))) {
|
|
|
|
|
getOperation()->emitRemark() << "applyFullConversion failed";
|
|
|
|
|
}
|
2019-09-16 10:37:48 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-25 11:30:41 -07:00
|
|
|
// Otherwise, handle an analysis conversion.
|
|
|
|
|
assert(mode == ConversionMode::Analysis);
|
|
|
|
|
|
|
|
|
|
// Analyze the convertible operations.
|
|
|
|
|
DenseSet<Operation *> legalizedOps;
|
2020-10-26 17:25:01 -07:00
|
|
|
if (failed(applyAnalysisConversion(getOperation(), target,
|
|
|
|
|
std::move(patterns), legalizedOps)))
|
2019-07-25 11:30:41 -07:00
|
|
|
return signalPassFailure();
|
|
|
|
|
|
|
|
|
|
// Emit remarks for each legalizable operation.
|
|
|
|
|
for (auto *op : legalizedOps)
|
|
|
|
|
op->emitRemark() << "op '" << op->getName() << "' is legalizable";
|
2019-06-11 15:38:13 -07:00
|
|
|
}
|
2019-07-25 11:30:41 -07:00
|
|
|
|
|
|
|
|
/// The mode of conversion to use.
|
|
|
|
|
ConversionMode mode;
|
2019-06-11 15:38:13 -07:00
|
|
|
};
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
2019-07-25 11:30:41 -07:00
|
|
|
static llvm::cl::opt<TestLegalizePatternDriver::ConversionMode>
|
|
|
|
|
legalizerConversionMode(
|
|
|
|
|
"test-legalize-mode",
|
|
|
|
|
llvm::cl::desc("The legalization mode to use with the test driver"),
|
|
|
|
|
llvm::cl::init(TestLegalizePatternDriver::ConversionMode::Partial),
|
|
|
|
|
llvm::cl::values(
|
|
|
|
|
clEnumValN(TestLegalizePatternDriver::ConversionMode::Analysis,
|
|
|
|
|
"analysis", "Perform an analysis conversion"),
|
2019-09-16 10:37:48 -07:00
|
|
|
clEnumValN(TestLegalizePatternDriver::ConversionMode::Full, "full",
|
|
|
|
|
"Perform a full conversion"),
|
2019-07-25 11:30:41 -07:00
|
|
|
clEnumValN(TestLegalizePatternDriver::ConversionMode::Partial,
|
|
|
|
|
"partial", "Perform a partial conversion")));
|
|
|
|
|
|
2019-11-19 10:15:36 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// ConversionPatternRewriter::getRemappedValue testing. This method is used
|
2020-04-05 11:30:01 +09:00
|
|
|
// to get the remapped value of an original value that was replaced using
|
2019-11-19 10:15:36 -08:00
|
|
|
// ConversionPatternRewriter.
|
|
|
|
|
namespace {
|
2021-10-27 02:00:10 +00:00
|
|
|
struct TestRemapValueTypeConverter : public TypeConverter {
|
|
|
|
|
using TypeConverter::TypeConverter;
|
|
|
|
|
|
|
|
|
|
TestRemapValueTypeConverter() {
|
|
|
|
|
addConversion(
|
|
|
|
|
[](Float32Type type) { return Float64Type::get(type.getContext()); });
|
|
|
|
|
addConversion([](Type type) { return type; });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-11-19 10:15:36 -08:00
|
|
|
/// Converter that replaces a one-result one-operand OneVResOneVOperandOp1 with
|
|
|
|
|
/// a one-operand two-result OneVResOneVOperandOp1 by replicating its original
|
|
|
|
|
/// operand twice.
|
|
|
|
|
///
|
|
|
|
|
/// Example:
|
|
|
|
|
/// %1 = test.one_variadic_out_one_variadic_in1"(%0)
|
|
|
|
|
/// is replaced with:
|
|
|
|
|
/// %1 = test.one_variadic_out_one_variadic_in1"(%0, %0)
|
|
|
|
|
struct OneVResOneVOperandOp1Converter
|
|
|
|
|
: public OpConversionPattern<OneVResOneVOperandOp1> {
|
|
|
|
|
using OpConversionPattern<OneVResOneVOperandOp1>::OpConversionPattern;
|
|
|
|
|
|
2020-03-17 20:07:55 -07:00
|
|
|
LogicalResult
|
2021-09-24 17:51:20 +00:00
|
|
|
matchAndRewrite(OneVResOneVOperandOp1 op, OpAdaptor adaptor,
|
2019-11-19 10:15:36 -08:00
|
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
|
|
|
auto origOps = op.getOperands();
|
|
|
|
|
assert(std::distance(origOps.begin(), origOps.end()) == 1 &&
|
|
|
|
|
"One operand expected");
|
2019-12-23 14:45:01 -08:00
|
|
|
Value origOp = *origOps.begin();
|
|
|
|
|
SmallVector<Value, 2> remappedOperands;
|
2019-11-19 10:15:36 -08:00
|
|
|
// Replicate the remapped original operand twice. Note that we don't used
|
|
|
|
|
// the remapped 'operand' since the goal is testing 'getRemappedValue'.
|
|
|
|
|
remappedOperands.push_back(rewriter.getRemappedValue(origOp));
|
|
|
|
|
remappedOperands.push_back(rewriter.getRemappedValue(origOp));
|
|
|
|
|
|
2020-01-27 19:57:14 -08:00
|
|
|
rewriter.replaceOpWithNewOp<OneVResOneVOperandOp1>(op, op.getResultTypes(),
|
2019-11-19 10:15:36 -08:00
|
|
|
remappedOperands);
|
2020-03-17 20:07:55 -07:00
|
|
|
return success();
|
2019-11-19 10:15:36 -08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-10-27 02:00:10 +00:00
|
|
|
/// A rewriter pattern that tests that blocks can be merged.
|
|
|
|
|
struct TestRemapValueInRegion
|
|
|
|
|
: public OpConversionPattern<TestRemappedValueRegionOp> {
|
|
|
|
|
using OpConversionPattern<TestRemappedValueRegionOp>::OpConversionPattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
|
matchAndRewrite(TestRemappedValueRegionOp op, OpAdaptor adaptor,
|
|
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
Block &block = op.getBody().front();
|
|
|
|
|
Operation *terminator = block.getTerminator();
|
|
|
|
|
|
|
|
|
|
// Merge the block into the parent region.
|
|
|
|
|
Block *parentBlock = op->getBlock();
|
|
|
|
|
Block *finalBlock = rewriter.splitBlock(parentBlock, op->getIterator());
|
|
|
|
|
rewriter.mergeBlocks(&block, parentBlock, ValueRange());
|
|
|
|
|
rewriter.mergeBlocks(finalBlock, parentBlock, ValueRange());
|
|
|
|
|
|
|
|
|
|
// Replace the results of this operation with the remapped terminator
|
|
|
|
|
// values.
|
|
|
|
|
SmallVector<Value> terminatorOperands;
|
|
|
|
|
if (failed(rewriter.getRemappedValues(terminator->getOperands(),
|
|
|
|
|
terminatorOperands)))
|
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
|
|
rewriter.eraseOp(terminator);
|
|
|
|
|
rewriter.replaceOp(op, terminatorOperands);
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-07 13:56:16 -07:00
|
|
|
struct TestRemappedValue
|
|
|
|
|
: public mlir::PassWrapper<TestRemappedValue, FunctionPass> {
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final { return "test-remapped-value"; }
|
|
|
|
|
StringRef getDescription() const final {
|
|
|
|
|
return "Test public remapped value mechanism in ConversionPatternRewriter";
|
|
|
|
|
}
|
2019-11-19 10:15:36 -08:00
|
|
|
void runOnFunction() override {
|
2021-10-27 02:00:10 +00:00
|
|
|
TestRemapValueTypeConverter typeConverter;
|
|
|
|
|
|
2021-03-22 16:58:34 -07:00
|
|
|
mlir::RewritePatternSet patterns(&getContext());
|
|
|
|
|
patterns.add<OneVResOneVOperandOp1Converter>(&getContext());
|
2021-10-27 02:00:10 +00:00
|
|
|
patterns.add<TestChangeProducerTypeF32ToF64, TestUpdateConsumerType>(
|
|
|
|
|
&getContext());
|
|
|
|
|
patterns.add<TestRemapValueInRegion>(typeConverter, &getContext());
|
2019-11-19 10:15:36 -08:00
|
|
|
|
|
|
|
|
mlir::ConversionTarget target(getContext());
|
2021-03-11 23:58:02 +00:00
|
|
|
target.addLegalOp<ModuleOp, FuncOp, TestReturnOp>();
|
2021-10-27 02:00:10 +00:00
|
|
|
|
|
|
|
|
// Expect the type_producer/type_consumer operations to only operate on f64.
|
|
|
|
|
target.addDynamicallyLegalOp<TestTypeProducerOp>(
|
|
|
|
|
[](TestTypeProducerOp op) { return op.getType().isF64(); });
|
|
|
|
|
target.addDynamicallyLegalOp<TestTypeConsumerOp>([](TestTypeConsumerOp op) {
|
|
|
|
|
return op.getOperand().getType().isF64();
|
|
|
|
|
});
|
|
|
|
|
|
2019-11-19 10:15:36 -08:00
|
|
|
// We make OneVResOneVOperandOp1 legal only when it has more that one
|
|
|
|
|
// operand. This will trigger the conversion that will replace one-operand
|
|
|
|
|
// OneVResOneVOperandOp1 with two-operand OneVResOneVOperandOp1.
|
|
|
|
|
target.addDynamicallyLegalOp<OneVResOneVOperandOp1>(
|
2021-10-27 02:00:10 +00:00
|
|
|
[](Operation *op) { return op->getNumOperands() > 1; });
|
2019-11-19 10:15:36 -08:00
|
|
|
|
2020-10-26 17:25:01 -07:00
|
|
|
if (failed(mlir::applyFullConversion(getFunction(), target,
|
|
|
|
|
std::move(patterns)))) {
|
2019-11-19 10:15:36 -08:00
|
|
|
signalPassFailure();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
2020-06-18 13:58:25 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Test patterns without a specific root operation kind
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
/// This pattern matches and removes any operation in the test dialect.
|
|
|
|
|
struct RemoveTestDialectOps : public RewritePattern {
|
2021-03-23 13:44:14 -07:00
|
|
|
RemoveTestDialectOps(MLIRContext *context)
|
|
|
|
|
: RewritePattern(MatchAnyOpTypeTag(), /*benefit=*/1, context) {}
|
2020-06-18 13:58:25 -07:00
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(Operation *op,
|
|
|
|
|
PatternRewriter &rewriter) const override {
|
|
|
|
|
if (!isa<TestDialect>(op->getDialect()))
|
|
|
|
|
return failure();
|
|
|
|
|
rewriter.eraseOp(op);
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct TestUnknownRootOpDriver
|
|
|
|
|
: public mlir::PassWrapper<TestUnknownRootOpDriver, FunctionPass> {
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final {
|
|
|
|
|
return "test-legalize-unknown-root-patterns";
|
|
|
|
|
}
|
|
|
|
|
StringRef getDescription() const final {
|
|
|
|
|
return "Test public remapped value mechanism in ConversionPatternRewriter";
|
|
|
|
|
}
|
2020-06-18 13:58:25 -07:00
|
|
|
void runOnFunction() override {
|
2021-03-22 16:58:34 -07:00
|
|
|
mlir::RewritePatternSet patterns(&getContext());
|
2021-03-23 13:44:14 -07:00
|
|
|
patterns.add<RemoveTestDialectOps>(&getContext());
|
2020-06-18 13:58:25 -07:00
|
|
|
|
|
|
|
|
mlir::ConversionTarget target(getContext());
|
|
|
|
|
target.addIllegalDialect<TestDialect>();
|
2020-10-26 17:25:01 -07:00
|
|
|
if (failed(
|
|
|
|
|
applyPartialConversion(getFunction(), target, std::move(patterns))))
|
2020-06-18 13:58:25 -07:00
|
|
|
signalPassFailure();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Test type conversions
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
struct TestTypeConversionProducer
|
|
|
|
|
: public OpConversionPattern<TestTypeProducerOp> {
|
|
|
|
|
using OpConversionPattern<TestTypeProducerOp>::OpConversionPattern;
|
|
|
|
|
LogicalResult
|
2021-09-24 17:51:20 +00:00
|
|
|
matchAndRewrite(TestTypeProducerOp op, OpAdaptor adaptor,
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
Type resultType = op.getType();
|
|
|
|
|
if (resultType.isa<FloatType>())
|
|
|
|
|
resultType = rewriter.getF64Type();
|
|
|
|
|
else if (resultType.isInteger(16))
|
|
|
|
|
resultType = rewriter.getIntegerType(64);
|
|
|
|
|
else
|
|
|
|
|
return failure();
|
|
|
|
|
|
|
|
|
|
rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, resultType);
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-01-29 19:41:10 +01:00
|
|
|
/// Call signature conversion and then fail the rewrite to trigger the undo
|
|
|
|
|
/// mechanism.
|
|
|
|
|
struct TestSignatureConversionUndo
|
|
|
|
|
: public OpConversionPattern<TestSignatureConversionUndoOp> {
|
|
|
|
|
using OpConversionPattern<TestSignatureConversionUndoOp>::OpConversionPattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
2021-09-24 17:51:20 +00:00
|
|
|
matchAndRewrite(TestSignatureConversionUndoOp op, OpAdaptor adaptor,
|
2021-01-29 19:41:10 +01:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
(void)rewriter.convertRegionTypes(&op->getRegion(0), *getTypeConverter());
|
|
|
|
|
return failure();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2021-11-05 18:43:26 +00:00
|
|
|
/// Call signature conversion without providing a type converter to handle
|
|
|
|
|
/// materializations.
|
|
|
|
|
struct TestTestSignatureConversionNoConverter
|
|
|
|
|
: public OpConversionPattern<TestSignatureConversionNoConverterOp> {
|
|
|
|
|
TestTestSignatureConversionNoConverter(TypeConverter &converter,
|
|
|
|
|
MLIRContext *context)
|
|
|
|
|
: OpConversionPattern<TestSignatureConversionNoConverterOp>(context),
|
|
|
|
|
converter(converter) {}
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
|
|
|
|
matchAndRewrite(TestSignatureConversionNoConverterOp op, OpAdaptor adaptor,
|
|
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
Region ®ion = op->getRegion(0);
|
|
|
|
|
Block *entry = ®ion.front();
|
|
|
|
|
|
|
|
|
|
// Convert the original entry arguments.
|
|
|
|
|
TypeConverter::SignatureConversion result(entry->getNumArguments());
|
|
|
|
|
if (failed(
|
|
|
|
|
converter.convertSignatureArgs(entry->getArgumentTypes(), result)))
|
|
|
|
|
return failure();
|
|
|
|
|
rewriter.updateRootInPlace(
|
|
|
|
|
op, [&] { rewriter.applySignatureConversion(®ion, result); });
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TypeConverter &converter;
|
|
|
|
|
};
|
|
|
|
|
|
2021-01-29 19:41:10 +01:00
|
|
|
/// Just forward the operands to the root op. This is essentially a no-op
|
|
|
|
|
/// pattern that is used to trigger target materialization.
|
|
|
|
|
struct TestTypeConsumerForward
|
|
|
|
|
: public OpConversionPattern<TestTypeConsumerOp> {
|
|
|
|
|
using OpConversionPattern<TestTypeConsumerOp>::OpConversionPattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
2021-09-24 17:51:20 +00:00
|
|
|
matchAndRewrite(TestTypeConsumerOp op, OpAdaptor adaptor,
|
2021-01-29 19:41:10 +01:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
2021-09-24 17:51:20 +00:00
|
|
|
rewriter.updateRootInPlace(op,
|
|
|
|
|
[&] { op->setOperands(adaptor.getOperands()); });
|
2021-01-29 19:41:10 +01:00
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
[mlir] Apply source materialization in case of transitive conversion
In dialect conversion infrastructure, source materialization applies as part of
the finalization procedure to results of the newly produced operations that
replace previously existing values with values having a different type.
However, such operations may be created to replace operations created in other
patterns. At this point, it is possible that the results of the _original_
operation are still in use and have mismatching types, but the results of the
_intermediate_ operation that performed the type change are not in use leading
to the absence of source materialization. For example,
%0 = dialect.produce : !dialect.A
dialect.use %0 : !dialect.A
can be replaced with
%0 = dialect.other : !dialect.A
%1 = dialect.produce : !dialect.A // replaced, scheduled for removal
dialect.use %1 : !dialect.A
and then with
%0 = dialect.final : !dialect.B
%1 = dialect.other : !dialect.A // replaced, scheduled for removal
%2 = dialect.produce : !dialect.A // replaced, scheduled for removal
dialect.use %2 : !dialect.A
in the same rewriting, but only the %1->%0 replacement is currently considered.
Change the logic in dialect conversion to look up all values that were replaced
by the given value and performing source materialization if any of those values
is still in use with mismatching types. This is performed by computing the
inverse value replacement mapping. This arguably expensive manipulation is
performed only if there were some type-changing replacements. An alternative
could be to consider all replaced operations and not only those that resulted
in type changes, but it would harm pattern-level composability: the pattern
that performed the non-type-changing replacement would have to be made aware of
the type converter in order to call the materialization hook.
Reviewed By: rriddle
Differential Revision: https://reviews.llvm.org/D95626
2021-01-28 17:43:13 +01:00
|
|
|
struct TestTypeConversionAnotherProducer
|
|
|
|
|
: public OpRewritePattern<TestAnotherTypeProducerOp> {
|
|
|
|
|
using OpRewritePattern<TestAnotherTypeProducerOp>::OpRewritePattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(TestAnotherTypeProducerOp op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
|
|
|
|
rewriter.replaceOpWithNewOp<TestTypeProducerOp>(op, op.getType());
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
struct TestTypeConversionDriver
|
|
|
|
|
: public PassWrapper<TestTypeConversionDriver, OperationPass<ModuleOp>> {
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-18 20:01:19 +00:00
|
|
|
void getDependentDialects(DialectRegistry ®istry) const override {
|
|
|
|
|
registry.insert<TestDialect>();
|
|
|
|
|
}
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final {
|
|
|
|
|
return "test-legalize-type-conversion";
|
|
|
|
|
}
|
|
|
|
|
StringRef getDescription() const final {
|
|
|
|
|
return "Test various type conversion functionalities in DialectConversion";
|
|
|
|
|
}
|
Separate the Registration from Loading dialects in the Context
This changes the behavior of constructing MLIRContext to no longer load globally
registered dialects on construction. Instead Dialects are only loaded explicitly
on demand:
- the Parser is lazily loading Dialects in the context as it encounters them
during parsing. This is the only purpose for registering dialects and not load
them in the context.
- Passes are expected to declare the dialects they will create entity from
(Operations, Attributes, or Types), and the PassManager is loading Dialects into
the Context when starting a pipeline.
This changes simplifies the configuration of the registration: a compiler only
need to load the dialect for the IR it will emit, and the optimizer is
self-contained and load the required Dialects. For example in the Toy tutorial,
the compiler only needs to load the Toy dialect in the Context, all the others
(linalg, affine, std, LLVM, ...) are automatically loaded depending on the
optimization pipeline enabled.
To adjust to this change, stop using the existing dialect registration: the
global registry will be removed soon.
1) For passes, you need to override the method:
virtual void getDependentDialects(DialectRegistry ®istry) const {}
and registery on the provided registry any dialect that this pass can produce.
Passes defined in TableGen can provide this list in the dependentDialects list
field.
2) For dialects, on construction you can register dependent dialects using the
provided MLIRContext: `context.getOrLoadDialect<DialectName>()`
This is useful if a dialect may canonicalize or have interfaces involving
another dialect.
3) For loading IR, dialect that can be in the input file must be explicitly
registered with the context. `MlirOptMain()` is taking an explicit registry for
this purpose. See how the standalone-opt.cpp example is setup:
mlir::DialectRegistry registry;
registry.insert<mlir::standalone::StandaloneDialect>();
registry.insert<mlir::StandardOpsDialect>();
Only operations from these two dialects can be in the input file. To include all
of the dialects in MLIR Core, you can populate the registry this way:
mlir::registerAllDialects(registry);
4) For `mlir-translate` callback, as well as frontend, Dialects can be loaded in
the context before emitting the IR: context.getOrLoadDialect<ToyDialect>()
Differential Revision: https://reviews.llvm.org/D85622
2020-08-18 20:01:19 +00:00
|
|
|
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
void runOnOperation() override {
|
|
|
|
|
// Initialize the type converter.
|
|
|
|
|
TypeConverter converter;
|
|
|
|
|
|
|
|
|
|
/// Add the legal set of type conversions.
|
|
|
|
|
converter.addConversion([](Type type) -> Type {
|
|
|
|
|
// Treat F64 as legal.
|
|
|
|
|
if (type.isF64())
|
|
|
|
|
return type;
|
|
|
|
|
// Allow converting BF16/F16/F32 to F64.
|
|
|
|
|
if (type.isBF16() || type.isF16() || type.isF32())
|
|
|
|
|
return FloatType::getF64(type.getContext());
|
|
|
|
|
// Otherwise, the type is illegal.
|
|
|
|
|
return nullptr;
|
|
|
|
|
});
|
|
|
|
|
converter.addConversion([](IntegerType type, SmallVectorImpl<Type> &) {
|
|
|
|
|
// Drop all integer types.
|
|
|
|
|
return success();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/// Add the legal set of type materializations.
|
|
|
|
|
converter.addSourceMaterialization([](OpBuilder &builder, Type resultType,
|
|
|
|
|
ValueRange inputs,
|
|
|
|
|
Location loc) -> Value {
|
|
|
|
|
// Allow casting from F64 back to F32.
|
|
|
|
|
if (!resultType.isF16() && inputs.size() == 1 &&
|
|
|
|
|
inputs[0].getType().isF64())
|
|
|
|
|
return builder.create<TestCastOp>(loc, resultType, inputs).getResult();
|
|
|
|
|
// Allow producing an i32 or i64 from nothing.
|
|
|
|
|
if ((resultType.isInteger(32) || resultType.isInteger(64)) &&
|
|
|
|
|
inputs.empty())
|
|
|
|
|
return builder.create<TestTypeProducerOp>(loc, resultType);
|
|
|
|
|
// Allow producing an i64 from an integer.
|
|
|
|
|
if (resultType.isa<IntegerType>() && inputs.size() == 1 &&
|
|
|
|
|
inputs[0].getType().isa<IntegerType>())
|
|
|
|
|
return builder.create<TestCastOp>(loc, resultType, inputs).getResult();
|
|
|
|
|
// Otherwise, fail.
|
|
|
|
|
return nullptr;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Initialize the conversion target.
|
|
|
|
|
mlir::ConversionTarget target(getContext());
|
|
|
|
|
target.addDynamicallyLegalOp<TestTypeProducerOp>([](TestTypeProducerOp op) {
|
|
|
|
|
return op.getType().isF64() || op.getType().isInteger(64);
|
|
|
|
|
});
|
|
|
|
|
target.addDynamicallyLegalOp<FuncOp>([&](FuncOp op) {
|
|
|
|
|
return converter.isSignatureLegal(op.getType()) &&
|
|
|
|
|
converter.isLegal(&op.getBody());
|
|
|
|
|
});
|
|
|
|
|
target.addDynamicallyLegalOp<TestCastOp>([&](TestCastOp op) {
|
|
|
|
|
// Allow casts from F64 to F32.
|
|
|
|
|
return (*op.operand_type_begin()).isF64() && op.getType().isF32();
|
|
|
|
|
});
|
2021-11-05 18:43:26 +00:00
|
|
|
target.addDynamicallyLegalOp<TestSignatureConversionNoConverterOp>(
|
|
|
|
|
[&](TestSignatureConversionNoConverterOp op) {
|
|
|
|
|
return converter.isLegal(op.getRegion().front().getArgumentTypes());
|
|
|
|
|
});
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
|
|
|
|
|
// Initialize the set of rewrite patterns.
|
2021-03-22 16:58:34 -07:00
|
|
|
RewritePatternSet patterns(&getContext());
|
|
|
|
|
patterns.add<TestTypeConsumerForward, TestTypeConversionProducer,
|
2021-11-05 18:43:26 +00:00
|
|
|
TestSignatureConversionUndo,
|
|
|
|
|
TestTestSignatureConversionNoConverter>(converter,
|
|
|
|
|
&getContext());
|
2021-03-22 16:58:34 -07:00
|
|
|
patterns.add<TestTypeConversionAnotherProducer>(&getContext());
|
2021-03-20 16:29:41 -07:00
|
|
|
mlir::populateFuncOpTypeConversionPattern(patterns, converter);
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
|
2020-10-26 17:25:01 -07:00
|
|
|
if (failed(applyPartialConversion(getOperation(), target,
|
|
|
|
|
std::move(patterns))))
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
signalPassFailure();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
2021-01-14 11:57:17 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Test Block Merging
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2020-08-03 10:04:16 -07:00
|
|
|
namespace {
|
|
|
|
|
/// A rewriter pattern that tests that blocks can be merged.
|
|
|
|
|
struct TestMergeBlock : public OpConversionPattern<TestMergeBlocksOp> {
|
|
|
|
|
using OpConversionPattern<TestMergeBlocksOp>::OpConversionPattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
2021-09-24 17:51:20 +00:00
|
|
|
matchAndRewrite(TestMergeBlocksOp op, OpAdaptor adaptor,
|
2020-08-03 10:04:16 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
2021-10-20 07:08:36 -07:00
|
|
|
Block &firstBlock = op.getBody().front();
|
2020-08-03 10:04:16 -07:00
|
|
|
Operation *branchOp = firstBlock.getTerminator();
|
2021-10-20 07:08:36 -07:00
|
|
|
Block *secondBlock = &*(std::next(op.getBody().begin()));
|
2020-08-03 10:04:16 -07:00
|
|
|
auto succOperands = branchOp->getOperands();
|
|
|
|
|
SmallVector<Value, 2> replacements(succOperands);
|
|
|
|
|
rewriter.eraseOp(branchOp);
|
|
|
|
|
rewriter.mergeBlocks(secondBlock, &firstBlock, replacements);
|
|
|
|
|
rewriter.updateRootInPlace(op, [] {});
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// A rewrite pattern to tests the undo mechanism of blocks being merged.
|
|
|
|
|
struct TestUndoBlocksMerge : public ConversionPattern {
|
|
|
|
|
TestUndoBlocksMerge(MLIRContext *ctx)
|
|
|
|
|
: ConversionPattern("test.undo_blocks_merge", /*benefit=*/1, ctx) {}
|
|
|
|
|
LogicalResult
|
|
|
|
|
matchAndRewrite(Operation *op, ArrayRef<Value> operands,
|
|
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
Block &firstBlock = op->getRegion(0).front();
|
|
|
|
|
Operation *branchOp = firstBlock.getTerminator();
|
|
|
|
|
Block *secondBlock = &*(std::next(op->getRegion(0).begin()));
|
|
|
|
|
rewriter.setInsertionPointToStart(secondBlock);
|
|
|
|
|
rewriter.create<ILLegalOpF>(op->getLoc(), rewriter.getF32Type());
|
|
|
|
|
auto succOperands = branchOp->getOperands();
|
|
|
|
|
SmallVector<Value, 2> replacements(succOperands);
|
|
|
|
|
rewriter.eraseOp(branchOp);
|
|
|
|
|
rewriter.mergeBlocks(secondBlock, &firstBlock, replacements);
|
|
|
|
|
rewriter.updateRootInPlace(op, [] {});
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// A rewrite mechanism to inline the body of the op into its parent, when both
|
|
|
|
|
/// ops can have a single block.
|
|
|
|
|
struct TestMergeSingleBlockOps
|
|
|
|
|
: public OpConversionPattern<SingleBlockImplicitTerminatorOp> {
|
|
|
|
|
using OpConversionPattern<
|
|
|
|
|
SingleBlockImplicitTerminatorOp>::OpConversionPattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult
|
2021-09-24 17:51:20 +00:00
|
|
|
matchAndRewrite(SingleBlockImplicitTerminatorOp op, OpAdaptor adaptor,
|
2020-08-03 10:04:16 -07:00
|
|
|
ConversionPatternRewriter &rewriter) const final {
|
|
|
|
|
SingleBlockImplicitTerminatorOp parentOp =
|
2020-12-09 11:50:18 +01:00
|
|
|
op->getParentOfType<SingleBlockImplicitTerminatorOp>();
|
2020-08-03 10:04:16 -07:00
|
|
|
if (!parentOp)
|
|
|
|
|
return failure();
|
2021-10-20 07:08:36 -07:00
|
|
|
Block &innerBlock = op.getRegion().front();
|
2020-08-03 10:04:16 -07:00
|
|
|
TerminatorOp innerTerminator =
|
|
|
|
|
cast<TerminatorOp>(innerBlock.getTerminator());
|
2020-08-19 16:07:42 -07:00
|
|
|
rewriter.mergeBlockBefore(&innerBlock, op);
|
2020-08-03 10:04:16 -07:00
|
|
|
rewriter.eraseOp(innerTerminator);
|
|
|
|
|
rewriter.eraseOp(op);
|
|
|
|
|
rewriter.updateRootInPlace(op, [] {});
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct TestMergeBlocksPatternDriver
|
|
|
|
|
: public PassWrapper<TestMergeBlocksPatternDriver,
|
|
|
|
|
OperationPass<ModuleOp>> {
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final { return "test-merge-blocks"; }
|
|
|
|
|
StringRef getDescription() const final {
|
|
|
|
|
return "Test Merging operation in ConversionPatternRewriter";
|
|
|
|
|
}
|
2020-08-03 10:04:16 -07:00
|
|
|
void runOnOperation() override {
|
|
|
|
|
MLIRContext *context = &getContext();
|
2021-03-22 16:58:34 -07:00
|
|
|
mlir::RewritePatternSet patterns(context);
|
|
|
|
|
patterns.add<TestMergeBlock, TestUndoBlocksMerge, TestMergeSingleBlockOps>(
|
|
|
|
|
context);
|
2020-08-03 10:04:16 -07:00
|
|
|
ConversionTarget target(*context);
|
2021-03-11 23:58:02 +00:00
|
|
|
target.addLegalOp<FuncOp, ModuleOp, TerminatorOp, TestBranchOp,
|
|
|
|
|
TestTypeConsumerOp, TestTypeProducerOp, TestReturnOp>();
|
2020-08-03 10:04:16 -07:00
|
|
|
target.addIllegalOp<ILLegalOpF>();
|
|
|
|
|
|
|
|
|
|
/// Expect the op to have a single block after legalization.
|
|
|
|
|
target.addDynamicallyLegalOp<TestMergeBlocksOp>(
|
|
|
|
|
[&](TestMergeBlocksOp op) -> bool {
|
2021-10-20 07:08:36 -07:00
|
|
|
return llvm::hasSingleElement(op.getBody());
|
2020-08-03 10:04:16 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/// Only allow `test.br` within test.merge_blocks op.
|
|
|
|
|
target.addDynamicallyLegalOp<TestBranchOp>([&](TestBranchOp op) -> bool {
|
2020-12-09 11:50:18 +01:00
|
|
|
return op->getParentOfType<TestMergeBlocksOp>();
|
2020-08-03 10:04:16 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/// Expect that all nested test.SingleBlockImplicitTerminator ops are
|
|
|
|
|
/// inlined.
|
|
|
|
|
target.addDynamicallyLegalOp<SingleBlockImplicitTerminatorOp>(
|
|
|
|
|
[&](SingleBlockImplicitTerminatorOp op) -> bool {
|
2020-12-09 11:50:18 +01:00
|
|
|
return !op->getParentOfType<SingleBlockImplicitTerminatorOp>();
|
2020-08-03 10:04:16 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
DenseSet<Operation *> unlegalizedOps;
|
2020-10-26 17:25:01 -07:00
|
|
|
(void)applyPartialConversion(getOperation(), target, std::move(patterns),
|
2020-08-03 10:04:16 -07:00
|
|
|
&unlegalizedOps);
|
|
|
|
|
for (auto *op : unlegalizedOps)
|
|
|
|
|
op->emitRemark() << "op '" << op->getName() << "' is not legalizable";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2021-01-14 11:57:17 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// Test Selective Replacement
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
/// A rewrite mechanism to inline the body of the op into its parent, when both
|
|
|
|
|
/// ops can have a single block.
|
|
|
|
|
struct TestSelectiveOpReplacementPattern : public OpRewritePattern<TestCastOp> {
|
|
|
|
|
using OpRewritePattern<TestCastOp>::OpRewritePattern;
|
|
|
|
|
|
|
|
|
|
LogicalResult matchAndRewrite(TestCastOp op,
|
|
|
|
|
PatternRewriter &rewriter) const final {
|
|
|
|
|
if (op.getNumOperands() != 2)
|
|
|
|
|
return failure();
|
|
|
|
|
OperandRange operands = op.getOperands();
|
|
|
|
|
|
|
|
|
|
// Replace non-terminator uses with the first operand.
|
|
|
|
|
rewriter.replaceOpWithIf(op, operands[0], [](OpOperand &operand) {
|
2021-02-09 11:41:10 -08:00
|
|
|
return operand.getOwner()->hasTrait<OpTrait::IsTerminator>();
|
2021-01-14 11:57:17 -08:00
|
|
|
});
|
|
|
|
|
// Replace everything else with the second operand if the operation isn't
|
|
|
|
|
// dead.
|
|
|
|
|
rewriter.replaceOp(op, op.getOperand(1));
|
|
|
|
|
return success();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct TestSelectiveReplacementPatternDriver
|
|
|
|
|
: public PassWrapper<TestSelectiveReplacementPatternDriver,
|
|
|
|
|
OperationPass<>> {
|
2021-06-16 23:42:13 +00:00
|
|
|
StringRef getArgument() const final {
|
|
|
|
|
return "test-pattern-selective-replacement";
|
|
|
|
|
}
|
|
|
|
|
StringRef getDescription() const final {
|
|
|
|
|
return "Test selective replacement in the PatternRewriter";
|
|
|
|
|
}
|
2021-01-14 11:57:17 -08:00
|
|
|
void runOnOperation() override {
|
|
|
|
|
MLIRContext *context = &getContext();
|
2021-03-22 16:58:34 -07:00
|
|
|
mlir::RewritePatternSet patterns(context);
|
|
|
|
|
patterns.add<TestSelectiveOpReplacementPattern>(context);
|
2021-02-04 14:57:50 -08:00
|
|
|
(void)applyPatternsAndFoldGreedily(getOperation()->getRegions(),
|
|
|
|
|
std::move(patterns));
|
2021-01-14 11:57:17 -08:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
// PassRegistration
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2020-02-12 09:03:40 +00:00
|
|
|
namespace mlir {
|
2020-11-04 22:18:26 +01:00
|
|
|
namespace test {
|
2020-02-12 09:03:40 +00:00
|
|
|
void registerPatternsTestPass() {
|
2021-06-16 23:42:13 +00:00
|
|
|
PassRegistration<TestReturnTypeDriver>();
|
2020-02-12 09:03:40 +00:00
|
|
|
|
2021-06-16 23:42:13 +00:00
|
|
|
PassRegistration<TestDerivedAttributeDriver>();
|
2020-04-16 08:05:21 -07:00
|
|
|
|
2021-06-16 23:42:13 +00:00
|
|
|
PassRegistration<TestPatternDriver>();
|
2020-02-12 09:03:40 +00:00
|
|
|
|
2021-06-16 23:42:13 +00:00
|
|
|
PassRegistration<TestLegalizePatternDriver>([] {
|
|
|
|
|
return std::make_unique<TestLegalizePatternDriver>(legalizerConversionMode);
|
|
|
|
|
});
|
2020-02-12 09:03:40 +00:00
|
|
|
|
2021-06-16 23:42:13 +00:00
|
|
|
PassRegistration<TestRemappedValue>();
|
2020-06-18 13:58:25 -07:00
|
|
|
|
2021-06-16 23:42:13 +00:00
|
|
|
PassRegistration<TestUnknownRootOpDriver>();
|
[mlir][DialectConversion] Enable deeper integration of type conversions
This revision adds support for much deeper type conversion integration into the conversion process, and enables auto-generating cast operations when necessary. Type conversions are now largely automatically managed by the conversion infra when using a ConversionPattern with a provided TypeConverter. This removes the need for patterns to do type cast wrapping themselves and moves the burden to the infra. This makes it much easier to perform partial lowerings when type conversions are involved, as any lingering type conversions will be automatically resolved/legalized by the conversion infra.
To support this new integration, a few changes have been made to the type materialization API on TypeConverter. Materialization has been split into three separate categories:
* Argument Materialization: This type of materialization is used when converting the type of block arguments when calling `convertRegionTypes`. This is useful for contextually inserting additional conversion operations when converting a block argument type, such as when converting the types of a function signature.
* Source Materialization: This type of materialization is used to convert a legal type of the converter into a non-legal type, generally a source type. This may be called when uses of a non-legal type persist after the conversion process has finished.
* Target Materialization: This type of materialization is used to convert a non-legal, or source, type into a legal, or target, type. This type of materialization is used when applying a pattern on an operation, but the types of the operands have not yet been converted.
Differential Revision: https://reviews.llvm.org/D82831
2020-07-23 19:38:30 -07:00
|
|
|
|
2021-06-16 23:42:13 +00:00
|
|
|
PassRegistration<TestTypeConversionDriver>();
|
2020-08-03 10:04:16 -07:00
|
|
|
|
2021-06-16 23:42:13 +00:00
|
|
|
PassRegistration<TestMergeBlocksPatternDriver>();
|
|
|
|
|
PassRegistration<TestSelectiveReplacementPatternDriver>();
|
2020-02-12 09:03:40 +00:00
|
|
|
}
|
2020-11-04 22:18:26 +01:00
|
|
|
} // namespace test
|
2020-02-12 09:03:40 +00:00
|
|
|
} // namespace mlir
|