LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
Dialect.cpp
Go to the documentation of this file.
1//===-- Dialect.cpp - Felt dialect implementation ---------------*- 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
11
18#include "llzk/Util/Field.h"
19
20#include <mlir/IR/DialectImplementation.h>
21
22#include <llvm/ADT/TypeSwitch.h>
23
24#include <limits>
25
26// TableGen'd implementation files
28
29#define GET_TYPEDEF_CLASSES
31#define GET_ATTRDEF_CLASSES
33
34using namespace mlir;
35
36namespace {
37
39enum class FeltAttrEncoding : uint8_t {
40 FeltConst = 0,
41 FieldSpec = 1,
42};
43
44struct FeltDialectBytecodeInterface
45 : public llzk::LLZKDialectBytecodeInterface<llzk::felt::FeltDialect> {
46 using llzk::LLZKDialectBytecodeInterface<llzk::felt::FeltDialect>::LLZKDialectBytecodeInterface;
47
48 Attribute readAttribute(DialectBytecodeReader &reader) const final {
49 uint64_t encoding;
50 if (failed(reader.readVarInt(encoding))) {
51 return {};
52 }
53 if (encoding > std::numeric_limits<uint8_t>::max()) {
54 reader.emitError() << "unknown felt attribute encoding: " << encoding;
55 return {};
56 }
57
58 switch (static_cast<FeltAttrEncoding>(encoding)) {
59 case FeltAttrEncoding::FeltConst: {
60 FailureOr<APInt> value = llzk::readAPInt(reader);
62 if (failed(value) || failed(reader.readType(type))) {
63 return {};
64 }
65 return llzk::felt::FeltConstAttr::get(getContext(), *value, type);
66 }
67 case FeltAttrEncoding::FieldSpec: {
68 StringAttr fieldName;
69 if (failed(reader.readAttribute(fieldName))) {
70 return {};
71 }
72 FailureOr<APInt> prime = llzk::readAPInt(reader);
73 if (failed(prime)) {
74 return {};
75 }
76
77 llzk::Field::addField(fieldName.getValue(), *prime, [&reader]() {
78 return llzk::InFlightDiagnosticWrapper(reader.emitError());
79 });
80 return llzk::felt::FieldSpecAttr::get(getContext(), fieldName, *prime);
81 }
82 }
83
84 reader.emitError() << "unknown felt attribute encoding: " << encoding;
85 return {};
86 }
87
88 LogicalResult writeAttribute(Attribute attr, DialectBytecodeWriter &writer) const final {
89 if (auto feltConst = dyn_cast<llzk::felt::FeltConstAttr>(attr)) {
90 writer.writeVarInt(static_cast<uint64_t>(FeltAttrEncoding::FeltConst));
91 llzk::writeAPInt(writer, feltConst.getValue());
92 writer.writeType(feltConst.getType());
93 return success();
94 }
95 if (auto fieldSpec = dyn_cast<llzk::felt::FieldSpecAttr>(attr)) {
96 writer.writeVarInt(static_cast<uint64_t>(FeltAttrEncoding::FieldSpec));
97 writer.writeAttribute(fieldSpec.getFieldName());
98 llzk::writeAPInt(writer, fieldSpec.getPrime());
99 return success();
100 }
101 return failure();
102 }
103};
104
105} // namespace
106
107namespace llzk::felt {
108
109//===------------------------------------------------------------------===//
110// FieldSpecAttr
111//
112// Custom parse/print needs to be here where Attrs.cpp.inc is included, and
113// it doesn't work to put it in Attrs.cpp.
114//===------------------------------------------------------------------===//
115
116Attribute FieldSpecAttr::parse(AsmParser &odsParser, Type) {
117 Builder odsBuilder(odsParser.getContext());
118 llvm::SMLoc odsLoc = odsParser.getCurrentLocation();
119 FailureOr<StringAttr> fieldNameAttrRes;
120 FailureOr<llvm::APInt> primeRes;
121
122 // Parse literal '<'
123 if (odsParser.parseLess()) {
124 return {};
125 }
126
127 // Parse variable 'fieldName'
128 fieldNameAttrRes = FieldParser<StringAttr>::parse(odsParser);
129 if (failed(fieldNameAttrRes)) {
130 odsParser.emitError(
131 odsParser.getCurrentLocation(), "failed to parse LLZK_FieldSpecAttr parameter 'fieldName' "
132 "which is to be a `StringAttr`"
133 );
134 return {};
135 }
136 // Parse literal ','
137 if (odsParser.parseComma()) {
138 return {};
139 }
140
141 // Parse variable 'prime'
142 primeRes = FieldParser<llvm::APInt>::parse(odsParser);
143 if (failed(primeRes)) {
144 odsParser.emitError(
145 odsParser.getCurrentLocation(),
146 "failed to parse LLZK_FieldSpecAttr parameter 'prime' which is to be a `llvm::APInt`"
147 );
148 return {};
149 }
150 // Parse literal '>'
151 if (odsParser.parseGreater()) {
152 return {};
153 }
154 assert(succeeded(fieldNameAttrRes));
155 assert(succeeded(primeRes));
156
157 // Custom logic: cache the field, reporting an error if there's a conflict
158 auto errFn = [&odsParser]() {
159 return InFlightDiagnosticWrapper(odsParser.emitError(odsParser.getCurrentLocation()));
160 };
161 Field::addField(fieldNameAttrRes.value(), primeRes.value(), errFn);
162
163 return odsParser.getChecked<FieldSpecAttr>(
164 odsLoc, odsParser.getContext(), StringAttr(*fieldNameAttrRes), llvm::APInt(*primeRes)
165 );
166}
167
168void FieldSpecAttr::print(AsmPrinter &odsPrinter) const {
169 Builder odsBuilder(getContext());
170 odsPrinter << "<";
171 odsPrinter.printStrippedAttrOrType(getFieldName());
172 odsPrinter << ", ";
173 odsPrinter.printStrippedAttrOrType(getPrime());
174 odsPrinter << '>';
175}
176
177//===------------------------------------------------------------------===//
178// FeltConstAttr
179//===------------------------------------------------------------------===//
180
181Attribute FeltConstAttr::parse(AsmParser &odsParser, Type) {
182 SMLoc odsLoc = odsParser.getCurrentLocation();
183
184 // Parse the APInt value.
185 FailureOr<APInt> valueRes = FieldParser<APInt>::parse(odsParser);
186 if (failed(valueRes)) {
187 odsParser.emitError(
188 odsParser.getCurrentLocation(),
189 "failed to parse LLZK_FeltConstAttr parameter 'value' which is to be a `::llvm::APInt`"
190 );
191 return {};
192 }
193
194 FeltType type = FeltType::get(odsParser.getContext());
195
196 // v2 syntax: VALUE : !felt.type<"fieldName">
197 if (odsParser.parseOptionalColon().succeeded()) {
198 FailureOr<FeltType> typeRes = FieldParser<FeltType>::parse(odsParser);
199 if (failed(typeRes)) {
200 odsParser.emitError(
201 odsParser.getCurrentLocation(),
202 "failed to parse LLZK_FeltConstAttr parameter 'type' which is to be a `FeltType`"
203 );
204 return {};
205 }
206 type = *typeRes;
207 }
208 // v1 compat syntax: VALUE <"fieldName">
209 else if (odsParser.parseOptionalLess().succeeded()) {
210 FailureOr<StringAttr> fieldNameRes = FieldParser<StringAttr>::parse(odsParser);
211 if (failed(fieldNameRes)) {
212 odsParser.emitError(
213 odsParser.getCurrentLocation(), "failed to parse LLZK_FeltConstAttr(version 1) field "
214 "name parameter which is to be a `StringAttr`"
215 );
216 return {};
217 }
218 if (odsParser.parseGreater()) {
219 return {};
220 }
221 type = FeltType::get(odsParser.getContext(), (*fieldNameRes).getValue());
222 }
223
224 return odsParser.getChecked<FeltConstAttr>(odsLoc, odsParser.getContext(), *valueRes, type);
225}
226
227// Same as tablegen would generate to serialize version 2 IR.
228void FeltConstAttr::print(AsmPrinter &odsPrinter) const {
229 odsPrinter << ' ';
230 odsPrinter.printStrippedAttrOrType(getValue());
231 if (getType() != FeltType::get(getContext())) {
232 odsPrinter << " : ";
233 odsPrinter.printStrippedAttrOrType(getType());
234 }
235}
236
237//===------------------------------------------------------------------===//
238// FeltType
239//===------------------------------------------------------------------===//
240
241const Field &FeltType::getField() const { return Field::getField(getFieldName().getValue()); }
242
243llvm::LogicalResult
244FeltType::verify(llvm::function_ref<InFlightDiagnostic()> errFn, StringAttr fieldName) {
245 return fieldName ? Field::verifyFieldDefined(
246 fieldName.getValue(), wrapNonNullableInFlightDiagnostic(errFn)
247 )
248 : success();
249}
250
251//===------------------------------------------------------------------===//
252// FeltDialect
253//===------------------------------------------------------------------===//
254
255Operation *
256FeltDialect::materializeConstant(OpBuilder &builder, Attribute value, Type, Location loc) {
257 if (auto attr = llvm::dyn_cast<FeltConstAttr>(value)) {
258 return builder.create<FeltConstantOp>(loc, attr);
259 }
260 return nullptr;
261}
262
263auto FeltDialect::initialize() -> void {
264 // clang-format off
265 addOperations<
266 #define GET_OP_LIST
268 >();
269
270 // Suppress false positive from `clang-tidy`
271 // NOLINTNEXTLINE(clang-analyzer-core.StackAddressEscape)
272 addTypes<
273 #define GET_TYPEDEF_LIST
275 >();
276
277 // Suppress false positive from `clang-tidy`
278 // NOLINTNEXTLINE(clang-analyzer-core.StackAddressEscape)
279 addAttributes<
280 #define GET_ATTRDEF_LIST
282 >();
283 // clang-format on
284 addInterfaces<FeltDialectBytecodeInterface>();
285}
286
287} // namespace llzk::felt
Information about the prime finite field used for the interval analysis.
Definition Field.h:36
static llvm::LogicalResult verifyFieldDefined(llvm::StringRef fieldName, EmitErrorFn errFn)
Search for a field with the given name, reporting an error if the field is not found.
Definition Field.cpp:67
static const Field & getField(llvm::StringRef fieldName, EmitErrorFn errFn)
Get a Field from a given field name string.
static void addField(llvm::StringRef fieldName, const llvm::APInt &prime, EmitErrorFn errFn)
Add a new field to the set of available prime fields.
Definition Field.h:41
::mlir::Operation * materializeConstant(::mlir::OpBuilder &builder, ::mlir::Attribute value, ::mlir::Type type, ::mlir::Location loc) override
Materialize a single constant operation from a given attribute value with the desired resultant type.
Definition Dialect.cpp:256
::llvm::LogicalResult verify(::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError, ::mlir::StringAttr fieldName)
Definition Dialect.cpp:244
const ::llzk::Field & getField() const
Definition Dialect.cpp:241
::mlir::StringAttr getFieldName() const
static FeltType get(::mlir::MLIRContext *context, ::mlir::StringAttr fieldName)
Definition Types.cpp.inc:67
void writeAPInt(mlir::DialectBytecodeWriter &writer, const llvm::APInt &value)
Write an APInt with its bit width, so the bytecode reader can use MLIR's native APInt payload encodin...
Definition Versioning.h:44
mlir::FailureOr< llvm::APInt > readAPInt(mlir::DialectBytecodeReader &reader)
Read an APInt written by writeAPInt.
Definition Versioning.h:50
OwningEmitErrorFn wrapNonNullableInFlightDiagnostic(llvm::function_ref< mlir::InFlightDiagnostic()> emitError)
This implements the bytecode interface for the LLZK dialect.
Definition Versioning.h:63
static mlir::FailureOr< llvm::APInt > parse(mlir::AsmParser &parser)