LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
OpHelpers.h
Go to the documentation of this file.
1//===-- OpHelpers.h ---------------------------------------------*- C++ -*-===//
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#pragma once
11
14#include "llzk/Util/Constants.h"
17
18#include <mlir/IR/BuiltinTypes.h>
19#include <mlir/IR/OpImplementation.h>
20#include <mlir/IR/Operation.h>
21#include <mlir/IR/SymbolTable.h>
22#include <mlir/Support/LogicalResult.h>
23
24#include <llvm/ADT/STLExtras.h>
25#include <llvm/ADT/SmallString.h>
26#include <llvm/ADT/StringRef.h>
27#include <llvm/Support/Debug.h>
28
29namespace llzk {
30
34template <typename OpClass> inline llvm::StringLiteral getOperationName() {
35 return OpClass::getOperationName();
36}
37
40template <typename OpClass> inline OpClass getSelfOrParentOfType(mlir::Operation *op) {
41 if (op) {
42 if (OpClass self = llvm::dyn_cast<OpClass>(op)) {
43 return self;
44 }
45 if (OpClass parent = op->getParentOfType<OpClass>()) {
46 return parent;
47 }
48 }
49 return {};
50}
51
53template <typename OpClass> inline OpClass getParentOfType(mlir::Operation *op) {
54 if (op) {
55 if (OpClass p = op->getParentOfType<OpClass>()) {
56 return p;
57 }
58 }
59 return {};
60}
61
64template <typename... OpTys> bool hasParentThatIsa(mlir::Operation *op) {
65 while ((op = op->getParentOp())) {
66 if (llvm::isa<OpTys...>(op)) {
67 return true;
68 }
69 }
70 return false;
71}
72
74template <typename TypeClass>
75// Suppress false positive from `clang-tidy`
76// NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
78 : public mlir::OpTrait::TraitBase<TypeClass, LLZKSymbolTableImplTrait> {
79public:
80 static mlir::LogicalResult verifyRegionTrait(mlir::Operation *op) {
81 // Note: the current op will be checked by the normal `SymbolTable` trait that is
82 // included in `LLZKSymbolTable`. Checking it here would cause the same error described
83 // in `LLZKSymbolTable`.
84 while ((op = op->getParentWithTrait<mlir::OpTrait::SymbolTable>())) {
85 if (mlir::failed(mlir::detail::verifySymbolTable(op))) {
86 return mlir::failure();
87 }
88 }
89 return mlir::success();
90 }
91};
92
94template <typename Ancestor, typename... Ancestors> struct HasAncestor {
95 template <typename ConcreteType>
96 // Suppress false positive from `clang-tidy`
97 // NOLINTNEXTLINE(bugprone-crtp-constructor-accessibility)
98 struct Impl : public mlir::OpTrait::TraitBase<ConcreteType, Impl> {
99 static mlir::LogicalResult verifyRegionTrait(mlir::Operation *op) {
101 return mlir::success();
102 }
103 auto diag = op->emitOpError();
104
105 if constexpr (sizeof...(Ancestors) == 0) {
106 diag << "must have an ancestor of type '" << Ancestor::getOperationName() << '\'';
107 } else {
108 diag << "must have an ancestor of one of the following types: ";
109 llvm::interleaveComma(
110 llvm::ArrayRef<llvm::StringLiteral>(
111 {Ancestor::getOperationName(), Ancestors::getOperationName()...}
112 ),
113 diag, [&diag](auto name) { diag << '\'' << name << '\''; }
114 );
115 }
116
117 return diag;
118 }
119 };
120};
121
124template <int OperandSegmentIndex> struct VerifySizesForMultiAffineOps {
125 template <typename TypeClass> class Impl : public mlir::OpTrait::TraitBase<TypeClass, Impl> {
126 inline static mlir::LogicalResult verifyHelper(mlir::Operation *op, int32_t segmentSize) {
127 TypeClass c = llvm::cast<TypeClass>(op);
129 op, segmentSize, c.getMapOpGroupSizesAttr(), c.getMapOperands(), c.getNumDimsPerMapAttr()
130 );
131 }
132
133 public:
134 static mlir::LogicalResult verifyTrait(mlir::Operation *op) {
135 if (TypeClass::template hasTrait<mlir::OpTrait::AttrSizedOperandSegments>()) {
136 // If the AttrSizedOperandSegments trait is present, must have `OperandSegmentIndex`.
137 static_assert(
138 OperandSegmentIndex >= 0,
139 "When the `AttrSizedOperandSegments` trait is present, the index of `$mapOperands` "
140 "within the `operandSegmentSizes` attribute must be specified."
141 );
142 mlir::DenseI32ArrayAttr segmentSizes = op->getAttrOfType<mlir::DenseI32ArrayAttr>(
143 mlir::OpTrait::AttrSizedOperandSegments<TypeClass>::getOperandSegmentSizeAttr()
144 );
145 assert(
146 OperandSegmentIndex < segmentSizes.size() &&
147 "Parameter of `VerifySizesForMultiAffineOps` exceeds the number of ODS-declared "
148 "operands"
149 );
150 return verifyHelper(op, segmentSizes[OperandSegmentIndex]);
151 } else {
152 // If the trait is not present, the `OperandSegmentIndex` is ignored. Pass `-1` to indicate
153 // that the checks against `operandSegmentSizes` should be skipped.
154 return verifyHelper(op, -1);
155 }
156 }
157 };
158};
159
160template <unsigned N>
161inline mlir::ParseResult parseDimAndSymbolList(
162 mlir::OpAsmParser &parser,
163 mlir::SmallVector<mlir::OpAsmParser::UnresolvedOperand, N> &mapOperands,
164 mlir::IntegerAttr &numDims
165) {
166 return affineMapHelpers::parseDimAndSymbolList(parser, mapOperands, numDims);
167}
168
170 mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRange mapOperands,
171 mlir::IntegerAttr numDims
172) {
173 return affineMapHelpers::printDimAndSymbolList(printer, op, mapOperands, numDims);
174}
175
176inline mlir::ParseResult parseMultiDimAndSymbolList(
177 mlir::OpAsmParser &parser,
178 mlir::SmallVector<mlir::SmallVector<mlir::OpAsmParser::UnresolvedOperand>> &multiMapOperands,
179 mlir::DenseI32ArrayAttr &numDimsPerMap
180) {
181 return affineMapHelpers::parseMultiDimAndSymbolList(parser, multiMapOperands, numDimsPerMap);
182}
183
185 mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRangeRange multiMapOperands,
186 mlir::DenseI32ArrayAttr numDimsPerMap
187) {
188 return affineMapHelpers::printMultiDimAndSymbolList(printer, op, multiMapOperands, numDimsPerMap);
189}
190
191inline mlir::ParseResult parseAttrDictWithWarnings(
192 mlir::OpAsmParser &parser, mlir::NamedAttrList &extraAttrs, mlir::OperationState &state
193) {
194 return affineMapHelpers::parseAttrDictWithWarnings(parser, extraAttrs, state);
195}
196
197template <typename ConcreteOp>
199 mlir::OpAsmPrinter &printer, ConcreteOp op, mlir::DictionaryAttr extraAttrs,
200 typename mlir::PropertiesSelector<ConcreteOp>::type state
201) {
202 return affineMapHelpers::printAttrDictWithWarnings(printer, op, extraAttrs, state);
203}
204
205inline mlir::ParseResult parseTemplateParams(mlir::AsmParser &parser, mlir::ArrayAttr &value) {
206 mlir::SmallVector<mlir::Attribute> elements;
207 auto parseElement = [&]() -> mlir::ParseResult {
208 // `?` is a wildcard meaning "infer this parameter"; only valid for tvar-restricted params.
209 if (mlir::succeeded(parser.parseOptionalQuestion())) {
210 elements.push_back(parser.getBuilder().getIndexAttr(mlir::ShapedType::kDynamic));
211 return mlir::success();
212 }
213 auto attrParseResult = mlir::FieldParser<mlir::Attribute>::parse(parser);
214 if (mlir::failed(attrParseResult)) {
215 return parser.emitError(
216 parser.getCurrentLocation(), "failed to parse template parameter attribute"
217 );
218 }
219 auto emitError = [&parser] {
220 return llzk::InFlightDiagnosticWrapper(parser.emitError(parser.getCurrentLocation()));
221 };
222 mlir::FailureOr<mlir::Attribute> forced = forceIntAttrType(*attrParseResult, emitError);
223 if (mlir::failed(forced)) {
224 return mlir::failure();
225 }
226 elements.push_back(*forced);
227 return mlir::success();
228 };
229 auto res = parser.parseCommaSeparatedList(mlir::AsmParser::Delimiter::Square, parseElement);
230 if (mlir::failed(res)) {
231 return res; // parseElement() already emits a sufficient error message
232 }
233 value = parser.getBuilder().getArrayAttr(elements);
234 return mlir::success();
235}
236
237// 2 parameter version used by types
238inline void printTemplateParams(mlir::AsmPrinter &printer, mlir::ArrayAttr value) {
239 printer << '[';
240 printAttrs(printer, value.getValue(), ", ");
241 printer << ']';
242}
243
244// 3 parameter version used by ops
245inline void printTemplateParams(mlir::AsmPrinter &printer, void *, mlir::ArrayAttr value) {
246 printTemplateParams(printer, value);
247}
248
249} // namespace llzk
Wrapper around InFlightDiagnostic that can either be a regular InFlightDiagnostic or a special versio...
Definition ErrorHelper.h:26
See LLZKSymbolTable ODS documentation for details.
Definition OpHelpers.h:78
static mlir::LogicalResult verifyRegionTrait(mlir::Operation *op)
Definition OpHelpers.h:80
static mlir::LogicalResult verifyTrait(mlir::Operation *op)
Definition OpHelpers.h:134
LogicalResult verifySizesForMultiAffineOps(Operation *op, int32_t segmentSize, ArrayRef< int32_t > mapOpGroupSizes, OperandRangeRange mapOperands, ArrayRef< int32_t > numDimsPerMap)
ParseResult parseMultiDimAndSymbolList(OpAsmParser &parser, SmallVectorImpl< SmallVector< OpAsmParser::UnresolvedOperand > > &multiMapOperands, DenseI32ArrayAttr &numDimsPerMap)
ParseResult parseDimAndSymbolList(OpAsmParser &parser, SmallVectorImpl< OpAsmParser::UnresolvedOperand > &mapOperands, IntegerAttr &numDims)
ParseResult parseAttrDictWithWarnings(OpAsmParser &parser, NamedAttrList &extraAttrs, OperationState &state)
void printMultiDimAndSymbolList(OpAsmPrinter &printer, Operation *, OperandRangeRange multiMapOperands, DenseI32ArrayAttr numDimsPerMap)
void printDimAndSymbolList(OpAsmPrinter &printer, Operation *, OperandRange mapOperands, IntegerAttr numDims)
void printAttrDictWithWarnings(mlir::OpAsmPrinter &printer, ConcreteOp, mlir::DictionaryAttr extraAttrs, typename ConcreteOp::Properties)
FailureOr< Attribute > forceIntAttrType(Attribute attr, EmitErrorFn emitError)
void printTemplateParams(mlir::AsmPrinter &printer, mlir::ArrayAttr value)
Definition OpHelpers.h:238
void printDimAndSymbolList(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRange mapOperands, mlir::IntegerAttr numDims)
Definition OpHelpers.h:169
void printAttrs(AsmPrinter &printer, ArrayRef< Attribute > attrs, const StringRef &separator)
OpClass getParentOfType(mlir::Operation *op)
Return the closest surrounding parent/ancestor operation that is of type 'OpClass'.
Definition OpHelpers.h:53
mlir::ParseResult parseAttrDictWithWarnings(mlir::OpAsmParser &parser, mlir::NamedAttrList &extraAttrs, mlir::OperationState &state)
Definition OpHelpers.h:191
void printMultiDimAndSymbolList(mlir::OpAsmPrinter &printer, mlir::Operation *op, mlir::OperandRangeRange multiMapOperands, mlir::DenseI32ArrayAttr numDimsPerMap)
Definition OpHelpers.h:184
void printAttrDictWithWarnings(mlir::OpAsmPrinter &printer, ConcreteOp op, mlir::DictionaryAttr extraAttrs, typename mlir::PropertiesSelector< ConcreteOp >::type state)
Definition OpHelpers.h:198
mlir::ParseResult parseTemplateParams(mlir::AsmParser &parser, mlir::ArrayAttr &value)
Definition OpHelpers.h:205
mlir::ParseResult parseMultiDimAndSymbolList(mlir::OpAsmParser &parser, mlir::SmallVector< mlir::SmallVector< mlir::OpAsmParser::UnresolvedOperand > > &multiMapOperands, mlir::DenseI32ArrayAttr &numDimsPerMap)
Definition OpHelpers.h:176
llvm::StringLiteral getOperationName()
Get the operation name, like "constrain.eq" for the given OpClass.
Definition OpHelpers.h:34
bool hasParentThatIsa(mlir::Operation *op)
Return true if the parameter has a parent/ancestor op that is an instance of one of the template type...
Definition OpHelpers.h:64
OpClass getSelfOrParentOfType(mlir::Operation *op)
Return the closest surrounding parent/ancestor operation that is of type 'OpClass',...
Definition OpHelpers.h:40
mlir::ParseResult parseDimAndSymbolList(mlir::OpAsmParser &parser, mlir::SmallVector< mlir::OpAsmParser::UnresolvedOperand, N > &mapOperands, mlir::IntegerAttr &numDims)
Definition OpHelpers.h:161
static mlir::LogicalResult verifyRegionTrait(mlir::Operation *op)
Definition OpHelpers.h:99
See HasAncestor ODS documentation for details.
Definition OpHelpers.h:94
Produces errors if there is an inconsistency in the various attributes/values that are used to suppor...
Definition OpHelpers.h:124