LLZK 2.1.1
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 felt-typed SSA value holding an integer constant.
97
98 Example:
99
100 ```llzk
101 %0 = felt.const 42
102 %0 = felt.const 99 <"bn254">
103 ```
104 }];
105
106 let arguments = (ins LLZK_FeltConstAttr:$value);
107 let results = (outs AnyLLZKType:$result);
108 let assemblyFormat = [{ $value attr-dict }];
109 let hasFolder = 1;
110 let extraClassDeclaration = [{
111 auto getValueAPInt() -> ::llvm::APInt { return getValue().getValue(); }
112
113 auto tryGetZExtValue() -> std::optional<uint64_t> { return getValueAPInt().tryZExtValue(); }
114 }];
115}
116
117//===------------------------------------------------------------------===//
118// Operators
119//===------------------------------------------------------------------===//
120
121def LLZK_AddFeltOp : FeltDialectBinaryOp<"add", [Commutative]> {
122 let summary = "addition operator for field elements";
123 let description = [{}];
124}
125
126def LLZK_SubFeltOp : FeltDialectBinaryOp<"sub"> {
127 let summary = "subtraction operator for field elements";
128 let description = [{}];
129}
130
131def LLZK_MulFeltOp : FeltDialectBinaryOp<"mul", [Commutative]> {
132 let summary = "multiplication operator for field elements";
133 let description = [{}];
134}
135
136def LLZK_PowFeltOp : FeltDialectBinaryOp<"pow", [NotFieldNative]> {
137 let summary = "exponentiation operator for field elements";
138 let description = [{
139
140 Raises a field element to the power of an exponent.
141
142 ```llzk
143 %result = felt.pow %base, %exponent
144 ```
145
146 }];
147}
148
149def LLZK_DivFeltOp : FeltDialectBinaryOp<"div"> {
150 let summary = "field-division operator for field elements";
151 let description = [{
152 Performs finite-field division by multiplying the dividend by the
153 multiplicative inverse of the divisor. For a non-zero divisor `b`,
154 `felt.div %a, %b` computes `%a * inv(%b)` modulo the field prime of the result
155 prime field.
156
157 The divisor must be non-zero.
158
159 This is not integer division. Use `felt.uintdiv` or `felt.sintdiv` when the
160 operands should be interpreted as integers.
161 }];
162}
163
164def LLZK_UnsignedIntDivFeltOp
165 : FeltDialectBinaryOp<"uintdiv", [NotFieldNative]> {
166 let summary = "unsigned integer division operator for field elements";
167 let description = [{
168 Treats the operands as if they were unsigned integers with bitwidth
169 equal to that of the prime modulus and performs division rounding towards zero.
170 }];
171}
172
173def LLZK_SignedIntDivFeltOp : FeltDialectBinaryOp<"sintdiv", [NotFieldNative]> {
174 let summary = "signed integer division operator for field elements";
175 let description = [{
176 Treats the operands as if they were signed integers with bitwidth
177 equal to that of the prime modulus (no additional sign bit is added)
178 and performs division rounding towards zero.
179
180 The signed integer representation of felt `f` in prime field with modulus
181 `p` follows the following formula:
182
183 signed_int(f) = f if 0 <= f < p/2 + 1
184 "p/2" here is unsigned integer division rounding towards 0
185 signed_int(f) = f-p if p/2 + 1 <= f < p
186 }];
187}
188
189def LLZK_UnsignedModFeltOp : FeltDialectBinaryOp<"umod", [NotFieldNative]> {
190 let summary =
191 "unsigned integer modulus/remainder operator for field elements";
192 let description = [{
193 Computes the remainder that would result from the division operation performed
194 by `felt.uintdiv`.
195 }];
196}
197
198def LLZK_SignedModFeltOp : FeltDialectBinaryOp<"smod", [NotFieldNative]> {
199 let summary = "signed integer modulus/remainder operator for field elements";
200 let description = [{
201 Computes the remainder that would result from the division operation performed
202 by `felt.sintdiv`.
203 }];
204}
205
206def LLZK_NegFeltOp : FeltDialectUnaryOp<"neg"> {
207 let summary = "negation operator for field elements";
208 let description = [{}];
209}
210
211def LLZK_InvFeltOp : FeltDialectUnaryOp<"inv", [NotFieldNative]> {
212 let summary = "inverse operator for field elements";
213 let description = [{}];
214}
215
216def LLZK_AndFeltOp
217 : FeltDialectBinaryOp<"bit_and", [NotFieldNative, Commutative]> {
218 let summary = "bitwise AND operator for field elements";
219 let description = [{}];
220}
221
222def LLZK_OrFeltOp
223 : FeltDialectBinaryOp<"bit_or", [NotFieldNative, Commutative]> {
224 let summary = "bitwise OR operator for field elements";
225 let description = [{}];
226}
227
228def LLZK_XorFeltOp
229 : FeltDialectBinaryOp<"bit_xor", [NotFieldNative, Commutative]> {
230 let summary = "bitwise XOR operator for field elements";
231 let description = [{}];
232}
233
234def LLZK_NotFeltOp : FeltDialectUnaryOp<"bit_not", [NotFieldNative]> {
235 let summary = "integer complement (bitwise-not) operator for field elements";
236 let description = [{
237 Treats the operand as an integer with a bitwidth equal to the bitwidth
238 of the prime field's modulus and compute the one's complement of the integer.
239 The result is converted back to a field element by applying the prime modulus.
240 }];
241}
242
243def LLZK_ShlFeltOp : FeltDialectBinaryOp<"shl", [NotFieldNative]> {
244 let summary = "left shift operator for field elements";
245 let description = [{
246 Treats both operands as unsigned integer representatives of field elements.
247 `felt.shl %a, %b` computes `%a * 2^%b`, then converts the result back to a
248 field element by reducing modulo the field prime.
249 }];
250}
251
252def LLZK_ShrFeltOp : FeltDialectBinaryOp<"shr", [NotFieldNative]> {
253 let summary = "right shift operator for field elements";
254 let description = [{
255 Treats both operands as unsigned integer representatives of field elements.
256 `felt.shr %a, %b` computes `%a / 2^%b` using unsigned integer division
257 rounding towards zero, then converts the result back to a field element.
258 }];
259}
260
261#endif // LLZK_FELT_OPS