1//===-- OpTraits.td - Custom Trait classes for ops ---------*- tablegen -*-===//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
8//===----------------------------------------------------------------------===//
10#ifndef LLZK_SHARED_OP_HELPER
11#define LLZK_SHARED_OP_HELPER
13include "mlir/Interfaces/InferTypeOpInterface.td"
14include "mlir/Interfaces/SideEffectInterfaces.td"
15include "mlir/IR/SymbolInterfaces.td"
17// Do not use this directly. Use LLZKSymbolTable.
18def LLZKSymbolTableImplTrait : NativeOpTrait<"LLZKSymbolTableImplTrait"> {
19 string cppNamespace = "::llzk";
22// Always use this trait instead of builtin `SymbolTable` trait.
24// This trait avoids an assertion failure in the `SymbolTable` class constructor
25// when the symbol table is malformed (e.g. duplicate symbols), instead allowing
26// it produce an error diagnostic. This can happen due to the implementations of
27// `verifySymbolUses()` that do symbol lookups from the root module. These are
28// called before ancestor module symbol tables are verified, thus leading to an
29// assertion failure before the verifier would produce a friendly diagnostic.
30// This trait handles that by injecting the usual symbol table verification on
31// ancestor symbol tables before performing verification of the current symbol
33def LLZKSymbolTable : TraitList<[LLZKSymbolTableImplTrait, SymbolTable]>;
35/// Verify that the operation has a parent of type `op` somewhere in its
37class HasAncestor<string op> : ParamNativeOpTrait<"HasAncestor", op>,
39 string cppNamespace = "::llzk";
42// Implements verification for ops with an affine_map instantiation list. These
43// ops are expected to contain the following in their `arguments` list:
44// - VariadicOfVariadic<Index, "mapOpGroupSizes">:$mapOperands
45// - DefaultValuedAttr<DenseI32ArrayAttr, "{}">:$numDimsPerMap
46// - DenseI32ArrayAttr:$mapOpGroupSizes
47// Additionally, if the op also has the `AttrSizedOperandSegments` trait, the
48// parameter of this trait specifies the index within the `operandSegmentSizes`
49// attribute associated with the `$mapOperands` argument, otherwise the
50// parameter is ignored. All of these attributes are necessary because MLIR
51// stores all operands for an Op in a single list. These attributes specify how
52// the list of operands is split into logical pieces for the operand components.
54// For example, suppose the `CreateArrayOp` is used to create an array with type
55// `!array.type<affine_map<(d0)->(d0)>,affine_map<(d0)[s0]->(d0+s0)> x i1>`
57// 1) `CreateArrayOp` requires the `AttrSizedOperandSegments` trait because it
58// defines two variadic arguments: `$elements` and `$mapOperands` (in that
59// order). Thus, the `operandSegmentSizes` attribute is automatically defined
60// to specify the number of operands that belong to each variadic argument:
61// `operandSegmentSizes = array<i32: COUNT($elements), COUNT($mapOperands)>`
62// In the case of `CreateArrayOp`, one of those sizes will always be 0 because
63// its assembly format has `$elements` and `$mapOperands` as alternatives. In
64// this example, `COUNT($elements) = 0` and `COUNT($mapOperands) = 3` (this is
65// the sum of operand count for all affine_map that are used as array dimensions
66// in the result array type).
68// 2) The `$mapOpGroupSizes` attribute groups the `$mapOperands` per affine_map.
69// This implies that their sum equals `COUNT($mapOperands)`. In the example, the
70// first affine_map has 1 parameter and the second has 2 so:
71// `mapOpGroupSizes = array<i32: 1, 2>`
73// 3) Finally, the `$numDimsPerMap` attribute splits the `$mapOperands` in each
74// group into the dimensional and symbolic inputs for each affine_map.
75// Dimensional inputs appear between the () and symbolic inputs appear between
76// the []. LLZK mainly uses dimensional inputs and not symbolic inputs but both
77// are fully supported. The length of `$numDimsPerMap` must equal the length of
78// `$mapOpGroupSizes` and each element of `$numDimsPerMap` must be less than the
79// corresponding element of `$mapOpGroupSizes`. In the example, the both
80// affine_map instantiations in the array type have 1 dimensional input so:
81// `numDimsPerMap = array<i32: 1, 1>`
83// It is also recomended to use `custom<AttrDictWithWarnings>(attr-dict)` in the
84// assembly format (or the associated parse/print functions directly) to parse
85// the attribute dictionary in these ops and present warnings if the
86// aforementioned attributes are manually specified.
87class VerifySizesForMultiAffineOps<int operandSegmentIndex>
88 : ParamNativeOpTrait<"VerifySizesForMultiAffineOps",
89 ""#operandSegmentIndex>,
91 string cppNamespace = "::llzk";
94// Identical to `TypesMatchWith` with `rhsArg = result`. This should be used
95// instead of `TypesMatchWith` when custom return type inference is necessary
96// (via `InferTypeOpAdaptor*`) because MLIR has special handing for
97// `TypesMatchWith` that results in "error: redefinition of 'inferReturnTypes'".
98class TypeMatchResultWith<string lhsArg, string lhsSummary = lhsArg,
100 string comparator = "std::equal_to<>()">
102 "result type matches with "#lhsSummary#" type",
103 CPred<comparator#"("#!subst("$_self", "$"#lhsArg#".getType()",
104 transform)#", $result.getType())">> {
106 string rhs = "result";
107 string transformer = transform;
110// Like TypesUnify with `rhsArg = "result"`
111class TypeUnifyWithResult<string lhsArg, string lhsSummary = lhsArg,
112 string transform = "$_self">
113 : TypeMatchResultWith<lhsArg, lhsSummary, transform, "::llzk::typesUnify">;
115// Implementation of TypesMatchWith for Variadic `rhsArg` that returns success
116// if `rhsArg` is empty.
117class VariadicTypesMatchWith<string summary, string lhsArg, string rhsArg,
119 string comparator = "std::equal_to<>()">
121 summary, lhsArg, rhsArg, transform,
122 "get"#snakeCaseToCamelCase<rhsArg>.ret#"().empty() || "#comparator>;
124// Type constraint `llzk::typesUnify(transform(lhs.getType()), rhs.getType())`.
125// If either parameter is `$result` it is recommended to use TypeUnifyWithResult
126// instead as this is likely too restrictive when type variables are involved.
127class TypesUnify<string lhsArg, string rhsArg, string lhsSummary = lhsArg,
128 string rhsSummary = rhsArg, string transform = "$_self">
129 : TypesMatchWith<rhsSummary#" type matches with "#lhsSummary#" type",
130 lhsArg, rhsArg, transform, "::llzk::typesUnify">;
132// Returns success if `elementArg` unifies with the `arrayArg` element type.
133class ArrayElemTypeUnifyWith<string arrayArg, string elementArg>
135 arrayArg, elementArg, arrayArg#" element", elementArg,
136 "::llvm::cast<::llzk::array::ArrayType>($_self).getElementType()">;
138// Returns success if `$result` unifies with the `arrayArg` element type.
139class ArrayElemTypeUnifyWithResult<string arrayArg>
140 : TypeMatchResultWith<
141 arrayArg, arrayArg#" element",
142 "::llvm::cast<::llzk::array::ArrayType>($_self).getElementType()",
143 "::llzk::typesUnify">;
145// ArrayElemTypeUnifyWithResult + InferTypeOpAdaptorWithIsCompatible (i.e.
146// generate inferReturnTypes() and isCompatibleReturnTypes() functions)
147class ArrayTypeElemsUnifyWithResultCustomInfer<string arrayArg>
148 : TraitList<[ArrayElemTypeUnifyWithResult<arrayArg>,
149 InferTypeOpAdaptorWithIsCompatible]>;
151#endif // LLZK_SHARED_OP_HELPER