//===- TestPatterns.cpp - Test dialect pattern driver ---------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "TestDialect.h" #include "TestOps.h" #include "TestTypes.h" #include "mlir/Dialect/Arith/IR/Arith.h" #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Dialect/Func/Transforms/FuncConversions.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" #include "mlir/IR/Matchers.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/DialectConversion.h" #include "mlir/Transforms/FoldUtils.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "llvm/ADT/ScopeExit.h" usingnamespacemlir; usingnamespacetest; // Native function for testing NativeCodeCall static Value chooseOperand(Value input1, Value input2, BoolAttr choice) { … } static void createOpI(PatternRewriter &rewriter, Location loc, Value input) { … } static void handleNoResultOp(PatternRewriter &rewriter, OpSymbolBindingNoResult op) { … } static bool getFirstI32Result(Operation *op, Value &value) { … } static Value bindNativeCodeCallResult(Value value) { … } static SmallVector<Value, 2> bindMultipleNativeCodeCallResult(Value input1, Value input2) { … } // 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 = …; static Attribute opMTest(PatternRewriter &rewriter, Value val) { … } namespace { #include "TestPatterns.inc" } // namespace //===----------------------------------------------------------------------===// // Test Reduce Pattern Interface //===----------------------------------------------------------------------===// void test::populateTestReductionPatterns(RewritePatternSet &patterns) { … } //===----------------------------------------------------------------------===// // Canonicalizer Driver. //===----------------------------------------------------------------------===// namespace { struct FoldingPattern : public RewritePattern { … }; /// 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> { … }; /// This pattern matches test.op_commutative2 with the first operand being /// another test.op_commutative2 with a constant on the right side and fold it /// away by propagating it as its result. This is intend to check that patterns /// are applied after the commutative property moves constant to the right. struct FolderCommutativeOp2WithConstant : public OpRewritePattern<TestCommutative2Op> { … }; /// This pattern matches test.any_attr_of_i32_str ops. In case of an integer /// attribute with value smaller than MaxVal, it increments the value by 1. template <int MaxVal> struct IncrementIntAttribute : public OpRewritePattern<AnyAttrOfOp> { … }; /// This patterns adds an "eligible" attribute to "foo.maybe_eligible_op". struct MakeOpEligible : public RewritePattern { … }; /// This pattern hoists eligible ops out of a "test.one_region_op". struct HoistEligibleOps : public OpRewritePattern<test::OneRegionOp> { … }; /// This pattern moves "test.move_before_parent_op" before the parent op. struct MoveBeforeParentOp : public RewritePattern { … }; /// This pattern inlines blocks that are nested in /// "test.inline_blocks_into_parent" into the parent block. struct InlineBlocksIntoParent : public RewritePattern { … }; /// This pattern splits blocks at "test.split_block_here" and replaces the op /// with a new op (to prevent an infinite loop of block splitting). struct SplitBlockHere : public RewritePattern { … }; /// This pattern clones "test.clone_me" ops. struct CloneOp : public RewritePattern { … }; /// This pattern clones regions of "test.clone_region_before" ops before the /// parent block. struct CloneRegionBeforeOp : public RewritePattern { … }; struct TestPatternDriver : public PassWrapper<TestPatternDriver, OperationPass<>> { … }; struct DumpNotifications : public RewriterBase::Listener { … }; struct TestStrictPatternDriver : public PassWrapper<TestStrictPatternDriver, OperationPass<func::FuncOp>> { … }; } // namespace //===----------------------------------------------------------------------===// // ReturnType Driver. //===----------------------------------------------------------------------===// namespace { // Generate ops for each instance where the type can be successfully inferred. template <typename OpTy> static void invokeCreateWithInferredReturnType(Operation *op) { … } static void reifyReturnShape(Operation *op) { … } struct TestReturnTypeDriver : public PassWrapper<TestReturnTypeDriver, OperationPass<func::FuncOp>> { … }; } // namespace namespace { struct TestDerivedAttributeDriver : public PassWrapper<TestDerivedAttributeDriver, OperationPass<func::FuncOp>> { … }; } // namespace void TestDerivedAttributeDriver::runOnOperation() { … } //===----------------------------------------------------------------------===// // Legalization Driver. //===----------------------------------------------------------------------===// namespace { //===----------------------------------------------------------------------===// // Region-Block Rewrite Testing /// This pattern applies a signature conversion to a block inside a detached /// region. struct TestDetachedSignatureConversion : public ConversionPattern { … }; /// This pattern is a simple pattern that inlines the first region of a given /// operation into the parent region. struct TestRegionRewriteBlockMovement : public ConversionPattern { … }; /// This pattern is a simple pattern that generates a region containing an /// illegal operation. struct TestRegionRewriteUndo : public RewritePattern { … }; /// A simple pattern that creates a block at the end of the parent region of the /// matched operation. struct TestCreateBlock : public RewritePattern { … }; /// A simple pattern that creates a block containing an invalid operation in /// order to trigger the block creation undo mechanism. struct TestCreateIllegalBlock : public RewritePattern { … }; /// A simple pattern that tests the undo mechanism when replacing the uses of a /// block argument. struct TestUndoBlockArgReplace : public ConversionPattern { … }; /// This pattern hoists ops out of a "test.hoist_me" and then fails conversion. /// This is to test the rollback logic. struct TestUndoMoveOpBefore : public ConversionPattern { … }; /// A rewrite pattern that tests the undo mechanism when erasing a block. struct TestUndoBlockErase : public ConversionPattern { … }; /// A pattern that modifies a property in-place, but keeps the op illegal. struct TestUndoPropertiesModification : public ConversionPattern { … }; //===----------------------------------------------------------------------===// // Type-Conversion Rewrite Testing /// This patterns erases a region operation that has had a type conversion. struct TestDropOpSignatureConversion : public ConversionPattern { … }; /// This pattern simply updates the operands of the given operation. struct TestPassthroughInvalidOp : public ConversionPattern { … }; /// This pattern handles the case of a split return value. struct TestSplitReturnType : public ConversionPattern { … }; //===----------------------------------------------------------------------===// // Multi-Level Type-Conversion Rewrite Testing struct TestChangeProducerTypeI32ToF32 : public ConversionPattern { … }; struct TestChangeProducerTypeF32ToF64 : public ConversionPattern { … }; struct TestChangeProducerTypeF32ToInvalid : public ConversionPattern { … }; struct TestUpdateConsumerType : public ConversionPattern { … }; //===----------------------------------------------------------------------===// // 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 { … }; //===----------------------------------------------------------------------===// // Recursive Rewrite Testing /// This pattern is applied to the same operation multiple times, but has a /// bounded recursion. struct TestBoundedRecursiveRewrite : public OpRewritePattern<TestRecursiveRewriteOp> { … }; struct TestNestedOpCreationUndoRewrite : public OpRewritePattern<IllegalOpWithRegionAnchor> { … }; // This pattern matches `test.blackhole` and delete this op and its producer. struct TestReplaceEraseOp : public OpRewritePattern<BlackHoleOp> { … }; // This pattern replaces explicitly illegal op with explicitly legal op, // but in addition creates unregistered operation. struct TestCreateUnregisteredOp : public OpRewritePattern<ILLegalOpG> { … }; } // namespace namespace { struct TestTypeConverter : public TypeConverter { … }; struct TestLegalizePatternDriver : public PassWrapper<TestLegalizePatternDriver, OperationPass<>> { … }; } // namespace 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"), clEnumValN(TestLegalizePatternDriver::ConversionMode::Full, "full", "Perform a full conversion"), clEnumValN(TestLegalizePatternDriver::ConversionMode::Partial, "partial", "Perform a partial conversion"))); //===----------------------------------------------------------------------===// // ConversionPatternRewriter::getRemappedValue testing. This method is used // to get the remapped value of an original value that was replaced using // ConversionPatternRewriter. namespace { struct TestRemapValueTypeConverter : public TypeConverter { … }; /// 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> { … }; /// A rewriter pattern that tests that blocks can be merged. struct TestRemapValueInRegion : public OpConversionPattern<TestRemappedValueRegionOp> { … }; struct TestRemappedValue : public mlir::PassWrapper<TestRemappedValue, OperationPass<>> { … }; } // namespace //===----------------------------------------------------------------------===// // Test patterns without a specific root operation kind //===----------------------------------------------------------------------===// namespace { /// This pattern matches and removes any operation in the test dialect. struct RemoveTestDialectOps : public RewritePattern { … }; struct TestUnknownRootOpDriver : public mlir::PassWrapper<TestUnknownRootOpDriver, OperationPass<>> { … }; } // namespace //===----------------------------------------------------------------------===// // Test patterns that uses operations and types defined at runtime //===----------------------------------------------------------------------===// namespace { /// This pattern matches dynamic operations 'test.one_operand_two_results' and /// replace them with dynamic operations 'test.generic_dynamic_op'. struct RewriteDynamicOp : public RewritePattern { … }; struct TestRewriteDynamicOpDriver : public PassWrapper<TestRewriteDynamicOpDriver, OperationPass<>> { … }; } // end anonymous namespace //===----------------------------------------------------------------------===// // Test type conversions //===----------------------------------------------------------------------===// namespace { struct TestTypeConversionProducer : public OpConversionPattern<TestTypeProducerOp> { … }; /// Call signature conversion and then fail the rewrite to trigger the undo /// mechanism. struct TestSignatureConversionUndo : public OpConversionPattern<TestSignatureConversionUndoOp> { … }; /// Call signature conversion without providing a type converter to handle /// materializations. struct TestTestSignatureConversionNoConverter : public OpConversionPattern<TestSignatureConversionNoConverterOp> { … }; /// 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> { … }; struct TestTypeConversionAnotherProducer : public OpRewritePattern<TestAnotherTypeProducerOp> { … }; struct TestReplaceWithLegalOp : public ConversionPattern { … }; struct TestTypeConversionDriver : public PassWrapper<TestTypeConversionDriver, OperationPass<>> { … }; } // namespace //===----------------------------------------------------------------------===// // Test Target Materialization With No Uses //===----------------------------------------------------------------------===// namespace { struct ForwardOperandPattern : public OpConversionPattern<TestTypeChangerOp> { … }; struct TestTargetMaterializationWithNoUses : public PassWrapper<TestTargetMaterializationWithNoUses, OperationPass<>> { … }; } // namespace //===----------------------------------------------------------------------===// // Test Block Merging //===----------------------------------------------------------------------===// namespace { /// A rewriter pattern that tests that blocks can be merged. struct TestMergeBlock : public OpConversionPattern<TestMergeBlocksOp> { … }; /// A rewrite pattern to tests the undo mechanism of blocks being merged. struct TestUndoBlocksMerge : public ConversionPattern { … }; /// 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> { … }; struct TestMergeBlocksPatternDriver : public PassWrapper<TestMergeBlocksPatternDriver, OperationPass<>> { … }; } // namespace //===----------------------------------------------------------------------===// // 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> { … }; struct TestSelectiveReplacementPatternDriver : public PassWrapper<TestSelectiveReplacementPatternDriver, OperationPass<>> { … }; } // namespace //===----------------------------------------------------------------------===// // PassRegistration //===----------------------------------------------------------------------===// namespace mlir { namespace test { void registerPatternsTestPass() { … } } // namespace test } // namespace mlir