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 2026 Project LLZK
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef LLZK_POD_OPS
11#define LLZK_POD_OPS
12
13include "llzk/Dialect/POD/IR/Dialect.td"
14include "llzk/Dialect/POD/IR/OpInterfaces.td"
15include "llzk/Dialect/POD/IR/Types.td"
16include "llzk/Dialect/Shared/OpTraits.td"
17include "llzk/Dialect/Shared/DiscardableAllocationOpInterfaces.td"
18
19include "mlir/IR/OpBase.td"
20include "mlir/IR/OpAsmInterface.td"
21include "mlir/IR/SymbolInterfaces.td"
22include "mlir/Interfaces/SideEffectInterfaces.td"
23
24class PODDialectOp<string mnemonic, list<Trait> traits = []>
25 : Op<PODDialect, mnemonic, traits>;
26
27class PODAccessOpBase<string mnemonic, list<Trait> traits = []>
28 : PODDialectOp<mnemonic,
29 traits#[DeclareOpInterfaceMethods<PodAccessOpInterface>]> {
30 let extraClassDeclaration = [{
31 /// Gets the type of the referenced pod.
32 inline ::llzk::pod::PodType getPodRefType() {
33 return ::llvm::cast<PodAccessOpInterface>(getOperation()).getPodRefType();
34 }
35 }];
36}
37
38// isRead: read(1) vs write(0) ops
39class ScalarPODAccessOp<string mnemonic, bit isRead, list<Trait> traits = []>
40 : PODAccessOpBase<
41 mnemonic,
42 traits#[DeclareOpInterfaceMethods<DestructurableAccessorOpInterface>,
43 DeclareOpInterfaceMethods<PromotableMemOpInterface>,
44 DeclareOpInterfaceMethods<
45 DiscardableAllocationAccessorOpInterface>]> {
46 let extraClassDefinition = [{
47 /// Required by DestructurableAllocationOpInterface / SROA pass
48 bool $cppClass::canRewire(const ::mlir::DestructurableMemorySlot &slot,
49 ::llvm::SmallPtrSetImpl<::mlir::Attribute> &usedIndices,
50 ::mlir::SmallVectorImpl<::mlir::MemorySlot> &mustBeSafelyUsed,
51 const ::mlir::DataLayout &dataLayout) {
52 return ::llvm::cast<PodAccessOpInterface>(getOperation())
53 .canRewire(slot, usedIndices, mustBeSafelyUsed, dataLayout);
54 }
55
56 /// Required by DestructurableAllocationOpInterface / SROA pass
57 ::mlir::DeletionKind $cppClass::rewire(const ::mlir::DestructurableMemorySlot &slot,
58 ::llvm::DenseMap<::mlir::Attribute, ::mlir::MemorySlot> &subslots,
59 ::mlir::OpBuilder &builder, const ::mlir::DataLayout &dataLayout) {
60 return ::llvm::cast<PodAccessOpInterface>(getOperation())
61 .rewire(slot, subslots, builder, dataLayout);
62 }
63
64 /// Required by PromotableMemOpInterface / mem2reg pass
65 bool $cppClass::loadsFrom(const ::mlir::MemorySlot &slot) {
66 return }]#!if(isRead, "getPodRef() == slot.ptr", "false")#[{;
67 }
68
69 /// Required by PromotableMemOpInterface / mem2reg pass
70 bool $cppClass::storesTo(const ::mlir::MemorySlot &slot) {
71 return }]#!if(isRead, "false", "getPodRef() == slot.ptr")#[{;
72 }
73
74 /// Required by PromotableAllocationOpInterface / mem2reg pass
75 ::mlir::Value $cppClass::getStored(const ::mlir::MemorySlot &, ::mlir::OpBuilder &,
76 ::mlir::Value, const ::mlir::DataLayout &) {
77 }]#!if(
78 isRead,
79 "llvm_unreachable(\"getStored() should not be called on $cppClass\")",
80 "return getValue()")#[{;
81 }
82
83 /// Required by PromotableMemOpInterface / mem2reg pass
84 bool $cppClass::canUsesBeRemoved(
85 const ::mlir::MemorySlot &slot,
86 const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &blockingUses,
87 ::llvm::SmallVectorImpl<::mlir::OpOperand *> & /*newBlockingUses*/,
88 const ::mlir::DataLayout & /*datalayout*/) {
89 if (blockingUses.size() != 1) {
90 return false;
91 }
92 ::mlir::Value blockingUse = (*blockingUses.begin())->get();
93 return blockingUse == slot.ptr && getPodRef() == slot.ptr &&
94 }]#!if(isRead, "getResult().getType() == slot.elemType",
95 "getValue() != slot.ptr && getValue().getType() == slot.elemType")#[{;
96 }
97
98 /// Required by PromotableMemOpInterface / mem2reg pass
99 ::mlir::DeletionKind $cppClass::removeBlockingUses(
100 const ::mlir::MemorySlot &, const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &,
101 ::mlir::OpBuilder &, ::mlir::Value reachingDefinition, const ::mlir::DataLayout &) {
102 }]#!if(isRead, "getResult().replaceAllUsesWith(reachingDefinition)",
103 "")#[{;
104 return ::mlir::DeletionKind::Delete;
105 }
106
107 /// Return `true` if the op is a read, `false` if it's a write.
108 bool $cppClass::isRead() {
109 return }]#!if(isRead, "true", "false")#[{;
110 }
111
112 /// Required by DiscardableAllocationAccessorOpInterface / unused allocation cleanup.
113 bool $cppClass::loadsFromDiscardableAllocation(::mlir::Value ptr) {
114 return }]#!if(isRead, "getPodRef() == ptr", "false")#[{;
115 }
116
117 /// Required by DiscardableAllocationAccessorOpInterface / unused allocation cleanup.
118 bool $cppClass::storesToDiscardableAllocation(::mlir::Value ptr) {
119 return }]#!if(isRead, "false",
120 "getPodRef() == ptr && getValue() != ptr")#[{;
121 }
122
123 /// Required by DiscardableAllocationAccessorOpInterface / unused allocation cleanup.
124 bool $cppClass::canEraseAsDeadStoreTo(::mlir::Value ptr, const ::mlir::DataLayout &) {
125 return }]#!if(isRead, "false",
126 "getPodRef() == ptr && getValue() != ptr")#[{;
127 }
128 }];
129}
130
131def LLZK_NewPodOp
132 : PODDialectOp<
133 "new", [MemoryEffects<[MemAlloc<DiscardableAllocationResource>]>,
134 AttrSizedOperandSegments, VerifySizesForMultiAffineOps<1>,
135 DeclareOpInterfaceMethods<PromotableAllocationOpInterface>,
136 DeclareOpInterfaceMethods<
137 DestructurableAllocationOpInterface>,
138 DeclareOpInterfaceMethods<
139 OpAsmOpInterface, ["getAsmResultNames"]>]> {
140 let summary = "create a new plain-old-data struct";
141 let description = [{
142 Creates a new, uninitialized, pod instance. Optionally, the user can pass a list of record names and values
143 that initialize the records of the pod. Partial initialization is allowed. All records without
144 an explicit initialization are initialized with nondeterministic values.
145
146 If the types of the pod records have affine map parameters the user can pass values for them similar
147 to how `array.new` does it.
148
149 This operation returns one value of type `PODType` and, if present, the records passed for initialization
150 must form a subset of the records in the type.
151
152 Examples:
153 ```llzk
154 // Empty pod instance
155 %0 = pod.new : !pod.type<[]>
156
157 // Uninitialized/nondeterministic pod instance
158 %0 = pod.new : !pod.type<[@n: !felt.type]>
159
160 // Initialized pod instance
161 %0 = felt.const 1
162 %1 = pod.new { @n = %0 } : !pod.type<[@n: !felt.type]>
163
164 // Another one, but with 2 fields
165 %0 = felt.const 1
166 %1 = felt.inv %0
167 %2 = pod.new { @n = %0, @inv = %1 } : !pod.type<[@n: !felt.type, @inv: !felt.type]>
168
169 // Partial init
170 %0 = felt.const 1
171 %1 = pod.new { @n = %0 } : !pod.type<[@n: !felt.type, @inv: !felt.type]>
172
173 // Affine map args on uninitialized pod instance
174 %0 = arith.constant 1 : index
175 %c = arith.constant 2 : index
176 %1 = pod.new(%0)[%c] : !pod.type<[@a: !array.type<#map, !felt.type>]>
177
178 // Affine map with initialized records
179 %0 = arith.constant 1 : index
180 %c = arith.constant 2 : index
181 %1 = felt.const 5
182 %2 = pod.new { @f = %1 }(%0)[%c] : !pod.type<[@f: !felt.type, @a: !array.type<#map, !felt.type>]>
183 ```
184 }];
185
186 let arguments = (ins
187 // Initialization values
188 Variadic<AnyLLZKType>:$initialValues,
189 DefaultValuedAttr<StrArrayAttr, "{}">:$initializedRecords,
190 // Affine map arguments
191 VariadicOfVariadic<Index, "mapOpGroupSizes">:$mapOperands,
192 DefaultValuedAttr<DenseI32ArrayAttr, "{}">:$numDimsPerMap,
193 DenseI32ArrayAttr:$mapOpGroupSizes);
194 let results = (outs LLZK_PODType:$result);
195 let skipDefaultBuilders = 1;
196 let builders =
197 [OpBuilder<
198 (ins CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues),
199 [{
200 auto resultType = ::llzk::pod::PodType::fromInitialValues($_builder.getContext(), initialValues);
201 build($_builder, $_state, resultType, initialValues);
202 }]>,
203 OpBuilder<(ins "::llzk::pod::PodType":$resultType,
204 CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues)>,
205 OpBuilder<(ins "::llzk::pod::PodType":$resultType,
206 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
207 "::mlir::DenseI32ArrayAttr":$numDimsPerMap,
208 CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues)>,
209 OpBuilder<
210 (ins "::llzk::pod::PodType":$resultType,
211 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
212 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
213 CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues),
214 [{
215 build($_builder, $_state, resultType, mapOperands,
216 $_builder.getDenseI32ArrayAttr(numDimsPerMap), initialValues);
217 }]>];
218 let hasCustomAssemblyFormat = 1;
219 let hasVerifier = 1;
220
221 let extraClassDeclaration = [{
222 ::mlir::SmallVector<::llzk::pod::RecordValue> getInitializedRecordValues();
223 }];
224}
225
226def LLZK_ReadPodOp : ScalarPODAccessOp<"read", 1> {
227 let summary = "reads the contents of a plain-old-data struct record";
228 let description = [{
229 Reads the current value of a named record from a pod instance.
230 Returns one value of the type of the record.
231
232 The name of the record must be a valid record name for the pod type and the result
233 type must match the type of the record.
234
235 Example:
236 ```llzk
237 %1 = pod.read %0[@sym] : !pod.type<[@sym: !type, ...]>, !type
238 ```
239 }];
240
241 let arguments = (ins Arg<LLZK_PODType, "the pod to read from">:$pod_ref,
242 StrAttr:$record_name);
243 let results = (outs AnyLLZKType:$result);
244 let assemblyFormat = [{
245 $pod_ref `[` custom<RecordName>($record_name) `]` `:` type($pod_ref) `,` type($result) attr-dict
246 }];
247 let useCustomPropertiesEncoding = 1;
248 let hasVerifier = 1;
249}
250
251def LLZK_WritePodOp : ScalarPODAccessOp<"write", 0> {
252 let summary = "writes content into a plain-old-data struct record";
253 let description = [{
254 Writes a value into a named record of a pod instance. This operation has no result values.
255
256 The name of the record must be a valid record name for the pod type and the source
257 value type must match the type of the record.
258
259 Writing a record that was written or initialized earlier overwrites the previous value.
260
261 Example:
262 ```llzk
263 pod.write %0[@sym] = %1 : !pod.type<[@sym: !type, ...]>, !type
264 ```
265 }];
266
267 let arguments = (ins Arg<LLZK_PODType, "the pod to write into">:$pod_ref,
268 StrAttr:$record_name, AnyLLZKType:$value);
269
270 let assemblyFormat = [{
271 $pod_ref `[` custom<RecordName>($record_name) `]` `=` $value `:` type($pod_ref) `,` type($value) attr-dict
272 }];
273 let useCustomPropertiesEncoding = 1;
274 let hasVerifier = 1;
275}
276
277#endif // LLZK_POD_OPS