LLZK 2.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
OpTraits.td
Go to the documentation of this file.
1//===-- OpTraits.td - Custom Trait classes for ops ---------*- tablegen -*-===//
2//
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
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef LLZK_SHARED_OP_HELPER
11#define LLZK_SHARED_OP_HELPER
12
13include "mlir/Interfaces/InferTypeOpInterface.td"
14include "mlir/Interfaces/SideEffectInterfaces.td"
15include "mlir/IR/SymbolInterfaces.td"
16
17// Do not use this directly. Use LLZKSymbolTable.
18def LLZKSymbolTableImplTrait : NativeOpTrait<"LLZKSymbolTableImplTrait"> {
19 string cppNamespace = "::llzk";
20}
21
22// Always use this trait instead of builtin `SymbolTable` trait.
23//
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
32// table.
33def LLZKSymbolTable : TraitList<[LLZKSymbolTableImplTrait, SymbolTable]>;
34
35// Implements verification for ops with an affine_map instantiation list. These
36// ops are expected to contain the following in their `arguments` list:
37// - VariadicOfVariadic<Index, "mapOpGroupSizes">:$mapOperands
38// - DefaultValuedAttr<DenseI32ArrayAttr, "{}">:$numDimsPerMap
39// - DenseI32ArrayAttr:$mapOpGroupSizes
40// Additionally, if the op also has the `AttrSizedOperandSegments` trait, the
41// parameter of this trait specifies the index within the `operandSegmentSizes`
42// attribute associated with the `$mapOperands` argument, otherwise the
43// parameter is ignored. All of these attributes are necessary because MLIR
44// stores all operands for an Op in a single list. These attributes specify how
45// the list of operands is split into logical pieces for the operand components.
46//
47// For example, suppose the `CreateArrayOp` is used to create an array with type
48// `!array.type<affine_map<(d0)->(d0)>,affine_map<(d0)[s0]->(d0+s0)> x i1>`
49//
50// 1) `CreateArrayOp` requires the `AttrSizedOperandSegments` trait because it
51// defines two variadic arguments: `$elements` and `$mapOperands` (in that
52// order). Thus, the `operandSegmentSizes` attribute is automatically defined
53// to specify the number of operands that belong to each variadic argument:
54// `operandSegmentSizes = array<i32: COUNT($elements), COUNT($mapOperands)>`
55// In the case of `CreateArrayOp`, one of those sizes will always be 0 because
56// its assembly format has `$elements` and `$mapOperands` as alternatives. In
57// this example, `COUNT($elements) = 0` and `COUNT($mapOperands) = 3` (this is
58// the sum of operand count for all affine_map that are used as array dimensions
59// in the result array type).
60//
61// 2) The `$mapOpGroupSizes` attribute groups the `$mapOperands` per affine_map.
62// This implies that their sum equals `COUNT($mapOperands)`. In the example, the
63// first affine_map has 1 parameter and the second has 2 so:
64// `mapOpGroupSizes = array<i32: 1, 2>`
65//
66// 3) Finally, the `$numDimsPerMap` attribute splits the `$mapOperands` in each
67// group into the dimensional and symbolic inputs for each affine_map.
68// Dimensional inputs appear between the () and symbolic inputs appear between
69// the []. LLZK mainly uses dimensional inputs and not symbolic inputs but both
70// are fully supported. The length of `$numDimsPerMap` must equal the length of
71// `$mapOpGroupSizes` and each element of `$numDimsPerMap` must be less than the
72// corresponding element of `$mapOpGroupSizes`. In the example, the both
73// affine_map instantiations in the array type have 1 dimensional input so:
74// `numDimsPerMap = array<i32: 1, 1>`
75//
76// It is also recomended to use `custom<AttrDictWithWarnings>(attr-dict)` in the
77// assembly format (or the associated parse/print functions directly) to parse
78// the attribute dictionary in these ops and present warnings if the
79// aforementioned attributes are manually specified.
80class VerifySizesForMultiAffineOps<int operandSegmentIndex>
81 : ParamNativeOpTrait<"VerifySizesForMultiAffineOps",
82 ""#operandSegmentIndex>,
83 StructuralOpTrait {
84 string cppNamespace = "::llzk";
85}
86
87// Identical to `TypesMatchWith` with `rhsArg = result`. This should be used
88// instead of `TypesMatchWith` when custom return type inference is necessary
89// (via `InferTypeOpAdaptor*`) because MLIR has special handing for
90// `TypesMatchWith` that results in "error: redefinition of 'inferReturnTypes'".
91class TypeMatchResultWith<string lhsArg, string lhsSummary = lhsArg,
92 string transform,
93 string comparator = "std::equal_to<>()">
94 : PredOpTrait<
95 "result type matches with "#lhsSummary#" type",
96 CPred<comparator#"("#!subst("$_self", "$"#lhsArg#".getType()",
97 transform)#", $result.getType())">> {
98 string lhs = lhsArg;
99 string rhs = "result";
100 string transformer = transform;
101}
102
103// Like TypesUnify with `rhsArg = "result"`
104class TypeUnifyWithResult<string lhsArg, string lhsSummary = lhsArg,
105 string transform = "$_self">
106 : TypeMatchResultWith<lhsArg, lhsSummary, transform, "::llzk::typesUnify">;
107
108// Implementation of TypesMatchWith for Variadic `rhsArg` that returns success
109// if `rhsArg` is empty.
110class VariadicTypesMatchWith<string summary, string lhsArg, string rhsArg,
111 string transform,
112 string comparator = "std::equal_to<>()">
113 : TypesMatchWith<
114 summary, lhsArg, rhsArg, transform,
115 "get"#snakeCaseToCamelCase<rhsArg>.ret#"().empty() || "#comparator>;
116
117// Type constraint `llzk::typesUnify(transform(lhs.getType()), rhs.getType())`.
118// If either parameter is `$result` it is recommended to use TypeUnifyWithResult
119// instead as this is likely too restrictive when type variables are involved.
120class TypesUnify<string lhsArg, string rhsArg, string lhsSummary = lhsArg,
121 string rhsSummary = rhsArg, string transform = "$_self">
122 : TypesMatchWith<rhsSummary#" type matches with "#lhsSummary#" type",
123 lhsArg, rhsArg, transform, "::llzk::typesUnify">;
124
125// Returns success if `elementArg` unifies with the `arrayArg` element type.
126class ArrayElemTypeUnifyWith<string arrayArg, string elementArg>
127 : TypesUnify<
128 arrayArg, elementArg, arrayArg#" element", elementArg,
129 "::llvm::cast<::llzk::array::ArrayType>($_self).getElementType()">;
130
131// Returns success if `$result` unifies with the `arrayArg` element type.
132class ArrayElemTypeUnifyWithResult<string arrayArg>
133 : TypeMatchResultWith<
134 arrayArg, arrayArg#" element",
135 "::llvm::cast<::llzk::array::ArrayType>($_self).getElementType()",
136 "::llzk::typesUnify">;
137
138// ArrayElemTypeUnifyWithResult + InferTypeOpAdaptorWithIsCompatible (i.e.
139// generate inferReturnTypes() and isCompatibleReturnTypes() functions)
140class ArrayTypeElemsUnifyWithResultCustomInfer<string arrayArg>
141 : TraitList<[ArrayElemTypeUnifyWithResult<arrayArg>,
142 InferTypeOpAdaptorWithIsCompatible]>;
143
144#endif // LLZK_SHARED_OP_HELPER