1//===-- Ops.td ---------------------------------------------*- tablegen -*-===//
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
8//===----------------------------------------------------------------------===//
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"
19include "mlir/IR/OpBase.td"
20include "mlir/IR/OpAsmInterface.td"
21include "mlir/IR/SymbolInterfaces.td"
22include "mlir/Interfaces/SideEffectInterfaces.td"
24class PODDialectOp<string mnemonic, list<Trait> traits = []>
25 : Op<PODDialect, mnemonic, traits>;
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();
38// isRead: read(1) vs write(0) ops
39class ScalarPODAccessOp<string mnemonic, bit isRead, list<Trait> traits = []>
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);
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);
64 /// Required by PromotableMemOpInterface / mem2reg pass
65 bool $cppClass::loadsFrom(const ::mlir::MemorySlot &slot) {
66 return }]#!if(isRead, "getPodRef() == slot.ptr", "false")#[{;
69 /// Required by PromotableMemOpInterface / mem2reg pass
70 bool $cppClass::storesTo(const ::mlir::MemorySlot &slot) {
71 return }]#!if(isRead, "false", "getPodRef() == slot.ptr")#[{;
74 /// Required by PromotableAllocationOpInterface / mem2reg pass
75 ::mlir::Value $cppClass::getStored(const ::mlir::MemorySlot &, ::mlir::OpBuilder &,
76 ::mlir::Value, const ::mlir::DataLayout &) {
79 "llvm_unreachable(\"getStored() should not be called on $cppClass\")",
80 "return getValue()")#[{;
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) {
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")#[{;
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)",
104 return ::mlir::DeletionKind::Delete;
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")#[{;
112 /// Required by DiscardableAllocationAccessorOpInterface / unused allocation cleanup.
113 bool $cppClass::loadsFromDiscardableAllocation(::mlir::Value ptr) {
114 return }]#!if(isRead, "getPodRef() == ptr", "false")#[{;
117 /// Required by DiscardableAllocationAccessorOpInterface / unused allocation cleanup.
118 bool $cppClass::storesToDiscardableAllocation(::mlir::Value ptr) {
119 return }]#!if(isRead, "false",
120 "getPodRef() == ptr && getValue() != ptr")#[{;
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")#[{;
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";
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.
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.
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.
154 // Empty pod instance
155 %0 = pod.new : !pod.type<[]>
157 // Uninitialized/nondeterministic pod instance
158 %0 = pod.new : !pod.type<[@n: !felt.type]>
160 // Initialized pod instance
162 %1 = pod.new { @n = %0 } : !pod.type<[@n: !felt.type]>
164 // Another one, but with 2 fields
167 %2 = pod.new { @n = %0, @inv = %1 } : !pod.type<[@n: !felt.type, @inv: !felt.type]>
171 %1 = pod.new { @n = %0 } : !pod.type<[@n: !felt.type, @inv: !felt.type]>
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>]>
178 // Affine map with initialized records
179 %0 = arith.constant 1 : index
180 %c = arith.constant 2 : index
182 %2 = pod.new { @f = %1 }(%0)[%c] : !pod.type<[@f: !felt.type, @a: !array.type<#map, !felt.type>]>
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;
198 (ins CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues),
200 auto resultType = ::llzk::pod::PodType::fromInitialValues($_builder.getContext(), initialValues);
201 build($_builder, $_state, resultType, initialValues);
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)>,
210 (ins "::llzk::pod::PodType":$resultType,
211 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
212 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
213 CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues),
215 build($_builder, $_state, resultType, mapOperands,
216 $_builder.getDenseI32ArrayAttr(numDimsPerMap), initialValues);
218 let hasCustomAssemblyFormat = 1;
221 let extraClassDeclaration = [{
222 ::mlir::SmallVector<::llzk::pod::RecordValue> getInitializedRecordValues();
226def LLZK_ReadPodOp : ScalarPODAccessOp<"read", 1> {
227 let summary = "reads the contents of a plain-old-data struct record";
229 Reads the current value of a named record from a pod instance.
230 Returns one value of the type of the record.
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.
237 %1 = pod.read %0[@sym] : !pod.type<[@sym: !type, ...]>, !type
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
247 let useCustomPropertiesEncoding = 1;
251def LLZK_WritePodOp : ScalarPODAccessOp<"write", 0> {
252 let summary = "writes content into a plain-old-data struct record";
254 Writes a value into a named record of a pod instance. This operation has no result values.
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.
259 Writing a record that was written or initialized earlier overwrites the previous value.
263 pod.write %0[@sym] = %1 : !pod.type<[@sym: !type, ...]>, !type
267 let arguments = (ins Arg<LLZK_PODType, "the pod to write into">:$pod_ref,
268 StrAttr:$record_name, AnyLLZKType:$value);
270 let assemblyFormat = [{
271 $pod_ref `[` custom<RecordName>($record_name) `]` `=` $value `:` type($pod_ref) `,` type($value) attr-dict
273 let useCustomPropertiesEncoding = 1;
277#endif // LLZK_POD_OPS