LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
Ops.td
Go to the documentation of this file.
1//===-- Ops.td ---------------------------------------------*- tablegen -*-===//
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#ifndef LLZK_FELT_OPS
11#define LLZK_FELT_OPS
12
13include "llzk/Dialect/Felt/IR/Dialect.td"
14include "llzk/Dialect/Felt/IR/Types.td"
15include "llzk/Dialect/Felt/IR/Attrs.td"
16include "llzk/Dialect/Function/IR/OpTraits.td"
17include "llzk/Dialect/Shared/OpsBase.td"
18include "llzk/Dialect/Felt/IR/OpInterfaces.td"
19
20include "mlir/IR/OpAsmInterface.td"
21include "mlir/IR/OpBase.td"
22include "mlir/IR/SymbolInterfaces.td"
23include "mlir/Interfaces/SideEffectInterfaces.td"
24
25//===------------------------------------------------------------------===//
26// Op Classes
27//===------------------------------------------------------------------===//
28
29class FeltDialectOp<string mnemonic, list<Trait> traits = []>
30 : Op<FeltDialect, mnemonic, traits>;
31
32class FeltDialectBinaryOp<string mnemonic, list<Trait> traits = []>
33 : BinaryOpBase<FeltDialect, mnemonic, LLZK_FeltType,
34 traits#[TypesUnify<"lhs", "rhs">,
35 DeclareOpInterfaceMethods<FeltBinaryOpInterface>,
36 InferTypeOpAdaptorWithIsCompatible]> {
37 // Allow any type to avoid building default felt for the result type, we
38 // employ our own inference below.
39 let results = (outs AnyLLZKType:$result);
40 let hasFolder = 1;
41
42 let extraClassDefinition = [{
43 ::llvm::LogicalResult $cppClass::inferReturnTypes(
44 mlir::MLIRContext *context, std::optional<mlir::Location> loc, Adaptor adaptor,
45 llvm::SmallVectorImpl<mlir::Type> &inferred
46 ) {
47 inferred.resize(1);
48 auto value = adaptor.getLhs();
49 inferred[0] = value ? value.getType() : FeltType::get(context, mlir::StringAttr());
50 return mlir::success();
51 }
52
53 bool $cppClass::isCompatibleReturnTypes(mlir::TypeRange l, mlir::TypeRange r) {
54 return l == r;
55 }
56 }];
57}
58
59class FeltDialectUnaryOp<string mnemonic, list<Trait> traits = []>
60 : UnaryOpBase<FeltDialect, mnemonic, LLZK_FeltType,
61 traits#[InferTypeOpAdaptorWithIsCompatible]> {
62 // Allow any type to avoid building default felt for the result type, we
63 // employ our own inference below.
64 let results = (outs AnyLLZKType:$result);
65 let hasFolder = 1;
66
67 let extraClassDefinition = [{
68 ::llvm::LogicalResult $cppClass::inferReturnTypes(
69 mlir::MLIRContext *context, std::optional<mlir::Location> loc, Adaptor adaptor,
70 llvm::SmallVectorImpl<mlir::Type> &inferred
71 ) {
72 inferred.resize(1);
73 auto value = adaptor.getOperand();
74 inferred[0] = value ? value.getType() : FeltType::get(context, mlir::StringAttr());
75 return mlir::success();
76 }
77
78 bool $cppClass::isCompatibleReturnTypes(mlir::TypeRange l, mlir::TypeRange r) {
79 return l == r;
80 }
81 }];
82}
83
84//===------------------------------------------------------------------===//
85// Constants
86//===------------------------------------------------------------------===//
87
88def LLZK_FeltConstantOp
89 : FeltDialectOp<"const", [ConstantLike, Pure,
90 DeclareOpInterfaceMethods<
91 OpAsmOpInterface, ["getAsmResultNames"]>,
92 TypeUnifyWithResult<"value">,
93 InferTypeOpAdaptorWithIsCompatible]> {
94 let summary = "field element constant";
95 let description = [{
96 This operation produces a constant finite field element.
97 `felt.const N : !felt.type<F>` denotes `N mod p`, where `p` is the prime
98 modulus of field `F`. This meaning does not depend on whether the operation
99 has been canonicalized: raw values congruent modulo `p` denote the same
100 field element.
101
102 The stored integer need not be in `[0, p)`. Canonicalization replaces it
103 with the unique representative in that range without changing its meaning.
104 When the field is unspecified, reduction is deferred until the field is
105 known, and canonicalization leaves the stored integer unchanged.
106
107 Some analyses or transformations may supply an explicit prime modulus `q`
108 for their arithmetic, such as interval analysis with its `field` option.
109 The constant retains its declared-field meaning: its value is first
110 interpreted as `N mod p`, then reduced modulo `q`, yielding `(N mod p) mod q`.
111 For example, a literal `8` declared in a field of modulus `7` contributes `1`
112 when analyzed with modulus `5`, both before and after canonicalization.
113 If the constant's field is unspecified, it is reduced directly as `N mod q`.
114
115 Example:
116
117 ```llzk
118 %0 = felt.const 42
119 %0 = felt.const 99 <"bn254">
120 ```
121 }];
122
123 let arguments = (ins LLZK_FeltConstAttr:$value);
124 let results = (outs AnyLLZKType:$result);
125 let assemblyFormat = [{ $value attr-dict }];
126 let hasFolder = 1;
127 let hasCanonicalizeMethod = 1;
128}
129
130//===------------------------------------------------------------------===//
131// Operators
132//===------------------------------------------------------------------===//
133
134def LLZK_AddFeltOp : FeltDialectBinaryOp<"add", [Commutative]> {
135 let summary = "addition operator for field elements";
136 let description = [{}];
137}
138
139def LLZK_SubFeltOp : FeltDialectBinaryOp<"sub"> {
140 let summary = "subtraction operator for field elements";
141 let description = [{}];
142}
143
144def LLZK_MulFeltOp : FeltDialectBinaryOp<"mul", [Commutative]> {
145 let summary = "multiplication operator for field elements";
146 let description = [{}];
147}
148
149def LLZK_PowFeltOp : FeltDialectBinaryOp<"pow", [NotFieldNative]> {
150 let summary = "exponentiation operator for field elements";
151 let description = [{
152
153 Raises a field element to the power of an exponent.
154
155 ```llzk
156 %result = felt.pow %base, %exponent
157 ```
158
159 }];
160}
161
162def LLZK_DivFeltOp : FeltDialectBinaryOp<"div"> {
163 let summary = "field-division operator for field elements";
164 let description = [{
165 Performs finite-field division by multiplying the dividend by the
166 multiplicative inverse of the divisor. For a non-zero divisor `b`,
167 `felt.div %a, %b` computes `%a * inv(%b)` modulo the field prime of the result
168 prime field.
169
170 The divisor must be non-zero.
171
172 This is not integer division. Use `felt.uintdiv` or `felt.sintdiv` when the
173 operands should be interpreted as integers.
174 }];
175}
176
177def LLZK_UnsignedIntDivFeltOp
178 : FeltDialectBinaryOp<"uintdiv", [NotFieldNative]> {
179 let summary = "unsigned integer division operator for field elements";
180 let description = [{
181 Treats the operands as if they were unsigned integers with bitwidth
182 equal to that of the prime modulus and performs division rounding towards zero.
183 }];
184}
185
186def LLZK_SignedIntDivFeltOp : FeltDialectBinaryOp<"sintdiv", [NotFieldNative]> {
187 let summary = "signed integer division operator for field elements";
188 let description = [{
189 Treats the operands as if they were signed integers with bitwidth
190 equal to that of the prime modulus (no additional sign bit is added)
191 and performs division rounding towards zero.
192
193 The signed integer representation of felt `f` in prime field with modulus
194 `p` follows the following formula:
195
196 signed_int(f) = f if 0 <= f < p/2 + 1
197 "p/2" here is unsigned integer division rounding towards 0
198 signed_int(f) = f-p if p/2 + 1 <= f < p
199 }];
200}
201
202def LLZK_UnsignedModFeltOp : FeltDialectBinaryOp<"umod", [NotFieldNative]> {
203 let summary =
204 "unsigned integer modulus/remainder operator for field elements";
205 let description = [{
206 Computes the remainder that would result from the division operation performed
207 by `felt.uintdiv`.
208 }];
209}
210
211def LLZK_SignedModFeltOp : FeltDialectBinaryOp<"smod", [NotFieldNative]> {
212 let summary = "signed integer modulus/remainder operator for field elements";
213 let description = [{
214 Computes the remainder that would result from the division operation performed
215 by `felt.sintdiv`.
216 }];
217}
218
219def LLZK_NegFeltOp : FeltDialectUnaryOp<"neg"> {
220 let summary = "negation operator for field elements";
221 let description = [{}];
222}
223
224def LLZK_InvFeltOp : FeltDialectUnaryOp<"inv", [NotFieldNative]> {
225 let summary = "inverse operator for field elements";
226 let description = [{}];
227}
228
229def LLZK_AndFeltOp
230 : FeltDialectBinaryOp<"bit_and", [NotFieldNative, Commutative]> {
231 let summary = "bitwise AND operator for field elements";
232 let description = [{}];
233}
234
235def LLZK_OrFeltOp
236 : FeltDialectBinaryOp<"bit_or", [NotFieldNative, Commutative]> {
237 let summary = "bitwise OR operator for field elements";
238 let description = [{}];
239}
240
241def LLZK_XorFeltOp
242 : FeltDialectBinaryOp<"bit_xor", [NotFieldNative, Commutative]> {
243 let summary = "bitwise XOR operator for field elements";
244 let description = [{}];
245}
246
247def LLZK_NotFeltOp : FeltDialectUnaryOp<"bit_not", [NotFieldNative]> {
248 let summary = "integer complement (bitwise-not) operator for field elements";
249 let description = [{
250 Treats the operand as an integer with a bitwidth equal to the bitwidth
251 of the prime field's modulus and compute the one's complement of the integer.
252 The result is converted back to a field element by applying the prime modulus.
253 }];
254}
255
256def LLZK_ShlFeltOp : FeltDialectBinaryOp<"shl", [NotFieldNative]> {
257 let summary = "left shift operator for field elements";
258 let description = [{
259 Treats both operands as unsigned integer representatives of field elements.
260 `felt.shl %a, %b` computes `%a * 2^%b`, then converts the result back to a
261 field element by reducing modulo the field prime.
262 }];
263}
264
265def LLZK_ShrFeltOp : FeltDialectBinaryOp<"shr", [NotFieldNative]> {
266 let summary = "right shift operator for field elements";
267 let description = [{
268 Treats both operands as unsigned integer representatives of field elements.
269 `felt.shr %a, %b` computes `%a / 2^%b` using unsigned integer division
270 rounding towards zero, then converts the result back to a field element.
271 }];
272}
273
274#endif // LLZK_FELT_OPS