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 = 2,
41 FieldSpec = 3,
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<llvm::DynamicAPInt> value = llzk::readDynamicAPInt(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<llvm::DynamicAPInt> prime = llzk::readDynamicAPInt(reader);
73 if (failed(prime)) {
74 return {};
75 }
76
77 // Create the FieldSpecAttr with verification.
78 auto fieldSpec = llzk::felt::FieldSpecAttr::getChecked([&reader]() {
79 return reader.emitError();
80 }, getContext(), fieldName, *prime);
81
82 // Cache the verified field, reporting an error if there's a conflict.
83 if (fieldSpec) {
84 llzk::Field::addField(fieldName.getValue(), *prime, [&reader]() {
85 return llzk::InFlightDiagnosticWrapper(reader.emitError());
86 });
87 }
88
89 return fieldSpec;
90 }
91 }
92
93 reader.emitError("unknown felt attribute encoding: ") << encoding;
94 return {};
95 }
96
97 LogicalResult writeAttribute(Attribute attr, DialectBytecodeWriter &writer) const final {
98 if (auto feltConst = dyn_cast<llzk::felt::FeltConstAttr>(attr)) {
99 writer.writeVarInt(static_cast<uint64_t>(FeltAttrEncoding::FeltConst));
100 llzk::writeDynamicAPInt(writer, feltConst.getRawValue());
101 writer.writeType(feltConst.getType());
102 return success();
103 }
104 if (auto fieldSpec = dyn_cast<llzk::felt::FieldSpecAttr>(attr)) {
105 writer.writeVarInt(static_cast<uint64_t>(FeltAttrEncoding::FieldSpec));
106 writer.writeAttribute(fieldSpec.getFieldName());
107 llzk::writeDynamicAPInt(writer, fieldSpec.getPrime());
108 return success();
109 }
110 return failure();
111 }
112};
113
114} // namespace
115
116namespace llzk::felt {
117
118//===------------------------------------------------------------------===//
119// FieldSpecAttr
120//
121// Custom parse/print needs to be here where Attrs.cpp.inc is included, and
122// it doesn't work to put it in Attrs.cpp.
123//===------------------------------------------------------------------===//
124
125Attribute FieldSpecAttr::parse(AsmParser &odsParser, Type) {
126 Builder odsBuilder(odsParser.getContext());
127 llvm::SMLoc odsLoc = odsParser.getCurrentLocation();
128 FailureOr<StringAttr> fieldNameAttrRes;
129 FailureOr<llzk::DynamicAPIntValue> primeRes;
130
131 // Parse literal '<'
132 if (odsParser.parseLess()) {
133 return {};
134 }
135
136 // Parse variable 'fieldName'
137 fieldNameAttrRes = FieldParser<StringAttr>::parse(odsParser);
138 if (failed(fieldNameAttrRes)) {
139 odsParser.emitError(
140 odsParser.getCurrentLocation(), "failed to parse LLZK_FieldSpecAttr parameter 'fieldName' "
141 "which is to be a `StringAttr`"
142 );
143 return {};
144 }
145 // Parse literal ','
146 if (odsParser.parseComma()) {
147 return {};
148 }
149
150 // Parse variable 'prime'
151 primeRes = llzk::parseDynamicAPIntValue(odsParser);
152 if (failed(primeRes)) {
153 odsParser.emitError(
154 odsParser.getCurrentLocation(),
155 "failed to parse LLZK_FieldSpecAttr parameter 'prime' which is to be an integer"
156 );
157 return {};
158 }
159 // Parse literal '>'
160 if (odsParser.parseGreater()) {
161 return {};
162 }
163 assert(succeeded(fieldNameAttrRes));
164 assert(succeeded(primeRes));
165
166 // Create the FieldSpecAttr with verification.
167 const llvm::DynamicAPInt &prime = *primeRes;
168 auto fieldSpec =
169 odsParser.getChecked<FieldSpecAttr>(odsLoc, odsParser.getContext(), *fieldNameAttrRes, prime);
170
171 // Cache the verified field, reporting an error if there's a conflict.
172 if (fieldSpec) {
173 Field::addField(fieldNameAttrRes.value(), prime, [&odsParser]() {
174 return InFlightDiagnosticWrapper(odsParser.emitError(odsParser.getCurrentLocation()));
175 });
176 }
177
178 return fieldSpec;
179}
180
181void FieldSpecAttr::print(AsmPrinter &odsPrinter) const {
182 Builder odsBuilder(getContext());
183 odsPrinter << "<";
184 odsPrinter.printStrippedAttrOrType(getFieldName());
185 odsPrinter << ", ";
186 odsPrinter.printStrippedAttrOrType(getPrime());
187 odsPrinter << '>';
188}
189
190//===------------------------------------------------------------------===//
191// FeltConstAttr
192//===------------------------------------------------------------------===//
193
194Attribute FeltConstAttr::parse(AsmParser &odsParser, Type) {
195 SMLoc odsLoc = odsParser.getCurrentLocation();
196
197 // Parse the signed mathematical literal.
198 auto valueRes = llzk::parseDynamicAPIntValue(odsParser);
199 if (failed(valueRes)) {
200 odsParser.emitError(
201 odsParser.getCurrentLocation(),
202 "failed to parse LLZK_FeltConstAttr parameter 'rawValue' which is to be an integer"
203 );
204 return {};
205 }
206
207 FeltType type = FeltType::get(odsParser.getContext());
208
209 // v2 syntax: VALUE : !felt.type<"fieldName">
210 if (odsParser.parseOptionalColon().succeeded()) {
211 FailureOr<FeltType> typeRes = FieldParser<FeltType>::parse(odsParser);
212 if (failed(typeRes)) {
213 odsParser.emitError(
214 odsParser.getCurrentLocation(),
215 "failed to parse LLZK_FeltConstAttr parameter 'type' which is to be a `FeltType`"
216 );
217 return {};
218 }
219 type = *typeRes;
220 }
221 // v1 compat syntax: VALUE <"fieldName">
222 else if (odsParser.parseOptionalLess().succeeded()) {
223 FailureOr<StringAttr> fieldNameRes = FieldParser<StringAttr>::parse(odsParser);
224 if (failed(fieldNameRes)) {
225 odsParser.emitError(
226 odsParser.getCurrentLocation(), "failed to parse LLZK_FeltConstAttr(version 1) field "
227 "name parameter which is to be a `StringAttr`"
228 );
229 return {};
230 }
231 if (odsParser.parseGreater()) {
232 return {};
233 }
234 type = FeltType::get(odsParser.getContext(), (*fieldNameRes).getValue());
235 }
236
237 return odsParser.getChecked<FeltConstAttr>(odsLoc, odsParser.getContext(), *valueRes, type);
238}
239
240// Same as tablegen would generate to serialize version 2 IR.
241void FeltConstAttr::print(AsmPrinter &odsPrinter) const {
242 odsPrinter << ' ';
243 odsPrinter.printStrippedAttrOrType(getRawValue());
244 FeltType ty = getType();
245 if (ty != FeltType::get(getContext())) {
246 odsPrinter << " : ";
247 odsPrinter.printStrippedAttrOrType(ty);
248 }
249}
250
251//===------------------------------------------------------------------===//
252// FeltType
253//===------------------------------------------------------------------===//
254
255const Field &FeltType::getField() const { return Field::getField(getFieldName().getValue()); }
256
257llvm::LogicalResult
258FeltType::verify(llvm::function_ref<InFlightDiagnostic()> errFn, StringAttr fieldName) {
259 return fieldName ? Field::verifyFieldDefined(
260 fieldName.getValue(), wrapNonNullableInFlightDiagnostic(errFn)
261 )
262 : success();
263}
264
265//===------------------------------------------------------------------===//
266// FeltDialect
267//===------------------------------------------------------------------===//
268
269Operation *
270FeltDialect::materializeConstant(OpBuilder &builder, Attribute value, Type, Location loc) {
271 if (auto attr = llvm::dyn_cast<FeltConstAttr>(value)) {
272 return FeltConstantOp::create(builder, loc, attr);
273 }
274 return nullptr;
275}
276
277auto FeltDialect::initialize() -> void {
278 // clang-format off
279 addOperations<
280 #define GET_OP_LIST
282 >();
283
284 addTypes<
285 #define GET_TYPEDEF_LIST
287 >();
288
289 addAttributes<
290 #define GET_ATTRDEF_LIST
292 >();
293 // clang-format on
294 addInterfaces<FeltDialectBytecodeInterface>();
295}
296
297} // namespace llzk::felt
Information about the prime finite field used for the interval analysis.
Definition Field.h:36
static void addField(llvm::StringRef fieldName, const llvm::DynamicAPInt &prime, EmitErrorFn errFn)
Add a new field to the set of available prime fields.
Definition Field.h:41
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:65
static const Field & getField(llvm::StringRef fieldName, EmitErrorFn errFn)
Get a Field from a given field name string.
static FeltConstantOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::llzk::felt::FeltConstAttr value)
Definition Ops.cpp.inc:1274
LLZK felt (Field ELemenT) type and operations.
Definition Dialect.h.inc:13
::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:270
::llvm::LogicalResult verify(::llvm::function_ref<::mlir::InFlightDiagnostic()> emitError, ::mlir::StringAttr fieldName)
Definition Dialect.cpp:258
const ::llzk::Field & getField() const
Definition Dialect.cpp:255
::mlir::StringAttr getFieldName() const
static FeltType get(::mlir::MLIRContext *context, ::mlir::StringAttr fieldName)
Definition Types.cpp.inc:69
mlir::FailureOr< DynamicAPIntValue > parseDynamicAPIntValue(mlir::AsmParser &parser)
Parse MLIR's signed integer literal and discard its encoding width.
mlir::FailureOr< llvm::DynamicAPInt > readDynamicAPInt(mlir::DialectBytecodeReader &reader)
Decode a signed integer, rejecting invalid widths before allocating storage.
Definition Versioning.h:52
OwningEmitErrorFn wrapNonNullableInFlightDiagnostic(llvm::function_ref< mlir::InFlightDiagnostic()> emitError)
void writeDynamicAPInt(mlir::DialectBytecodeWriter &writer, const llvm::DynamicAPInt &value)
Write the minimal signed two's-complement encoding of a mathematical integer.
Definition Versioning.h:44
This implements the bytecode interface for the LLZK dialect.
Definition Versioning.h:69