23#include <mlir/IR/Attributes.h>
24#include <mlir/IR/BuiltinAttributes.h>
25#include <mlir/IR/Operation.h>
27#include <llvm/ADT/APSInt.h>
28#include <llvm/ADT/SlowDynamicAPInt.h>
29#include <llvm/ADT/Twine.h>
30#include <llvm/ADT/TypeSwitch.h>
31#include <llvm/Support/LogicalResult.h>
43static DenseMap<StringRef, Field> &getKnownFields() {
44 static DenseMap<StringRef, Field> knownFields;
50Field::Field(
const llvm::DynamicAPInt &prime, StringRef name)
51 : primeName(name), primeMod(prime), halfPrime((primeMod + 1) / 2),
52 bitwidth(std::max(1U,
toAPSInt(prime).getActiveBits())) {}
55 static std::once_flag fieldsInit;
56 std::call_once(fieldsInit, initKnownFields);
58 auto &knownFields = getKnownFields();
59 if (
auto it = knownFields.find(fieldName); it != knownFields.end()) {
67 return errFn().append(
"field '", fieldName,
"' is not defined");
73 auto res = tryGetField(fieldName);
75 return res.value().get();
77 std::string msg =
"field \"" + fieldName.str() +
"\" is unsupported";
79 errFn().append(msg).report();
81 llvm::report_fatal_error(msg.c_str());
84void Field::addField(StringRef fieldName, StringRef primeStr, EmitErrorFn errFn) {
87 std::string message = llvm::toString(prime.takeError());
89 errFn().append(message).report();
91 llvm::report_fatal_error(message.c_str());
95 addField(fieldName, *prime, errFn);
98void Field::addField(Field &&f, EmitErrorFn errFn) {
101 errFn().append(
"field modulus must be at least 2").report();
103 llvm::report_fatal_error(
"field modulus must be at least 2");
108 auto existing = Field::tryGetField(f.name());
109 if (succeeded(existing)) {
112 debug::Appender(msg) <<
"Definition of \"" << f.name()
113 <<
"\" conflicts with prior definition: prior="
114 << existing.value().get().prime() <<
", new=" << f.prime();
116 errFn().append(msg).report();
118 llvm::report_fatal_error(msg.c_str());
123 getKnownFields().try_emplace(f.name(), f);
126void Field::initKnownFields() {
127 static constexpr const char BN128[] =
"bn128", BN254[] =
"bn254", GRUMPKIN[] =
"grumpkin",
128 BABYBEAR[] =
"babybear", GOLDILOCKS[] =
"goldilocks",
129 MERSENNE31[] =
"mersenne31", KOALABEAR[] =
"koalabear";
131 auto insert = [](
const char *name,
const char *primeStr) {
132 getKnownFields().try_emplace(name, Field(primeStr, name));
137 insert(BN128,
"21888242871839275222246405745257275088548364400416034343698204186575808495617");
138 insert(BN254,
"21888242871839275222246405745257275088548364400416034343698204186575808495617");
140 insert(GRUMPKIN,
"21888242871839275222246405745257275088696311157297823662689037894645226208583");
142 insert(BABYBEAR,
"2013265921");
144 insert(GOLDILOCKS,
"18446744069414584321");
146 insert(MERSENNE31,
"2147483647");
148 insert(KOALABEAR,
"2130706433");
151DynamicAPInt Field::reduce(
const DynamicAPInt &i)
const {
152 DynamicAPInt m = i % prime();
164 return IntegerAttr::get(
170static LogicalResult parseFields(Attribute a) {
173 Attribute, FailureOr<SmallVector<std::reference_wrapper<const Field>>>>(a)
179 [](
auto s) -> FailureOr<SmallVector<std::reference_wrapper<const Field>>> {
180 auto fieldRes = Field::tryGetField(s);
181 if (failed(fieldRes)) {
184 return SmallVector<std::reference_wrapper<const Field>> {fieldRes.value()};
187 [](
auto arr) -> FailureOr<SmallVector<std::reference_wrapper<const Field>>> {
189 SmallVector<std::reference_wrapper<const Field>> res;
190 for (Attribute elem : arr) {
191 if (
auto s = llvm::dyn_cast<StringAttr>(elem)) {
192 auto fieldRes = Field::tryGetField(s);
193 if (failed(fieldRes)) {
196 res.push_back(fieldRes.value());
203 .Default([](
auto) {
return failure(); });
209 return parseFields(a);
212 if (ModuleOp parentMod = modOp->getParentOfType<ModuleOp>()) {
224 LogicalResult &status;
230static void handleAttribute(Attribute, FieldsCtx &);
232static void handleType(Type type, FieldsCtx &ctx) {
233 TypeSwitch<Type> ts(type);
238 ctx.status = failure();
240 ctx.scope->emitWarning(
"felt type is unspecified, which may cause some passes to fail");
247 handleAttribute(record, ctx);
250 ts.Case([&ctx](FunctionType funcType) {
251 for (
auto i : funcType.getInputs()) {
254 for (
auto o : funcType.getResults()) {
259 ts.Default([](
auto) {});
262static void handleAttribute(Attribute attr, FieldsCtx &ctx) {
263 TypeSwitch<Attribute> ts(attr);
264 ts.Case([&ctx](TypeAttr typeAttr) { handleType(typeAttr.getValue(), ctx); });
265 ts.Case([&ctx](ArrayAttr arrayAttr) {
266 for (
auto a : arrayAttr) {
267 handleAttribute(a, ctx);
270 ts.Case([&ctx](DictionaryAttr dictAttr) {
271 for (
auto a : dictAttr.getValue()) {
272 handleAttribute(a.getValue(), ctx);
275 ts.Case([&ctx](llzk::pod::RecordAttr recordAttr) { handleType(recordAttr.getType(), ctx); });
276 ts.Default([](
auto) {});
283 LogicalResult status = success();
284 root->walk([&fields, &status, silent](Operation *op) {
285 FieldsCtx ctx = {.fields = fields, .status = status, .scope = silent ? nullptr : op};
287 for (
auto result : op->getOpResults()) {
288 handleType(result.getType(), ctx);
291 for (
auto attr : op->getAttrs()) {
292 handleAttribute(attr.getValue(), ctx);
295 for (
auto ®ion : op->getRegions()) {
296 for (
auto &block : region) {
297 for (
auto &arg : block.getArguments()) {
298 handleType(arg.getType(), ctx);
307std::optional<std::reference_wrapper<const llzk::Field>>
313 ModuleOp modOp = dyn_cast<ModuleOp>(root);
315 modOp = root->getParentOfType<ModuleOp>();
323 if (failed(
collectFields(modOp, fields)) || fields.size() != 1) {
326 return *fields.begin();
This file implements helpers for DynamicAPInt operations and conversions that LLVM does not provide.
Information about the prime finite field used for the interval analysis.
llvm::DynamicAPInt half() const
Returns ceil(p / 2), the first negative signed representative.
mlir::IntegerAttr getPrimeAttr(mlir::MLIRContext *context, unsigned bitWidth) const
Return the field prime modulus materialized as an integer attribute at bitWidth.
static llvm::FailureOr< std::reference_wrapper< const Field > > tryGetField(llvm::StringRef fieldName)
Get a Field from a given field name string, or failure if the field is not defined.
llvm::DynamicAPInt toSigned(const llvm::DynamicAPInt &i) const
Converts a canonical field element to its signed integer representation: toSigned(f) = f if f < field...
llvm::DynamicAPInt prime() const
For the prime field p, returns p.
unsigned bitWidth() const
Width sufficient for the modulus and its representatives, independent of literal storage.
llvm::DynamicAPInt inv(const llvm::DynamicAPInt &i) const
Returns the multiplicative inverse of i in prime field p.
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.
static const Field & getField(llvm::StringRef fieldName, EmitErrorFn errFn)
Get a Field from a given field name string.
::mlir::Type getElementType() const
const ::llzk::Field & getField() const
::llvm::ArrayRef<::llzk::pod::RecordAttr > getRecords() const
llvm::function_ref< InFlightDiagnosticWrapper()> EmitErrorFn
Callback to produce an error diagnostic.
DynamicAPInt toDynamicAPInt(StringRef str)
Expected< DynamicAPInt > parseDynamicAPInt(StringRef str)
LogicalResult addSpecifiedFields(ModuleOp modOp)
DynamicAPInt modInversePrime(const DynamicAPInt &f, const DynamicAPInt &p)
llvm::SmallSet< FieldRef, 2 > FieldSet
Typealias for a set of Fields.
APSInt toAPSInt(const DynamicAPInt &i)
std::optional< std::reference_wrapper< const Field > > tryDetectSpecifiedField(mlir::Operation *root)
Try to detect a uniquely used field from the enclosing LLZK module.
constexpr char FIELD_ATTR_NAME[]
Name of the attribute on the top-level ModuleOp that defines prime fields used in the circuit.
mlir::LogicalResult collectFields(mlir::Operation *root, FieldSet &fields, bool silent=true)
Collects all the fields used in a circuit.
Expected< APInt > checkedToAPInt(const DynamicAPInt &value, unsigned bitWidth, bool isSigned)