| //===- PDLInterpOps.td - Pattern Interpreter Dialect -------*- tablegen -*-===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file declares the PDL interpreter dialect ops. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef MLIR_DIALECT_PDLINTERP_IR_PDLINTERPOPS |
| #define MLIR_DIALECT_PDLINTERP_IR_PDLINTERPOPS |
| |
| include "mlir/Dialect/PDL/IR/PDLTypes.td" |
| include "mlir/Interfaces/SideEffectInterfaces.td" |
| |
| //===----------------------------------------------------------------------===// |
| // PDLInterp Dialect |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_Dialect : Dialect { |
| let summary = "Interpreted pattern execution dialect"; |
| let description = [{ |
| The PDL Interpreter dialect provides a lower level abstraction compared to |
| the PDL dialect, and is targeted towards low level optimization and |
| interpreter code generation. The dialect operations encapsulates |
| low-level pattern match and rewrite "primitives", such as navigating the |
| IR (Operation::getOperand), creating new operations (OpBuilder::create), |
| etc. Many of the operations within this dialect also fuse branching control |
| flow with some form of a predicate comparison operation. This type of fusion |
| reduces the amount of work that an interpreter must do when executing. |
| }]; |
| |
| let name = "pdl_interp"; |
| let cppNamespace = "::mlir::pdl_interp"; |
| let dependentDialects = ["pdl::PDLDialect"]; |
| let extraClassDeclaration = [{ |
| /// Returns the name of the function containing the matcher code. This |
| /// function is called by the interpreter when matching an operation. |
| static StringRef getMatcherFunctionName() { return "matcher"; } |
| |
| /// Returns the name of the module containing the rewrite functions. These |
| /// functions are invoked by distinct patterns within the matcher function |
| /// to rewrite the IR after a successful match. |
| static StringRef getRewriterModuleName() { return "rewriters"; } |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // PDLInterp Operations |
| //===----------------------------------------------------------------------===// |
| |
| // Generic interpreter operation. |
| class PDLInterp_Op<string mnemonic, list<OpTrait> traits = []> : |
| Op<PDLInterp_Dialect, mnemonic, traits>; |
| |
| //===----------------------------------------------------------------------===// |
| // PDLInterp_PredicateOp |
| |
| // Check operations evaluate a predicate on a positional value and then |
| // conditionally branch on the result. |
| class PDLInterp_PredicateOp<string mnemonic, list<OpTrait> traits = []> : |
| PDLInterp_Op<mnemonic, !listconcat([Terminator], traits)> { |
| let successors = (successor AnySuccessor:$trueDest, AnySuccessor:$falseDest); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // PDLInterp_SwitchOp |
| |
| // Switch operations evaluate a predicate on a positional value and then |
| // conditionally branch on the result. |
| class PDLInterp_SwitchOp<string mnemonic, list<OpTrait> traits = []> : |
| PDLInterp_Op<mnemonic, !listconcat([Terminator], traits)> { |
| let successors = (successor AnySuccessor:$defaultDest, |
| VariadicSuccessor<AnySuccessor>:$cases); |
| |
| let verifier = [{ |
| // Verify that the number of case destinations matches the number of case |
| // values. |
| size_t numDests = cases().size(); |
| size_t numValues = caseValues().size(); |
| if (numDests != numValues) { |
| return emitOpError("expected number of cases to match the number of case " |
| "values, got ") |
| << numDests << " but expected " << numValues; |
| } |
| return success(); |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::ApplyConstraintOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_ApplyConstraintOp : PDLInterp_PredicateOp<"apply_constraint"> { |
| let summary = "Apply a constraint to a set of positional values"; |
| let description = [{ |
| `pdl_interp.apply_constraint` operations apply a generic constraint, that |
| has been registered with the interpreter, with a given set of positional |
| values. The constraint may have any number of constant parameters. On |
| success, this operation branches to the true destination, otherwise the |
| false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| // Apply `myConstraint` to the entities defined by `input`, `attr`, and |
| // `op`. |
| pdl_interp.apply_constraint "myConstraint"[42, "abc", i32](%input, %attr, %op : !pdl.value, !pdl.attribute, !pdl.operation) -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins StrAttr:$name, |
| Variadic<PDL_AnyType>:$args, |
| OptionalAttr<ArrayAttr>:$constParams); |
| let assemblyFormat = [{ |
| $name ($constParams^)? `(` $args `:` type($args) `)` attr-dict `->` |
| successors |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::ApplyRewriteOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_ApplyRewriteOp : PDLInterp_Op<"apply_rewrite"> { |
| let summary = "Invoke and apply an externally registered rewrite method"; |
| let description = [{ |
| `pdl_interp.apply_rewrite` operations invoke an external rewriter that has |
| been registered with the interpreter to perform the rewrite after a |
| successful match. The rewrite is passed a set of positional arguments, |
| and a set of constant parameters. The rewrite function may return any |
| number of results. |
| |
| Example: |
| |
| ```mlir |
| // Rewriter operating solely on the root operation. |
| pdl_interp.apply_rewrite "rewriter"(%root : !pdl.operation) |
| |
| // Rewriter operating solely on the root operation and return an attribute. |
| %attr = pdl_interp.apply_rewrite "rewriter"(%root : !pdl.operation) : !pdl.attribute |
| |
| // Rewriter operating on the root operation along with additional arguments |
| // from the matcher. |
| pdl_interp.apply_rewrite "rewriter"(%root : !pdl.operation, %value : !pdl.value) |
| |
| // Rewriter operating on the root operation along with additional arguments |
| // and constant parameters. |
| pdl_interp.apply_rewrite "rewriter"[42](%root : !pdl.operation, %value : !pdl.value) |
| ``` |
| }]; |
| let arguments = (ins StrAttr:$name, |
| Variadic<PDL_AnyType>:$args, |
| OptionalAttr<ArrayAttr>:$constParams); |
| let results = (outs Variadic<PDL_AnyType>:$results); |
| let assemblyFormat = [{ |
| $name ($constParams^)? (`(` $args^ `:` type($args) `)`)? |
| (`:` type($results)^)? attr-dict |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::AreEqualOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_AreEqualOp |
| : PDLInterp_PredicateOp<"are_equal", [NoSideEffect, SameTypeOperands]> { |
| let summary = "Check if two positional values or ranges are equivalent"; |
| let description = [{ |
| `pdl_interp.are_equal` operations compare two positional values for |
| equality. On success, this operation branches to the true destination, |
| otherwise the false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.are_equal %result1, %result2 : !pdl.value -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_AnyType:$lhs, PDL_AnyType:$rhs); |
| let assemblyFormat = "operands `:` type($lhs) attr-dict `->` successors"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::BranchOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_BranchOp : PDLInterp_Op<"branch", [NoSideEffect, Terminator]> { |
| let summary = "General branch operation"; |
| let description = [{ |
| `pdl_interp.branch` operations expose general branch functionality to the |
| interpreter, and are generally used to branch from one pattern match |
| sequence to another. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.branch ^dest |
| ``` |
| }]; |
| |
| let successors = (successor AnySuccessor:$dest); |
| let assemblyFormat = "$dest attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CheckAttributeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CheckAttributeOp |
| : PDLInterp_PredicateOp<"check_attribute", [NoSideEffect]> { |
| let summary = "Check the value of an `Attribute`"; |
| let description = [{ |
| `pdl_interp.check_attribute` operations compare the value of a given |
| attribute with a constant value. On success, this operation branches to the |
| true destination, otherwise the false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.check_attribute %attr is 10 -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Attribute:$attribute, AnyAttr:$constantValue); |
| let assemblyFormat = [{ |
| $attribute `is` $constantValue attr-dict `->` successors |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CheckOperandCountOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CheckOperandCountOp |
| : PDLInterp_PredicateOp<"check_operand_count", [NoSideEffect]> { |
| let summary = "Check the number of operands of an `Operation`"; |
| let description = [{ |
| `pdl_interp.check_operand_count` operations compare the number of operands |
| of a given operation value with a constant. The comparison is either exact |
| or at_least, with the latter used to compare against a minimum number of |
| expected operands. On success, this operation branches to the true |
| destination, otherwise the false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| // Check for exact equality. |
| pdl_interp.check_operand_count of %op is 2 -> ^matchDest, ^failureDest |
| |
| // Check for at least N operands. |
| pdl_interp.check_operand_count of %op is at_least 2 -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, |
| Confined<I32Attr, [IntNonNegative]>:$count, |
| UnitAttr:$compareAtLeast); |
| let assemblyFormat = [{ |
| `of` $operation `is` (`at_least` $compareAtLeast^)? $count attr-dict |
| `->` successors |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CheckOperationNameOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CheckOperationNameOp |
| : PDLInterp_PredicateOp<"check_operation_name", [NoSideEffect]> { |
| let summary = "Check the OperationName of an `Operation`"; |
| let description = [{ |
| `pdl_interp.check_operation_name` operations compare the name of a given |
| operation with a known name. On success, this operation branches to the true |
| destination, otherwise the false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.check_operation_name of %op is "foo.op" -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, StrAttr:$name); |
| let assemblyFormat = "`of` $operation `is` $name attr-dict `->` successors"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CheckResultCountOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CheckResultCountOp |
| : PDLInterp_PredicateOp<"check_result_count", [NoSideEffect]> { |
| let summary = "Check the number of results of an `Operation`"; |
| let description = [{ |
| `pdl_interp.check_result_count` operations compare the number of results |
| of a given operation value with a constant. The comparison is either exact |
| or at_least, with the latter used to compare against a minimum number of |
| expected results. On success, this operation branches to the true |
| destination, otherwise the false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| // Check for exact equality. |
| pdl_interp.check_result_count of %op is 2 -> ^matchDest, ^failureDest |
| |
| // Check for at least N results. |
| pdl_interp.check_result_count of %op is at_least 2 -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, |
| Confined<I32Attr, [IntNonNegative]>:$count, |
| UnitAttr:$compareAtLeast); |
| let assemblyFormat = [{ |
| `of` $operation `is` (`at_least` $compareAtLeast^)? $count attr-dict |
| `->` successors |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CheckTypeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CheckTypeOp |
| : PDLInterp_PredicateOp<"check_type", [NoSideEffect]> { |
| let summary = "Compare a type to a known value"; |
| let description = [{ |
| `pdl_interp.check_type` operations compare a type with a statically known |
| type. On success, this operation branches to the true destination, otherwise |
| the false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.check_type %type is i32 -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Type:$value, TypeAttr:$type); |
| let assemblyFormat = "$value `is` $type attr-dict `->` successors"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CheckTypesOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CheckTypesOp |
| : PDLInterp_PredicateOp<"check_types", [NoSideEffect]> { |
| let summary = "Compare a range of types to a range of known values"; |
| let description = [{ |
| `pdl_interp.check_types` operations compare a range of types with a |
| statically known range of types. On success, this operation branches |
| to the true destination, otherwise the false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.check_types %type are [i32, i64] -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_RangeOf<PDL_Type>:$value, |
| TypeArrayAttr:$types); |
| let assemblyFormat = "$value `are` $types attr-dict `->` successors"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::ContinueOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_ContinueOp |
| : PDLInterp_Op<"continue", [NoSideEffect, HasParent<"ForEachOp">, |
| Terminator]> { |
| let summary = "Breaks the current iteration"; |
| let description = [{ |
| `pdl_interp.continue` operation breaks the current iteration within the |
| `pdl_interp.foreach` region and continues with the next iteration from |
| the beginning of the region. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.continue |
| ``` |
| }]; |
| |
| let assemblyFormat = "attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CreateAttributeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CreateAttributeOp |
| : PDLInterp_Op<"create_attribute", [NoSideEffect]> { |
| let summary = "Create an interpreter handle to a constant `Attribute`"; |
| let description = [{ |
| `pdl_interp.create_attribute` operations generate a handle within the |
| interpreter for a specific constant attribute value. |
| |
| Example: |
| |
| ```mlir |
| %attr = pdl_interp.create_attribute 10 : i64 |
| ``` |
| }]; |
| |
| let arguments = (ins AnyAttr:$value); |
| let results = (outs PDL_Attribute:$attribute); |
| let assemblyFormat = "$value attr-dict"; |
| |
| let builders = [ |
| OpBuilder<(ins "Attribute":$value), [{ |
| build($_builder, $_state, $_builder.getType<pdl::AttributeType>(), value); |
| }]>]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CreateOperationOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CreateOperationOp |
| : PDLInterp_Op<"create_operation", [AttrSizedOperandSegments]> { |
| let summary = "Create an instance of a specific `Operation`"; |
| let description = [{ |
| `pdl_interp.create_operation` operations create an `Operation` instance with |
| the specified attributes, operands, and result types. See `pdl.operation` |
| for a more detailed description on the interpretation of the arguments to |
| this operation. |
| |
| Example: |
| |
| ```mlir |
| // Create an instance of a `foo.op` operation. |
| %op = pdl_interp.create_operation "foo.op"(%arg0 : !pdl.value) {"attrA" = %attr0} -> (%type : !pdl.type) |
| ``` |
| }]; |
| |
| let arguments = (ins StrAttr:$name, |
| Variadic<PDL_InstOrRangeOf<PDL_Value>>:$operands, |
| Variadic<PDL_Attribute>:$attributes, |
| StrArrayAttr:$attributeNames, |
| Variadic<PDL_InstOrRangeOf<PDL_Type>>:$types); |
| let results = (outs PDL_Operation:$operation); |
| |
| let builders = [ |
| OpBuilder<(ins "StringRef":$name, "ValueRange":$types, |
| "ValueRange":$operands, "ValueRange":$attributes, |
| "ArrayAttr":$attributeNames), [{ |
| build($_builder, $_state, $_builder.getType<pdl::OperationType>(), name, |
| operands, attributes, attributeNames, types); |
| }]> |
| ]; |
| let assemblyFormat = [{ |
| $name (`(` $operands^ `:` type($operands) `)`)? |
| custom<CreateOperationOpAttributes>($attributes, $attributeNames) |
| (`->` `(` $types^ `:` type($types) `)`)? attr-dict |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CreateTypeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CreateTypeOp : PDLInterp_Op<"create_type", [NoSideEffect]> { |
| let summary = "Create an interpreter handle to a constant `Type`"; |
| let description = [{ |
| `pdl_interp.create_type` operations generate a handle within the interpreter |
| for a specific constant type value. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.create_type i64 |
| ``` |
| }]; |
| |
| let arguments = (ins TypeAttr:$value); |
| let results = (outs PDL_Type:$result); |
| let assemblyFormat = "$value attr-dict"; |
| |
| let builders = [ |
| OpBuilder<(ins "TypeAttr":$type), [{ |
| build($_builder, $_state, $_builder.getType<pdl::TypeType>(), type); |
| }]> |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::CreateTypesOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_CreateTypesOp : PDLInterp_Op<"create_types", [NoSideEffect]> { |
| let summary = "Create an interpreter handle to a range of constant `Type`s"; |
| let description = [{ |
| `pdl_interp.create_types` operations generate a handle within the |
| interpreter for a specific range of constant type values. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.create_types [i64, i64] |
| ``` |
| }]; |
| |
| let arguments = (ins TypeArrayAttr:$value); |
| let results = (outs PDL_RangeOf<PDL_Type>:$result); |
| let assemblyFormat = "$value attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::EraseOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_EraseOp : PDLInterp_Op<"erase"> { |
| let summary = "Mark an operation as `erased`"; |
| let description = [{ |
| `pdl.erase` operations are used to specify that an operation should be |
| marked as erased. The semantics of this operation correspond with the |
| `eraseOp` method on a `PatternRewriter`. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.erase %root |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation); |
| let assemblyFormat = "$operation attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::ExtractOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_ExtractOp |
| : PDLInterp_Op<"extract", [NoSideEffect, |
| TypesMatchWith< |
| "`range` is a PDL range whose element type matches type of `result`", |
| "result", "range", "pdl::RangeType::get($_self)">]> { |
| let summary = "Extract the item at the specified index in a range"; |
| let description = [{ |
| `pdl_interp.extract` operations are used to extract an item from a range |
| at the specified index. If the index is out of range, returns null. |
| |
| Example: |
| |
| ```mlir |
| // Extract the value at index 1 from a range of values. |
| %ops = pdl_interp.extract 1 of %values : !pdl.value |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_RangeOf<PDL_AnyType>:$range, |
| Confined<I32Attr, [IntNonNegative]>:$index); |
| let results = (outs PDL_AnyType:$result); |
| let assemblyFormat = "$index `of` $range `:` type($result) attr-dict"; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$range, "unsigned":$index), [{ |
| build($_builder, $_state, |
| range.getType().cast<pdl::RangeType>().getElementType(), |
| range, index); |
| }]>, |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::FinalizeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_FinalizeOp |
| : PDLInterp_Op<"finalize", [NoSideEffect, Terminator]> { |
| let summary = "Finalize a pattern match or rewrite sequence"; |
| let description = [{ |
| `pdl_interp.finalize` is used to denote the termination of a match or |
| rewrite sequence. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.finalize |
| ``` |
| }]; |
| let assemblyFormat = "attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::ForEachOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_ForEachOp |
| : PDLInterp_Op<"foreach", [Terminator]> { |
| let summary = "Iterates over a range of values or ranges"; |
| let description = [{ |
| `pdl_interp.foreach` iteratively selects an element from a range of values |
| and executes the region until pdl.continue is reached. |
| |
| In the bytecode interpreter, this operation is implemented by looping over |
| the values and, for each selection, running the bytecode until we reach |
| pdl.continue. This may result in multiple matches being reported. Note |
| that the input range is mutated (popped from). |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.foreach %op : !pdl.operation in %ops { |
| pdl_interp.continue |
| } -> ^next |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_RangeOf<PDL_AnyType>:$values); |
| let regions = (region AnyRegion:$region); |
| let successors = (successor AnySuccessor:$successor); |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$range, "Block *":$successor, "bool":$initLoop)> |
| ]; |
| |
| let extraClassDeclaration = [{ |
| /// Returns the loop variable. |
| BlockArgument getLoopVariable() { return region().getArgument(0); } |
| }]; |
| let parser = [{ return ::parseForEachOp(parser, result); }]; |
| let printer = [{ return ::print(p, *this); }]; |
| let verifier = [{ return ::verify(*this); }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetAttributeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetAttributeOp : PDLInterp_Op<"get_attribute", [NoSideEffect]> { |
| let summary = "Get a specified attribute value from an `Operation`"; |
| let description = [{ |
| `pdl_interp.get_attribute` operations try to get a specific attribute from |
| an operation. If the operation does not have that attribute, a null value is |
| returned. |
| |
| Example: |
| |
| ```mlir |
| %attr = pdl_interp.get_attribute "attr" of %op |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, |
| StrAttr:$name); |
| let results = (outs PDL_Attribute:$attribute); |
| let assemblyFormat = "$name `of` $operation attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetAttributeTypeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetAttributeTypeOp |
| : PDLInterp_Op<"get_attribute_type", [NoSideEffect]> { |
| let summary = "Get the result type of a specified `Attribute`"; |
| let description = [{ |
| `pdl_interp.get_attribute_type` operations get the resulting type of a |
| specific attribute. |
| |
| Example: |
| |
| ```mlir |
| %type = pdl_interp.get_attribute_type of %attr |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Attribute:$value); |
| let results = (outs PDL_Type:$result); |
| let assemblyFormat = "`of` $value attr-dict"; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$value), [{ |
| build($_builder, $_state, $_builder.getType<pdl::TypeType>(), value); |
| }]> |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetDefiningOpOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetDefiningOpOp |
| : PDLInterp_Op<"get_defining_op", [NoSideEffect]> { |
| let summary = "Get the defining operation of a `Value`"; |
| let description = [{ |
| `pdl_interp.get_defining_op` operations try to get the defining operation |
| of a specific value or range of values. In the case of range, the defining |
| op of the first value is returned. If the value is not an operation result |
| or range of operand results, null is returned. |
| |
| Example: |
| |
| ```mlir |
| %op = pdl_interp.get_defining_op of %value : !pdl.value |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_InstOrRangeOf<PDL_Value>:$value); |
| let results = (outs PDL_Operation:$operation); |
| let assemblyFormat = "`of` $value `:` type($value) attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetOperandOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetOperandOp : PDLInterp_Op<"get_operand", [NoSideEffect]> { |
| let summary = "Get a specified operand from an `Operation`"; |
| let description = [{ |
| `pdl_interp.get_operand` operations try to get a specific operand from an |
| operation If the operation does not have an operand for the given index, a |
| null value is returned. |
| |
| Example: |
| |
| ```mlir |
| %operand = pdl_interp.get_operand 1 of %op |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, |
| Confined<I32Attr, [IntNonNegative]>:$index); |
| let results = (outs PDL_Value:$value); |
| let assemblyFormat = "$index `of` $operation attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetOperandsOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetOperandsOp : PDLInterp_Op<"get_operands", [NoSideEffect]> { |
| let summary = "Get a specified operand group from an `Operation`"; |
| let description = [{ |
| `pdl_interp.get_operands` operations try to get a specific operand |
| group from an operation. If the expected result is a single Value, null is |
| returned if the operand group is not of size 1. If a range is expected, |
| null is returned if the operand group is invalid. If no index is provided, |
| the returned operand group corresponds to all operands of the operation. |
| |
| Example: |
| |
| ```mlir |
| // Get the first group of operands from an operation, and expect a single |
| // element. |
| %operand = pdl_interp.get_operands 0 of %op : !pdl.value |
| |
| // Get the first group of operands from an operation. |
| %operands = pdl_interp.get_operands 0 of %op : !pdl.range<value> |
| |
| // Get all of the operands from an operation. |
| %operands = pdl_interp.get_operands of %op : !pdl.range<value> |
| ``` |
| }]; |
| |
| let arguments = (ins |
| PDL_Operation:$operation, |
| OptionalAttr<Confined<I32Attr, [IntNonNegative]>>:$index |
| ); |
| let results = (outs PDL_InstOrRangeOf<PDL_Value>:$value); |
| let assemblyFormat = "($index^)? `of` $operation `:` type($value) attr-dict"; |
| let builders = [ |
| OpBuilder<(ins "Type":$resultType, "Value":$operation, |
| "Optional<unsigned>":$index), [{ |
| build($_builder, $_state, resultType, operation, |
| index ? $_builder.getI32IntegerAttr(*index) : IntegerAttr()); |
| }]>, |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetResultOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetResultOp : PDLInterp_Op<"get_result", [NoSideEffect]> { |
| let summary = "Get a specified result from an `Operation`"; |
| let description = [{ |
| `pdl_interp.get_result` operations try to get a specific result from an |
| operation. If the operation does not have a result for the given index, a |
| null value is returned. |
| |
| Example: |
| |
| ```mlir |
| %result = pdl_interp.get_result 1 of %op |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, |
| Confined<I32Attr, [IntNonNegative]>:$index); |
| let results = (outs PDL_Value:$value); |
| let assemblyFormat = "$index `of` $operation attr-dict"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetResultsOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetResultsOp : PDLInterp_Op<"get_results", [NoSideEffect]> { |
| let summary = "Get a specified result group from an `Operation`"; |
| let description = [{ |
| `pdl_interp.get_results` operations try to get a specific result group |
| from an operation. If the expected result is a single Value, null is |
| returned if the result group is not of size 1. If a range is expected, |
| null is returned if the result group is invalid. If no index is provided, |
| the returned operand group corresponds to all results of the operation. |
| |
| Example: |
| |
| ```mlir |
| // Get the first group of results from an operation, and expect a single |
| // element. |
| %result = pdl_interp.get_results 0 of %op : !pdl.value |
| |
| // Get the first group of results from an operation. |
| %results = pdl_interp.get_results 0 of %op : !pdl.range<value> |
| |
| // Get all of the results from an operation. |
| %results = pdl_interp.get_results of %op : !pdl.range<value> |
| ``` |
| }]; |
| |
| let arguments = (ins |
| PDL_Operation:$operation, |
| OptionalAttr<Confined<I32Attr, [IntNonNegative]>>:$index |
| ); |
| let results = (outs PDL_InstOrRangeOf<PDL_Value>:$value); |
| let assemblyFormat = "($index^)? `of` $operation `:` type($value) attr-dict"; |
| let builders = [ |
| OpBuilder<(ins "Type":$resultType, "Value":$operation, |
| "Optional<unsigned>":$index), [{ |
| build($_builder, $_state, resultType, operation, |
| index ? $_builder.getI32IntegerAttr(*index) : IntegerAttr()); |
| }]>, |
| OpBuilder<(ins "Value":$operation), [{ |
| build($_builder, $_state, |
| pdl::RangeType::get($_builder.getType<pdl::ValueType>()), operation, |
| IntegerAttr()); |
| }]>, |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetUsersOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetUsersOp |
| : PDLInterp_Op<"get_users", [NoSideEffect]> { |
| let summary = "Get the users of a `Value`"; |
| let description = [{ |
| `pdl_interp.get_users` extracts the users that accept this value. In the |
| case of a range, the union of users of the all the values are returned, |
| similarly to ResultRange::getUsers. |
| |
| Example: |
| |
| ```mlir |
| // Get all the users of a single value. |
| %ops = pdl_interp.get_users of %value : !pdl.value |
| |
| // Get all the users of the first value in a range. |
| %ops = pdl_interp.get_users of %values : !pdl.range<value> |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_InstOrRangeOf<PDL_Value>:$value); |
| let results = (outs PDL_RangeOf<PDL_Operation>:$operations); |
| let assemblyFormat = "`of` $value `:` type($value) attr-dict"; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$value), [{ |
| build($_builder, $_state, |
| pdl::RangeType::get($_builder.getType<pdl::OperationType>()), |
| value); |
| }]>, |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::GetValueTypeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_GetValueTypeOp : PDLInterp_Op<"get_value_type", [NoSideEffect, |
| TypesMatchWith<"`value` type matches arity of `result`", |
| "result", "value", "getGetValueTypeOpValueType($_self)">]> { |
| let summary = "Get the result type of a specified `Value`"; |
| let description = [{ |
| `pdl_interp.get_value_type` operations get the resulting type of a specific |
| value or range thereof. |
| |
| Example: |
| |
| ```mlir |
| // Get the type of a single value. |
| %type = pdl_interp.get_value_type of %value : !pdl.type |
| |
| // Get the types of a value range. |
| %type = pdl_interp.get_value_type of %values : !pdl.range<type> |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_InstOrRangeOf<PDL_Value>:$value); |
| let results = (outs PDL_InstOrRangeOf<PDL_Type>:$result); |
| let assemblyFormat = "`of` $value `:` type($result) attr-dict"; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$value), [{ |
| Type valType = value.getType(); |
| Type typeType = $_builder.getType<pdl::TypeType>(); |
| build($_builder, $_state, |
| valType.isa<pdl::RangeType>() ? pdl::RangeType::get(typeType) |
| : typeType, |
| value); |
| }]> |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::InferredTypesOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_InferredTypesOp : PDLInterp_Op<"inferred_types"> { |
| let summary = "Generate a handle to a range of Types that are \"inferred\""; |
| let description = [{ |
| `pdl_interp.inferred_types` operations generate handles to ranges of types |
| that should be inferred. This signals to other operations, such as |
| `pdl_interp.create_operation`, that these types should be inferred. |
| |
| Example: |
| |
| ```mlir |
| %types = pdl_interp.inferred_types |
| ``` |
| }]; |
| let results = (outs PDL_RangeOf<PDL_Type>:$type); |
| let assemblyFormat = "attr-dict"; |
| let builders = [ |
| OpBuilder<(ins), [{ |
| build($_builder, $_state, |
| pdl::RangeType::get($_builder.getType<pdl::TypeType>())); |
| }]> |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::IsNotNullOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_IsNotNullOp |
| : PDLInterp_PredicateOp<"is_not_null", [NoSideEffect]> { |
| let summary = "Check if a positional value is non-null"; |
| let description = [{ |
| `pdl_interp.is_not_null` operations check that a positional value or range |
| exists. For ranges, this does not mean that the range was simply empty. On |
| success, this operation branches to the true destination. Otherwise, the |
| false destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.is_not_null %value : !pdl.value -> ^matchDest, ^failureDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_AnyType:$value); |
| let assemblyFormat = "$value `:` type($value) attr-dict `->` successors"; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::RecordMatchOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_RecordMatchOp |
| : PDLInterp_Op<"record_match", [AttrSizedOperandSegments, Terminator]> { |
| let summary = "Record the metadata for a successful pattern match"; |
| let description = [{ |
| `pdl_interp.record_match` operations record a successful pattern match with |
| the interpreter and branch to the next part of the matcher. The metadata |
| recorded by these operations correspond to a specific `pdl.pattern`, as well |
| as what values were used during that match that should be propagated to the |
| rewriter. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.record_match @rewriters::myRewriter(%root : !pdl.operation) : benefit(1), loc([%root, %op1]), root("foo.op") -> ^nextDest |
| ``` |
| }]; |
| |
| let arguments = (ins Variadic<PDL_AnyType>:$inputs, |
| Variadic<PDL_Operation>:$matchedOps, |
| SymbolRefAttr:$rewriter, |
| OptionalAttr<StrAttr>:$rootKind, |
| OptionalAttr<StrArrayAttr>:$generatedOps, |
| Confined<I16Attr, [IntNonNegative]>:$benefit); |
| let successors = (successor AnySuccessor:$dest); |
| let assemblyFormat = [{ |
| $rewriter (`(` $inputs^ `:` type($inputs) `)`)? `:` |
| `benefit` `(` $benefit `)` `,` |
| (`generatedOps` `(` $generatedOps^ `)` `,`)? |
| `loc` `(` `[` $matchedOps `]` `)` |
| (`,` `root` `(` $rootKind^ `)`)? attr-dict `->` $dest |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::ReplaceOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_ReplaceOp : PDLInterp_Op<"replace"> { |
| let summary = "Mark an operation as `replace`d"; |
| let description = [{ |
| `pdl_interp.replaced` operations are used to specify that an operation |
| should be marked as replaced. The semantics of this operation correspond |
| with the `replaceOp` method on a `PatternRewriter`. The set of replacement |
| values must match the number of results specified by the operation. |
| |
| Example: |
| |
| ```mlir |
| // Replace root node with 2 values: |
| pdl_interp.replace %root with (%val0, %val1 : !pdl.type, !pdl.type) |
| ``` |
| }]; |
| let arguments = (ins PDL_Operation:$operation, |
| Variadic<PDL_InstOrRangeOf<PDL_Value>>:$replValues); |
| let assemblyFormat = [{ |
| $operation `with` ` ` `(` ($replValues^ `:` type($replValues))? `)` |
| attr-dict |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::SwitchAttributeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_SwitchAttributeOp |
| : PDLInterp_SwitchOp<"switch_attribute", [NoSideEffect]> { |
| let summary = "Switch on the value of an `Attribute`"; |
| let description = [{ |
| `pdl_interp.switch_attribute` operations compare the value of a given |
| attribute with a set of constant attributes. If the value matches one of the |
| provided case values the destination for that case value is taken, otherwise |
| the default destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.switch_attribute %attr to [10, true](^10Dest, ^trueDest) -> ^defaultDest |
| ``` |
| }]; |
| let arguments = (ins PDL_Attribute:$attribute, ArrayAttr:$caseValues); |
| let assemblyFormat = [{ |
| $attribute `to` $caseValues `(` $cases `)` attr-dict `->` $defaultDest |
| }]; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$attribute, "ArrayRef<Attribute>":$caseValues, |
| "Block *":$defaultDest, "BlockRange":$dests), [{ |
| build($_builder, $_state, attribute, $_builder.getArrayAttr(caseValues), |
| defaultDest, dests); |
| }]>]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::SwitchOperandCountOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_SwitchOperandCountOp |
| : PDLInterp_SwitchOp<"switch_operand_count", [NoSideEffect]> { |
| let summary = "Switch on the operand count of an `Operation`"; |
| let description = [{ |
| `pdl_interp.switch_operand_count` operations compare the operand count of a |
| given operation with a set of potential counts. If the value matches one of |
| the provided case values the destination for that case value is taken, |
| otherwise the default destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.switch_operand_count of %op to [10, 2] -> ^10Dest, ^2Dest, ^defaultDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, I32ElementsAttr:$caseValues); |
| let assemblyFormat = [{ |
| `of` $operation `to` $caseValues `(` $cases `)` attr-dict `->` $defaultDest |
| }]; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$operation, "ArrayRef<int32_t>":$counts, |
| "Block *":$defaultDest, "BlockRange":$dests), [{ |
| build($_builder, $_state, operation, $_builder.getI32VectorAttr(counts), |
| defaultDest, dests); |
| }]>]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::SwitchOperationNameOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_SwitchOperationNameOp |
| : PDLInterp_SwitchOp<"switch_operation_name", [NoSideEffect]> { |
| let summary = "Switch on the OperationName of an `Operation`"; |
| let description = [{ |
| `pdl_interp.switch_operation_name` operations compare the name of a given |
| operation with a set of known names. If the value matches one of the |
| provided case values the destination for that case value is taken, otherwise |
| the default destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.switch_operation_name of %op to ["foo.op", "bar.op"](^fooDest, ^barDest) -> ^defaultDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, |
| StrArrayAttr:$caseValues); |
| let assemblyFormat = [{ |
| `of` $operation `to` $caseValues `(` $cases `)` attr-dict `->` $defaultDest |
| }]; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$operation, "ArrayRef<OperationName>":$names, |
| "Block *":$defaultDest, "BlockRange":$dests), [{ |
| auto stringNames = llvm::to_vector<8>(llvm::map_range(names, |
| [](OperationName name) { return name.getStringRef(); })); |
| build($_builder, $_state, operation, $_builder.getStrArrayAttr(stringNames), |
| defaultDest, dests); |
| }]>, |
| ]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::SwitchResultCountOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_SwitchResultCountOp |
| : PDLInterp_SwitchOp<"switch_result_count", [NoSideEffect]> { |
| let summary = "Switch on the result count of an `Operation`"; |
| let description = [{ |
| `pdl_interp.switch_result_count` operations compare the result count of a |
| given operation with a set of potential counts. If the value matches one of |
| the provided case values the destination for that case value is taken, |
| otherwise the default destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.switch_result_count of %op to [0, 2](^0Dest, ^2Dest) -> ^defaultDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Operation:$operation, I32ElementsAttr:$caseValues); |
| let assemblyFormat = [{ |
| `of` $operation `to` $caseValues `(` $cases `)` attr-dict `->` $defaultDest |
| }]; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$operation, "ArrayRef<int32_t>":$counts, |
| "Block *":$defaultDest, "BlockRange":$dests), [{ |
| build($_builder, $_state, operation, $_builder.getI32VectorAttr(counts), |
| defaultDest, dests); |
| }]>]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::SwitchTypeOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_SwitchTypeOp : PDLInterp_SwitchOp<"switch_type", [NoSideEffect]> { |
| let summary = "Switch on a `Type` value"; |
| let description = [{ |
| `pdl_interp.switch_type` operations compare a type with a set of statically |
| known types. If the value matches one of the provided case values the |
| destination for that case value is taken, otherwise the default destination |
| is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.switch_type %type to [i32, i64] -> ^i32Dest, ^i64Dest, ^defaultDest |
| ``` |
| }]; |
| |
| let arguments = (ins PDL_Type:$value, TypeArrayAttr:$caseValues); |
| let assemblyFormat = [{ |
| $value `to` $caseValues `(` $cases `)` attr-dict `->` $defaultDest |
| }]; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$edge, "ArrayRef<Attribute>":$types, |
| "Block *":$defaultDest, "BlockRange":$dests), [{ |
| build($_builder, $_state, edge, $_builder.getArrayAttr(types), |
| defaultDest, dests); |
| }]>, |
| ]; |
| |
| let extraClassDeclaration = [{ |
| auto getCaseTypes() { return caseValues().getAsValueRange<TypeAttr>(); } |
| }]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // pdl_interp::SwitchTypesOp |
| //===----------------------------------------------------------------------===// |
| |
| def PDLInterp_SwitchTypesOp : PDLInterp_SwitchOp<"switch_types", |
| [NoSideEffect]> { |
| let summary = "Switch on a range of `Type` values"; |
| let description = [{ |
| `pdl_interp.switch_types` operations compare a range of types with a set of |
| statically known ranges. If the value matches one of the provided case |
| values the destination for that case value is taken, otherwise the default |
| destination is taken. |
| |
| Example: |
| |
| ```mlir |
| pdl_interp.switch_types %type is [[i32], [i64, i64]] -> ^i32Dest, ^i64Dest, ^defaultDest |
| ``` |
| }]; |
| |
| let arguments = (ins |
| PDL_RangeOf<PDL_Type>:$value, |
| TypedArrayAttrBase<TypeArrayAttr, "type-array array attribute">:$caseValues |
| ); |
| let assemblyFormat = [{ |
| $value `to` $caseValues `(` $cases `)` attr-dict `->` $defaultDest |
| }]; |
| |
| let builders = [ |
| OpBuilder<(ins "Value":$edge, "ArrayRef<Attribute>":$types, |
| "Block *":$defaultDest, "BlockRange":$dests), [{ |
| build($_builder, $_state, edge, $_builder.getArrayAttr(types), |
| defaultDest, dests); |
| }]>, |
| ]; |
| |
| let extraClassDeclaration = [{ |
| auto getCaseTypes() { return caseValues().getAsRange<ArrayAttr>(); } |
| }]; |
| } |
| |
| #endif // MLIR_DIALECT_PDLINTERP_IR_PDLINTERPOPS |