LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
Ops.cpp
Go to the documentation of this file.
1//===-- Ops.cpp - Cast operation implementations ----------------*- 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
19#include "llzk/Util/Field.h"
20
21#include <mlir/Support/LLVM.h>
22
23#include <llvm/ADT/STLExtras.h>
24
26static bool canCastAssertFail(mlir::Type sourceType, llzk::cast::OverflowSemantics overflow) {
27 if (overflow != llzk::cast::OverflowSemantics::ASSERT || sourceType.isInteger(1)) {
28 return false;
29 }
30 if (auto feltType = llvm::dyn_cast<llzk::felt::FeltType>(sourceType);
31 feltType && feltType.hasField()) {
32 // Every canonical source value fits if the field maximum fits the index safe range.
34 feltType.getField().maxVal(), mlir::IndexType::kInternalStorageBitWidth, overflow
35 );
36 }
37 return true;
38}
39
40// TableGen'd implementation files
41#define GET_OP_CLASSES
43using namespace mlir;
44
45static inline ParseResult
46parseOptionalOverflowSemantics(OpAsmParser &parser, llzk::cast::OverflowSemanticsAttr &overflow) {
47 StringRef keyword;
48 if (failed(parser.parseOptionalKeyword(&keyword))) {
49 return success();
50 }
51
52 std::optional<llzk::cast::OverflowSemantics> semantics =
54 if (!semantics) {
55 return parser.emitError(parser.getCurrentLocation()) << "expected overflow semantics keyword";
56 }
57
58 overflow = llzk::cast::OverflowSemanticsAttr::get(parser.getContext(), *semantics);
59 return success();
60}
61
62static inline void
63printOptionalOverflowSemantics(OpAsmPrinter &printer, llzk::cast::OverflowSemanticsAttr overflow) {
64 if (!overflow || overflow.getValue() == llzk::cast::OverflowSemantics::ASSERT) {
65 return;
66 }
67
68 printer << ' ' << stringifyOverflowSemantics(overflow.getValue());
69}
70
71namespace llzk::cast {
72
73bool IntToFeltOp::isCompatibleReturnTypes(::mlir::TypeRange lhs, ::mlir::TypeRange rhs) {
74 return lhs.size() == rhs.size() && llvm::all_of(llvm::zip_equal(lhs, rhs), [](auto pair) {
75 auto [lhsType, rhsType] = pair;
76 auto lhsFeltType = llvm::dyn_cast<llzk::felt::FeltType>(lhsType);
77 auto rhsFeltType = llvm::dyn_cast<llzk::felt::FeltType>(rhsType);
78
79 // If both types are felts but NOT structurally equal then check if the types are valid
80 // with the additional consideration that lhs is allowed to NOT have
81 // a declared field.
82 if (lhsFeltType && rhsFeltType && lhsFeltType != rhsFeltType) {
83 // If we reached this point we know that the felts are not equal and that only the lhs is
84 // allowed to not have a declared field. Thus, rhs must have a declared field. If lhs has a
85 // declared field, then, since they are not structurally equal, it must be a different field
86 // than rhs. With all that, the types are compatible if lhs does not have a field, so we can
87 // simply return that.
88 return !lhsFeltType.hasField();
89 }
90
91 // Any other case gets handled by standard equality.
92 return lhsType == rhsType;
93 });
94}
95
96OpFoldResult IntToFeltOp::fold(FoldAdaptor adaptor) {
97 auto value = llvm::dyn_cast_if_present<IntegerAttr>(adaptor.getValue());
98 felt::FeltType resultType = getType();
99 // A fieldless felt defers its field selection and overflow behavior.
100 if (!value || !resultType.hasField()) {
101 return {};
102 }
103
104 const Field &field = resultType.getField();
105 DynamicAPInt signedValue = integerAttrToDynamicAPInt(value);
106 auto result = applyIntToFeltOverflow(signedValue, field, getOverflow());
107 if (!result) {
108 return {};
109 }
110 return felt::FeltConstAttr::get(getContext(), *result, resultType);
111}
112
113ParseResult
114IntToFeltOp::parseOptionalOverflowSemantics(OpAsmParser &parser, OverflowSemanticsAttr &overflow) {
115 return ::parseOptionalOverflowSemantics(parser, overflow);
116}
117
119 OpAsmPrinter &printer, IntToFeltOp /*op*/, OverflowSemanticsAttr overflow
120) {
121 ::printOptionalOverflowSemantics(printer, overflow);
122}
123
124OpFoldResult FeltToIndexOp::fold(FoldAdaptor adaptor) {
125 auto value = llvm::dyn_cast_if_present<felt::FeltConstAttr>(adaptor.getValue());
126 if (!value) {
127 return {};
128 }
129 // A fieldless felt has no representative until its field is known.
130 auto reduced = value.getReducedValue();
131 if (!reduced) {
132 return {};
133 }
134 auto result =
135 applyFeltToIndexOverflow(*reduced, IndexType::kInternalStorageBitWidth, getOverflow());
136 if (!result) {
137 return {};
138 }
139 return IntegerAttr::get(getType(), cantFail(checkedToInt64(*result)));
140}
141
143 OpAsmParser &parser, OverflowSemanticsAttr &overflow
144) {
145 return ::parseOptionalOverflowSemantics(parser, overflow);
146}
147
149 OpAsmPrinter &printer, FeltToIndexOp /*op*/, OverflowSemanticsAttr overflow
150) {
151 ::printOptionalOverflowSemantics(printer, overflow);
152}
153
154} // namespace llzk::cast
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.
Definition Field.h:36
::llzk::cast::OverflowSemantics getOverflow()
Definition Ops.cpp.inc:186
::mlir::OpFoldResult fold(FoldAdaptor adaptor)
Definition Ops.cpp:124
GenericAdaptor<::llvm::ArrayRef<::mlir::Attribute > > FoldAdaptor
Definition Ops.h.inc:182
static void printOptionalOverflowSemantics(::mlir::OpAsmPrinter &printer, FeltToIndexOp op, ::llzk::cast::OverflowSemanticsAttr overflow)
Definition Ops.cpp:148
::mlir::ParseResult parseOptionalOverflowSemantics(::mlir::OpAsmParser &parser, ::llzk::cast::OverflowSemanticsAttr &overflow)
Definition Ops.cpp:142
::mlir::OpFoldResult fold(FoldAdaptor adaptor)
Definition Ops.cpp:96
GenericAdaptor<::llvm::ArrayRef<::mlir::Attribute > > FoldAdaptor
Definition Ops.h.inc:459
static void printOptionalOverflowSemantics(::mlir::OpAsmPrinter &printer, IntToFeltOp op, ::llzk::cast::OverflowSemanticsAttr overflow)
Definition Ops.cpp:118
::mlir::ParseResult parseOptionalOverflowSemantics(::mlir::OpAsmParser &parser, ::llzk::cast::OverflowSemanticsAttr &overflow)
Definition Ops.cpp:114
::llzk::cast::OverflowSemantics getOverflow()
Definition Ops.cpp.inc:686
static bool isCompatibleReturnTypes(::mlir::TypeRange lhs, ::mlir::TypeRange rhs)
Definition Ops.cpp:73
const ::llzk::Field & getField() const
Definition Dialect.cpp:255
bool hasField() const
Definition Types.h.inc:50
std::optional< DynamicAPInt > applyIntToFeltOverflow(const DynamicAPInt &value, const Field &field, OverflowSemantics overflow)
::llvm::StringRef stringifyOverflowSemantics(OverflowSemantics val)
Definition Enums.cpp.inc:12
std::optional< DynamicAPInt > applyFeltToIndexOverflow(const DynamicAPInt &value, unsigned indexBitWidth, OverflowSemantics overflow)
::std::optional< OverflowSemantics > symbolizeOverflowSemantics(::llvm::StringRef str)
Definition Enums.cpp.inc:22
Expected< int64_t > checkedToInt64(const DynamicAPInt &value)
llvm::DynamicAPInt integerAttrToDynamicAPInt(mlir::IntegerAttr attr)
Decode an MLIR integer using its signedness; signless i1 denotes a boolean.