//===-- TestOps.td - Test dialect operation definitions ----*- tablegen -*-===// // // Part of the MLIR 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 // //===----------------------------------------------------------------------===// #ifndef TEST_OPS #define TEST_OPS include "mlir/IR/OpBase.td" include "mlir/IR/OpAsmInterface.td" include "mlir/Analysis/CallInterfaces.td" include "mlir/Analysis/InferTypeOpInterface.td" def TEST_Dialect : Dialect { let name = "test"; let cppNamespace = ""; } class TEST_Op traits = []> : Op; //===----------------------------------------------------------------------===// // Test Types //===----------------------------------------------------------------------===// def ComplexF64 : Complex; def ComplexOp : TEST_Op<"complex_f64"> { let results = (outs ComplexF64); } def ComplexTensorOp : TEST_Op<"complex_f64_tensor"> { let results = (outs TensorOf<[ComplexF64]>); } def AnyShaped: ShapedContainerType<[AnyType], IsShapedTypePred, "shaped">; def TupleOp : TEST_Op<"tuple_32_bit"> { let results = (outs TupleOf<[I32, F32]>); } def NestedTupleOp : TEST_Op<"nested_tuple_32_bit"> { let results = (outs NestedTupleOf<[I32, F32]>); } def TakesStaticMemRefOp : TEST_Op<"takes_static_memref"> { let arguments = (ins AnyStaticShapeMemRef:$x); } def RankLessThan2I8F32MemRefOp : TEST_Op<"rank_less_than_2_I8_F32_memref"> { let results = (outs MemRefRankOf<[I8, F32], [0, 1]>); } def NDTensorOfOp : TEST_Op<"nd_tensor_of"> { let arguments = (ins 0DTensorOf<[F32]>:$arg0, 1DTensorOf<[F32]>:$arg1, 2DTensorOf<[I16]>:$arg2, 3DTensorOf<[I16]>:$arg3, 4DTensorOf<[I16]>:$arg4 ); } def RankedTensorOp : TEST_Op<"ranked_tensor_op"> { let arguments = (ins AnyRankedTensor:$input); } def MultiTensorRankOf : TEST_Op<"multi_tensor_rank_of"> { let arguments = (ins TensorRankOf<[I8, I32, F32], [0, 1]>:$arg0 ); } //===----------------------------------------------------------------------===// // Test Symbols //===----------------------------------------------------------------------===// def SymbolOp : TEST_Op<"symbol", [Symbol]> { let summary = "operation which defines a new symbol"; let arguments = (ins StrAttr:$sym_name, OptionalAttr:$sym_visibility); } def SymbolScopeOp : TEST_Op<"symbol_scope", [SymbolTable, SingleBlockImplicitTerminator<"TerminatorOp">]> { let summary = "operation which defines a new symbol table"; let regions = (region SizedRegion<1>:$region); } def SymbolTableRegionOp : TEST_Op<"symbol_table_region", [SymbolTable]> { let summary = "operation which defines a new symbol table without a " "restriction on a terminator"; let regions = (region SizedRegion<1>:$region); } //===----------------------------------------------------------------------===// // Test Operands //===----------------------------------------------------------------------===// def MixedNormalVariadicOperandOp : TEST_Op< "mixed_normal_variadic_operand", [SameVariadicOperandSize]> { let arguments = (ins Variadic:$input1, AnyTensor:$input2, Variadic:$input3 ); } //===----------------------------------------------------------------------===// // Test Results //===----------------------------------------------------------------------===// def MixedNormalVariadicResults : TEST_Op< "mixed_normal_variadic_result", [SameVariadicResultSize]> { let results = (outs Variadic:$output1, AnyTensor:$output2, Variadic:$output3 ); } //===----------------------------------------------------------------------===// // Test Attributes //===----------------------------------------------------------------------===// def NonNegIntAttrOp : TEST_Op<"non_negative_int_attr"> { let arguments = (ins NonNegativeI32Attr:$i32attr, NonNegativeI64Attr:$i64attr ); } def PositiveIntAttrOp : TEST_Op<"positive_int_attr"> { let arguments = (ins PositiveI32Attr:$i32attr, PositiveI64Attr:$i64attr ); } def TypeArrayAttrOp : TEST_Op<"type_array_attr"> { let arguments = (ins TypeArrayAttr:$attr); } def TypeStringAttrWithTypeOp : TEST_Op<"string_attr_with_type"> { let arguments = (ins StrAttr:$attr); let printer = [{ p << getAttr("attr"); }]; let parser = [{ Attribute attr; Type stringType = OpaqueType::get(Identifier::get("foo", result.getContext()), "string", result.getContext()); return parser.parseAttribute(attr, stringType, "attr", result.attributes); }]; } def StrCaseA: StrEnumAttrCase<"A">; def StrCaseB: StrEnumAttrCase<"B">; def SomeStrEnum: StrEnumAttr< "SomeStrEnum", "", [StrCaseA, StrCaseB]>; def StrEnumAttrOp : TEST_Op<"str_enum_attr"> { let arguments = (ins SomeStrEnum:$attr); let results = (outs I32:$val); } def I32Case5: I32EnumAttrCase<"case5", 5>; def I32Case10: I32EnumAttrCase<"case10", 10>; def SomeI32Enum: I32EnumAttr< "SomeI32Enum", "", [I32Case5, I32Case10]>; def I32EnumAttrOp : TEST_Op<"i32_enum_attr"> { let arguments = (ins SomeI32Enum:$attr); let results = (outs I32:$val); } def I64Case5: I64EnumAttrCase<"case5", 5>; def I64Case10: I64EnumAttrCase<"case10", 10>; def SomeI64Enum: I64EnumAttr< "SomeI64Enum", "", [I64Case5, I64Case10]>; def I64EnumAttrOp : TEST_Op<"i64_enum_attr"> { let arguments = (ins SomeI64Enum:$attr); let results = (outs I32:$val); } def FloatElementsAttrOp : TEST_Op<"float_elements_attr"> { let arguments = (ins RankedF32ElementsAttr<[2]>:$scalar_f32_attr, RankedF64ElementsAttr<[4, 8]>:$tensor_f64_attr ); } // A pattern that updates dense<[3.0, 4.0]> to dense<[5.0, 6.0]>. // This tests both matching and generating float elements attributes. def UpdateFloatElementsAttr : Pat< (FloatElementsAttrOp ConstantAttr, "{3.0f, 4.0f}">:$f32attr, $f64attr), (FloatElementsAttrOp ConstantAttr, "{5.0f, 6.0f}">:$f32attr, $f64attr)>; //===----------------------------------------------------------------------===// // Test Attribute Constraints //===----------------------------------------------------------------------===// def SymbolRefOp : TEST_Op<"symbol_ref_attr"> { let arguments = (ins Confined]>:$symbol ); } //===----------------------------------------------------------------------===// // Test Regions //===----------------------------------------------------------------------===// def OneRegionOp : TEST_Op<"one_region_op", []> { let regions = (region AnyRegion); } def TwoRegionOp : TEST_Op<"two_region_op", []> { let regions = (region AnyRegion, AnyRegion); } def SizedRegionOp : TEST_Op<"sized_region_op", []> { let regions = (region SizedRegion<2>:$my_region, SizedRegion<1>); } //===----------------------------------------------------------------------===// // Test Call Interfaces //===----------------------------------------------------------------------===// def ConversionCallOp : TEST_Op<"conversion_call_op", [CallOpInterface]> { let arguments = (ins Variadic:$inputs, SymbolRefAttr:$callee); let results = (outs Variadic); let extraClassDeclaration = [{ /// Get the argument operands to the called function. operand_range getArgOperands() { return inputs(); } /// Return the callee of this operation. CallInterfaceCallable getCallableForCallee() { return getAttrOfType("callee"); } }]; } def FunctionalRegionOp : TEST_Op<"functional_region_op", [CallableOpInterface]> { let regions = (region AnyRegion:$body); let results = (outs FunctionType); let extraClassDeclaration = [{ Region *getCallableRegion() { return &body(); } ArrayRef getCallableResults() { return getType().cast().getResults(); } }]; } //===----------------------------------------------------------------------===// // Test Traits //===----------------------------------------------------------------------===// def SameOperandElementTypeOp : TEST_Op<"same_operand_element_type", [SameOperandsElementType]> { let arguments = (ins AnyType, AnyType); let results = (outs AnyType); } def SameOperandAndResultElementTypeOp : TEST_Op<"same_operand_and_result_element_type", [SameOperandsAndResultElementType]> { let arguments = (ins Variadic); let results = (outs Variadic); } def SameOperandShapeOp : TEST_Op<"same_operand_shape", [SameOperandsShape]> { let arguments = (ins Variadic); } def SameOperandAndResultShapeOp : TEST_Op<"same_operand_and_result_shape", [SameOperandsAndResultShape]> { let arguments = (ins Variadic); let results = (outs Variadic); } def SameOperandAndResultTypeOp : TEST_Op<"same_operand_and_result_type", [SameOperandsAndResultType]> { let arguments = (ins Variadic); let results = (outs Variadic); } def ArgAndResHaveFixedElementTypesOp : TEST_Op<"arg_and_res_have_fixed_element_types", [PredOpTrait<"fixed type combination", And<[ElementTypeIsPred<"x", I32>, ElementTypeIsPred<"y", F32>]>>, ElementTypeIs<"res", I16>]> { let arguments = (ins AnyShaped:$x, AnyShaped:$y); let results = (outs AnyShaped:$res); } def OperandsHaveSameElementType : TEST_Op<"operands_have_same_element_type", [ AllElementTypesMatch<["x", "y"]>]> { let arguments = (ins AnyType:$x, AnyType:$y); } def OperandZeroAndResultHaveSameElementType : TEST_Op< "operand0_and_result_have_same_element_type", [AllElementTypesMatch<["x", "res"]>]> { let arguments = (ins AnyType:$x, AnyType:$y); let results = (outs AnyType:$res); } def OperandsHaveSameType : TEST_Op<"operands_have_same_type", [AllTypesMatch<["x", "y"]>]> { let arguments = (ins AnyType:$x, AnyType:$y); } def OperandZeroAndResultHaveSameType : TEST_Op<"operand0_and_result_have_same_type", [AllTypesMatch<["x", "res"]>]> { let arguments = (ins AnyType:$x, AnyType:$y); let results = (outs AnyType:$res); } def OperandsHaveSameRank : TEST_Op<"operands_have_same_rank", [AllRanksMatch<["x", "y"]>]> { let arguments = (ins AnyShaped:$x, AnyShaped:$y); } def OperandZeroAndResultHaveSameRank : TEST_Op<"operand0_and_result_have_same_rank", [AllRanksMatch<["x", "res"]>]> { let arguments = (ins AnyShaped:$x, AnyShaped:$y); let results = (outs AnyShaped:$res); } def OperandZeroAndResultHaveSameShape : TEST_Op<"operand0_and_result_have_same_shape", [AllShapesMatch<["x", "res"]>]> { let arguments = (ins AnyShaped:$x, AnyShaped:$y); let results = (outs AnyShaped:$res); } def OperandZeroAndResultHaveSameElementCount : TEST_Op<"operand0_and_result_have_same_element_count", [AllElementCountsMatch<["x", "res"]>]> { let arguments = (ins AnyShaped:$x, AnyShaped:$y); let results = (outs AnyShaped:$res); } def FourEqualsFive : TEST_Op<"four_equals_five", [AllMatch<["5", "4"], "4 equals 5">]>; def OperandRankEqualsResultSize : TEST_Op<"operand_rank_equals_result_size", [AllMatch<[Rank<"operand">.result, ElementCount<"result">.result], "operand rank equals result size">]> { let arguments = (ins AnyShaped:$operand); let results = (outs AnyShaped:$result); } def IfFirstOperandIsNoneThenSoIsSecond : TEST_Op<"if_first_operand_is_none_then_so_is_second", [PredOpTrait< "has either both none type operands or first is not none", Or<[ And<[TypeIsPred<"x", NoneType>, TypeIsPred<"y", NoneType>]>, Neg>]>>]> { let arguments = (ins AnyType:$x, AnyType:$y); } def BroadcastableOp : TEST_Op<"broadcastable", [Broadcastable]> { let arguments = (ins AnyTensor, AnyTensor); let results = (outs AnyTensor); } // There the "HasParent" trait. def ParentOp : TEST_Op<"parent">; def ChildOp : TEST_Op<"child", [HasParent<"ParentOp">]>; def TerminatorOp : TEST_Op<"finish", [Terminator]>; def SingleBlockImplicitTerminatorOp : TEST_Op<"SingleBlockImplicitTerminator", [SingleBlockImplicitTerminator<"TerminatorOp">]> { let regions = (region SizedRegion<1>:$region); } def I32ElementsAttrOp : TEST_Op<"i32ElementsAttr"> { let arguments = (ins I32ElementsAttr:$attr); } def OpWithInferTypeInterfaceOp : TEST_Op<"op_with_infer_type_if", [ DeclareOpInterfaceMethods]> { let arguments = (ins AnyTensor, AnyTensor); let results = (outs AnyTensor); } def IsNotScalar : Constraint>; def UpdateAttr : Pat<(I32ElementsAttrOp $attr), (I32ElementsAttrOp ConstantAttr), [(IsNotScalar $attr)]>; def TestBranchOp : TEST_Op<"br", [Terminator]> { let arguments = (ins Variadic:$operands); } def AttrSizedOperandOp : TEST_Op<"attr_sized_operands", [AttrSizedOperandSegments]> { let arguments = (ins Variadic:$a, Variadic:$b, I32:$c, Variadic:$d, I32ElementsAttr:$operand_segment_sizes ); } def AttrSizedResultOp : TEST_Op<"attr_sized_results", [AttrSizedResultSegments]> { let arguments = (ins I32ElementsAttr:$result_segment_sizes ); let results = (outs Variadic:$a, Variadic:$b, I32:$c, Variadic:$d ); } //===----------------------------------------------------------------------===// // Test Patterns //===----------------------------------------------------------------------===// def OpA : TEST_Op<"op_a"> { let arguments = (ins I32, I32Attr:$attr); let results = (outs I32); } def OpB : TEST_Op<"op_b"> { let arguments = (ins I32, I32Attr:$attr); let results = (outs I32); } // Test named pattern. def TestNamedPatternRule : Pat<(OpA $input, $attr), (OpB $input, $attr)>; // Test with fused location. def : Pat<(OpA (OpA $input, $attr), $bttr), (OpB $input, $bttr)>; // Test added benefit. def OpD : TEST_Op<"op_d">, Arguments<(ins I32)>, Results<(outs I32)>; def OpE : TEST_Op<"op_e">, Arguments<(ins I32)>, Results<(outs I32)>; def OpF : TEST_Op<"op_f">, Arguments<(ins I32)>, Results<(outs I32)>; def OpG : TEST_Op<"op_g">, Arguments<(ins I32)>, Results<(outs I32)>; // Verify that bumping benefit results in selecting different op. def : Pat<(OpD $input), (OpE $input)>; def : Pat<(OpD $input), (OpF $input), [], (addBenefit 10)>; // Verify that patterns with more source nodes are selected before those with fewer. def : Pat<(OpG $input), (OpB $input, ConstantAttr:$attr)>; def : Pat<(OpG (OpG $input)), (OpB $input, ConstantAttr:$attr)>; // Test patterns for zero-result op. def OpH : TEST_Op<"op_h">, Arguments<(ins I32)>, Results<(outs)>; def OpI : TEST_Op<"op_i">, Arguments<(ins I32)>, Results<(outs)>; def : Pat<(OpH $input), (OpI $input)>; // Test patterns for zero-input op. def OpJ : TEST_Op<"op_j">, Arguments<(ins)>, Results<(outs I32)>; def OpK : TEST_Op<"op_k">, Arguments<(ins)>, Results<(outs I32)>; def : Pat<(OpJ), (OpK)>; // Test `$_` for ignoring op argument match. def TestIgnoreArgMatchSrcOp : TEST_Op<"ignore_arg_match_src"> { let arguments = (ins AnyType:$a, AnyType:$b, AnyType:$c, AnyAttr:$d, AnyAttr:$e, AnyAttr:$f); } def TestIgnoreArgMatchDstOp : TEST_Op<"ignore_arg_match_dst"> { let arguments = (ins AnyType:$b, AnyAttr:$f); } def : Pat<(TestIgnoreArgMatchSrcOp $_, $b, I32, I64Attr:$_, $_, $f), (TestIgnoreArgMatchDstOp $b, $f)>; def OpInterleavedOperandAttribute1 : TEST_Op<"interleaved_operand_attr1"> { let arguments = (ins I32:$input1, I64Attr:$attr1, I32:$input2, I64Attr:$attr2 ); } def OpInterleavedOperandAttribute2 : TEST_Op<"interleaved_operand_attr2"> { let arguments = (ins I32:$input1, I64Attr:$attr1, I32:$input2, I64Attr:$attr2 ); } def ManyArgsOp : TEST_Op<"many_arguments"> { let arguments = (ins I32:$input1, I32:$input2, I32:$input3, I32:$input4, I32:$input5, I32:$input6, I32:$input7, I32:$input8, I32:$input9, I64Attr:$attr1, I64Attr:$attr2, I64Attr:$attr3, I64Attr:$attr4, I64Attr:$attr5, I64Attr:$attr6, I64Attr:$attr7, I64Attr:$attr8, I64Attr:$attr9 ); } // Test that DRR does not blow up when seeing lots of arguments. def : Pat<(ManyArgsOp $input1, $input2, $input3, $input4, $input5, $input6, $input7, $input8, $input9, ConstantAttr, $attr2, $attr3, $attr4, $attr5, $attr6, $attr7, $attr8, $attr9), (ManyArgsOp $input1, $input2, $input3, $input4, $input5, $input6, $input7, $input8, $input9, ConstantAttr, $attr2, $attr3, $attr4, $attr5, $attr6, $attr7, $attr8, $attr9)>; // Test that we can capture and reference interleaved operands and attributes. def : Pat<(OpInterleavedOperandAttribute1 $input1, $attr1, $input2, $attr2), (OpInterleavedOperandAttribute2 $input1, $attr1, $input2, $attr2)>; // Test NativeCodeCall. def OpNativeCodeCall1 : TEST_Op<"native_code_call1"> { let arguments = (ins I32:$input1, I32:$input2, BoolAttr:$choice, I64Attr:$attr1, I64Attr:$attr2 ); let results = (outs I32); } def OpNativeCodeCall2 : TEST_Op<"native_code_call2"> { let arguments = (ins I32:$input, I64ArrayAttr:$attr); let results = (outs I32); } // Native code call to invoke a C++ function def CreateOperand: NativeCodeCall<"chooseOperand($0, $1, $2)">; // Native code call to invoke a C++ expression def CreateArrayAttr: NativeCodeCall<"$_builder.getArrayAttr({$0, $1})">; // Test that we can use NativeCodeCall to create operand and attribute. // This pattern chooses between $input1 and $input2 according to $choice and // it combines $attr1 and $attr2 into an array attribute. def : Pat<(OpNativeCodeCall1 $input1, $input2, ConstBoolAttrTrue:$choice, $attr1, $attr2), (OpNativeCodeCall2 (CreateOperand $input1, $input2, $choice), (CreateArrayAttr $attr1, $attr2))>; // Note: the following is just for testing purpose. // Should use the replaceWithValue directive instead. def UseOpResult: NativeCodeCall<"$0">; // Test that we can use NativeCodeCall to create result. def : Pat<(OpNativeCodeCall1 $input1, $input2, ConstBoolAttrFalse, $attr1, $attr2), (UseOpResult $input2)>; def OpNativeCodeCall3 : TEST_Op<"native_code_call3"> { let arguments = (ins I32:$input); let results = (outs I32); } // Test that NativeCodeCall is not ignored if it is not used to directly // replace the matched root op. def : Pattern<(OpNativeCodeCall3 $input), [(NativeCodeCall<"createOpI($_builder, $0)"> $input), (OpK)]>; // Test AllAttrConstraintsOf. def OpAllAttrConstraint1 : TEST_Op<"all_attr_constraint_of1"> { let arguments = (ins I64ArrayAttr:$attr); let results = (outs I32); } def OpAllAttrConstraint2 : TEST_Op<"all_attr_constraint_of2"> { let arguments = (ins I64ArrayAttr:$attr); let results = (outs I32); } def Constraint0 : AttrConstraint< CPred<"$_self.cast().getValue()[0]." "cast().getInt() == 0">, "[0] == 0">; def Constraint1 : AttrConstraint< CPred<"$_self.cast().getValue()[1]." "cast().getInt() == 1">, "[1] == 1">; def : Pat<(OpAllAttrConstraint1 AllAttrConstraintsOf<[Constraint0, Constraint1]>:$attr), (OpAllAttrConstraint2 $attr)>; // Op for testing RewritePattern removing op with inner ops. def TestOpWithRegionPattern : TEST_Op<"op_with_region_pattern"> { let regions = (region SizedRegion<1>:$region); let hasCanonicalizer = 1; } // Op for testing trivial removal via folding of op with inner ops and no uses. def TestOpWithRegionFoldNoSideEffect : TEST_Op< "op_with_region_fold_no_side_effect", [NoSideEffect]> { let regions = (region SizedRegion<1>:$region); } // Op for testing folding of outer op with inner ops. def TestOpWithRegionFold : TEST_Op<"op_with_region_fold"> { let arguments = (ins I32:$operand); let results = (outs I32); let regions = (region SizedRegion<1>:$region); let hasFolder = 1; } def TestOpWithVariadicResultsAndFolder: TEST_Op<"op_with_variadic_results_and_folder"> { let arguments = (ins Variadic:$operands); let results = (outs Variadic); let hasFolder = 1; } //===----------------------------------------------------------------------===// // Test Patterns (Symbol Binding) // Test symbol binding. def OpSymbolBindingA : TEST_Op<"symbol_binding_a", []> { let arguments = (ins I32:$operand, I64Attr:$attr); let results = (outs I32); } def OpSymbolBindingB : TEST_Op<"symbol_binding_b", []> { let arguments = (ins I32:$operand); let results = (outs I32); let builders = [ OpBuilder< "Builder *builder, OperationState &state, Value operand", [{ state.types.assign({builder->getIntegerType(32)}); state.addOperands({operand}); }]> ]; } def OpSymbolBindingC : TEST_Op<"symbol_binding_c", []> { let arguments = (ins I32:$operand); let results = (outs I32); let builders = OpSymbolBindingB.builders; } def OpSymbolBindingD : TEST_Op<"symbol_binding_d", []> { let arguments = (ins I32:$input1, I32:$input2, I64Attr:$attr); let results = (outs I32); } def HasOneUse: Constraint, "has one use">; def : Pattern< // Bind to source pattern op operand/attribute/result (OpSymbolBindingA:$res_a $operand, $attr), [ // Bind to auxiliary op result (OpSymbolBindingC:$res_c (OpSymbolBindingB:$res_b $operand)), // Use bound symbols in resultant ops (OpSymbolBindingD $res_b, $res_c, $attr)], // Use bound symbols in additional constraints [(HasOneUse $res_a)]>; def OpSymbolBindingNoResult : TEST_Op<"symbol_binding_no_result", []> { let arguments = (ins I32:$operand); } // Test that we can bind to an op without results and reference it later. def : Pat<(OpSymbolBindingNoResult:$op $operand), (NativeCodeCall<"handleNoResultOp($_builder, $0)"> $op)>; //===----------------------------------------------------------------------===// // Test Patterns (Attributes) // Test matching against op attributes. def OpAttrMatch1 : TEST_Op<"match_op_attribute1"> { let arguments = (ins I32Attr:$required_attr, OptionalAttr:$optional_attr, DefaultValuedAttr:$default_valued_attr, I32Attr:$more_attr ); let results = (outs I32); } def OpAttrMatch2 : TEST_Op<"match_op_attribute2"> { let arguments = OpAttrMatch1.arguments; let results = (outs I32); } def MoreConstraint : AttrConstraint< CPred<"$_self.cast().getInt() == 4">, "more constraint">; def : Pat<(OpAttrMatch1 $required, $optional, $default_valued, MoreConstraint:$more), (OpAttrMatch2 $required, $optional, $default_valued, $more)>; // Test unit attrs. def OpAttrMatch3 : TEST_Op<"match_op_attribute3"> { let arguments = (ins UnitAttr:$attr); let results = (outs I32); } def OpAttrMatch4 : TEST_Op<"match_op_attribute4"> { let arguments = (ins UnitAttr:$attr1, UnitAttr:$attr2); let results = (outs I32); } def : Pat<(OpAttrMatch3 $attr), (OpAttrMatch4 ConstUnitAttr, $attr)>; // Test with constant attr. def OpC : TEST_Op<"op_c">, Arguments<(ins I32)>, Results<(outs I32)>; def : Pat<(OpC $input), (OpB $input, ConstantAttr:$attr)>; // Test string enum attribute in rewrites. def : Pat<(StrEnumAttrOp StrCaseA), (StrEnumAttrOp StrCaseB)>; // Test integer enum attribute in rewrites. def : Pat<(I32EnumAttrOp I32Case5), (I32EnumAttrOp I32Case10)>; def : Pat<(I64EnumAttrOp I64Case5), (I64EnumAttrOp I64Case10)>; //===----------------------------------------------------------------------===// // Test Patterns (Multi-result Ops) def MultiResultOpKind1: I64EnumAttrCase<"kind1", 1>; def MultiResultOpKind2: I64EnumAttrCase<"kind2", 2>; def MultiResultOpKind3: I64EnumAttrCase<"kind3", 3>; def MultiResultOpKind4: I64EnumAttrCase<"kind4", 4>; def MultiResultOpKind5: I64EnumAttrCase<"kind5", 5>; def MultiResultOpKind6: I64EnumAttrCase<"kind6", 6>; def MultiResultOpEnum: I64EnumAttr< "MultiResultOpEnum", "Multi-result op kinds", [ MultiResultOpKind1, MultiResultOpKind2, MultiResultOpKind3, MultiResultOpKind4, MultiResultOpKind5, MultiResultOpKind6 ]>; def ThreeResultOp : TEST_Op<"three_result"> { let arguments = (ins MultiResultOpEnum:$kind); let results = (outs I32:$result1, F32:$result2, F32:$result3); } def AnotherThreeResultOp : TEST_Op<"another_three_result"> { let arguments = (ins MultiResultOpEnum:$kind); let results = (outs I32:$result1, F32:$result2, F32:$result3); } def TwoResultOp : TEST_Op<"two_result"> { let arguments = (ins MultiResultOpEnum:$kind); let results = (outs I32:$result1, F32:$result2); let builders = [ OpBuilder< "Builder *builder, OperationState &state, IntegerAttr kind", [{ auto i32 = builder->getIntegerType(32); auto f32 = builder->getF32Type(); state.types.assign({i32, f32}); state.addAttribute("kind", kind); }]> ]; } def AnotherTwoResultOp : TEST_Op<"another_two_result"> { let arguments = (ins MultiResultOpEnum:$kind); let results = (outs F32:$result1, F32:$result2); } def OneResultOp1 : TEST_Op<"one_result1"> { let arguments = (ins MultiResultOpEnum:$kind); let results = (outs F32:$result1); } def OneResultOp2 : TEST_Op<"one_result2"> { let arguments = (ins MultiResultOpEnum:$kind); let results = (outs I32:$result1); } def OneResultOp3 : TEST_Op<"one_result3"> { let arguments = (ins F32); let results = (outs I32:$result1); } // Test using multi-result op as a whole def : Pat<(ThreeResultOp MultiResultOpKind1), (AnotherThreeResultOp MultiResultOpKind1)>; // Test using multi-result op as a whole for partial replacement def : Pattern<(ThreeResultOp MultiResultOpKind2), [(TwoResultOp MultiResultOpKind2), (OneResultOp1 MultiResultOpKind2)]>; def : Pattern<(ThreeResultOp MultiResultOpKind3), [(OneResultOp2 MultiResultOpKind3), (AnotherTwoResultOp MultiResultOpKind3)]>; // Test using results separately in a multi-result op def : Pattern<(ThreeResultOp MultiResultOpKind4), [(TwoResultOp:$res1__0 MultiResultOpKind4), (OneResultOp1 MultiResultOpKind4), (TwoResultOp:$res2__1 MultiResultOpKind4)]>; // Test referencing a single value in the value pack // This rule only matches TwoResultOp if its second result has no use. def : Pattern<(TwoResultOp:$res MultiResultOpKind5), [(OneResultOp2 MultiResultOpKind5), (OneResultOp1 MultiResultOpKind5)], [(HasNoUseOf:$res__1)]>; // Test using auxiliary ops for replacing multi-result op def : Pattern< (ThreeResultOp MultiResultOpKind6), [ // Auxiliary op generated to help building the final result but not // directly used to replace the source op's results. (TwoResultOp:$interm MultiResultOpKind6), (OneResultOp3 $interm__1), (AnotherTwoResultOp MultiResultOpKind6) ]>; //===----------------------------------------------------------------------===// // Test Patterns (Variadic Ops) def OneVResOneVOperandOp1 : TEST_Op<"one_variadic_out_one_variadic_in1"> { let arguments = (ins Variadic); let results = (outs Variadic); } def OneVResOneVOperandOp2 : TEST_Op<"one_variadic_out_one_variadic_in2"> { let arguments = (ins Variadic); let results = (outs Variadic); } // Rewrite an op with one variadic operand and one variadic result to // another similar op. def : Pat<(OneVResOneVOperandOp1 $inputs), (OneVResOneVOperandOp2 $inputs)>; def MixedVOperandOp1 : TEST_Op<"mixed_variadic_in1", [SameVariadicOperandSize]> { let arguments = (ins Variadic:$input1, F32:$input2, Variadic:$input3 ); } def MixedVOperandOp2 : TEST_Op<"mixed_variadic_in2", [SameVariadicOperandSize]> { let arguments = (ins Variadic:$input1, F32:$input2, Variadic:$input3 ); } // Rewrite an op with both variadic operands and normal operands. def : Pat<(MixedVOperandOp1 $input1, $input2, $input3), (MixedVOperandOp2 $input1, $input2, $input3)>; def MixedVResultOp1 : TEST_Op<"mixed_variadic_out1", [SameVariadicResultSize]> { let results = (outs Variadic:$output1, F32:$output2, Variadic:$output3 ); } def MixedVResultOp2 : TEST_Op<"mixed_variadic_out2", [SameVariadicResultSize]> { let results = (outs Variadic:$output1, F32:$output2, Variadic:$output3 ); } // Rewrite an op with both variadic results and normal results. // Note that because we are generating the op with a top-level result pattern, // we are able to deduce the correct result types for the generated op using // the information from the matched root op. def : Pat<(MixedVResultOp1), (MixedVResultOp2)>; def OneI32ResultOp : TEST_Op<"one_i32_out"> { let results = (outs I32); } def MixedVOperandOp3 : TEST_Op<"mixed_variadic_in3", [SameVariadicOperandSize]> { let arguments = (ins I32:$input1, Variadic:$input2, Variadic:$input3, I32Attr:$count ); let results = (outs I32); } def MixedVResultOp3 : TEST_Op<"mixed_variadic_out3", [SameVariadicResultSize]> { let arguments = (ins I32Attr:$count); let results = (outs I32:$output1, Variadic:$output2, Variadic:$output3 ); // We will use this op in a nested result pattern, where we cannot deduce the // result type. So need to provide a builder not requiring result types. let builders = [ OpBuilder< "Builder *builder, OperationState &state, IntegerAttr count", [{ auto i32Type = builder->getIntegerType(32); state.addTypes(i32Type); // $output1 SmallVector types(count.getInt(), i32Type); state.addTypes(types); // $output2 state.addTypes(types); // $output3 state.addAttribute("count", count); }]> ]; } // Generates an op with variadic results using nested pattern. def : Pat<(OneI32ResultOp), (MixedVOperandOp3 (MixedVResultOp3:$results__0 ConstantAttr), (replaceWithValue $results__1), (replaceWithValue $results__2), ConstantAttr)>; //===----------------------------------------------------------------------===// // Test Legalization //===----------------------------------------------------------------------===// def Test_LegalizerEnum_Success : StrEnumAttrCase<"Success">; def Test_LegalizerEnum_Failure : StrEnumAttrCase<"Failure">; def Test_LegalizerEnum : StrEnumAttr<"Success", "Failure", [Test_LegalizerEnum_Success, Test_LegalizerEnum_Failure]>; def ILLegalOpA : TEST_Op<"illegal_op_a">, Results<(outs I32)>; def ILLegalOpB : TEST_Op<"illegal_op_b">, Results<(outs I32)>; def ILLegalOpC : TEST_Op<"illegal_op_c">, Results<(outs I32)>; def ILLegalOpD : TEST_Op<"illegal_op_d">, Results<(outs I32)>; def ILLegalOpE : TEST_Op<"illegal_op_e">, Results<(outs I32)>; def ILLegalOpF : TEST_Op<"illegal_op_f">, Results<(outs I32)>; def LegalOpA : TEST_Op<"legal_op_a">, Arguments<(ins Test_LegalizerEnum:$status)>, Results<(outs I32)>; def LegalOpB : TEST_Op<"legal_op_b">, Results<(outs I32)>; // Check that smaller pattern depths are chosen, i.e. prioritize more direct // mappings. def : Pat<(ILLegalOpA), (LegalOpA Test_LegalizerEnum_Success)>; def : Pat<(ILLegalOpA), (ILLegalOpB)>; def : Pat<(ILLegalOpB), (LegalOpA Test_LegalizerEnum_Failure)>; // Check that the higher benefit pattern is taken for multiple legalizations // with the same depth. def : Pat<(ILLegalOpC), (ILLegalOpD)>; def : Pat<(ILLegalOpD), (LegalOpA Test_LegalizerEnum_Failure)>; def : Pat<(ILLegalOpC), (ILLegalOpE), [], (addBenefit 10)>; def : Pat<(ILLegalOpE), (LegalOpA Test_LegalizerEnum_Success)>; // Check that patterns use the most up-to-date value when being replaced. def TestRewriteOp : TEST_Op<"rewrite">, Arguments<(ins AnyType)>, Results<(outs AnyType)>; def : Pat<(TestRewriteOp $input), (replaceWithValue $input)>; //===----------------------------------------------------------------------===// // Test Type Legalization //===----------------------------------------------------------------------===// def TestRegionBuilderOp : TEST_Op<"region_builder">; def TestReturnOp : TEST_Op<"return", [Terminator]>, Arguments<(ins Variadic)>; def TestCastOp : TEST_Op<"cast">, Arguments<(ins Variadic)>, Results<(outs AnyType)>; def TestInvalidOp : TEST_Op<"invalid", [Terminator]>, Arguments<(ins Variadic)>; def TestTypeProducerOp : TEST_Op<"type_producer">, Results<(outs AnyType)>; def TestTypeConsumerOp : TEST_Op<"type_consumer">, Arguments<(ins AnyType)>; def TestValidOp : TEST_Op<"valid", [Terminator]>, Arguments<(ins Variadic)>; //===----------------------------------------------------------------------===// // Test parser. //===----------------------------------------------------------------------===// def WrappedKeywordOp : TEST_Op<"wrapped_keyword"> { let arguments = (ins StrAttr:$keyword); let parser = [{ return ::parse$cppClass(parser, result); }]; let printer = [{ return ::print(p, *this); }]; } //===----------------------------------------------------------------------===// // Test region argument list parsing. def IsolatedRegionOp : TEST_Op<"isolated_region", [IsolatedFromAbove]> { let summary = "isolated region operation"; let description = [{ Test op with an isolated region, to test passthrough region arguments. Each argument is of index type. }]; let arguments = (ins Index); let regions = (region SizedRegion<1>:$region); let parser = [{ return ::parse$cppClass(parser, result); }]; let printer = [{ return ::print(p, *this); }]; } def WrappingRegionOp : TEST_Op<"wrapping_region", [SingleBlockImplicitTerminator<"TestReturnOp">]> { let summary = "wrapping region operation"; let description = [{ Test op wrapping another op in a region, to test calling parseGenericOperation from the custom parser. }]; let results = (outs Variadic); let regions = (region SizedRegion<1>:$region); let parser = [{ return ::parse$cppClass(parser, result); }]; let printer = [{ return ::print(p, *this); }]; } def PolyForOp : TEST_Op<"polyfor"> { let summary = "polyfor operation"; let description = [{ Test op with multiple region arguments, each argument of index type. }]; let regions = (region SizedRegion<1>:$region); let parser = [{ return ::parse$cppClass(parser, result); }]; } //===----------------------------------------------------------------------===// // Test OpAsmInterface. def AsmInterfaceOp : TEST_Op<"asm_interface_op"> { let results = (outs AnyType:$first, Variadic:$middle_results, AnyType); } def AsmDialectInterfaceOp : TEST_Op<"asm_dialect_interface_op"> { let results = (outs AnyType); } #endif // TEST_OPS