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"
18include "mlir/IR/OpBase.td"
19include "mlir/IR/OpAsmInterface.td"
20include "mlir/IR/SymbolInterfaces.td"
21include "mlir/Interfaces/SideEffectInterfaces.td"
23class PODDialectOp<string mnemonic, list<Trait> traits = []>
24 : Op<PODDialect, mnemonic, traits>;
26class PODAccessOpBase<string mnemonic, list<Trait> traits = []>
27 : PODDialectOp<mnemonic,
28 traits#[DeclareOpInterfaceMethods<PodAccessOpInterface>]> {
29 let extraClassDeclaration = [{
30 /// Gets the type of the referenced pod.
31 inline ::llzk::pod::PodType getPodRefType() {
32 return ::llvm::cast<PodAccessOpInterface>(getOperation()).getPodRefType();
37// isRead: read(1) vs write(0) ops
38class ScalarPODAccessOp<string mnemonic, bit isRead, list<Trait> traits = []>
41 traits#[DeclareOpInterfaceMethods<DestructurableAccessorOpInterface>,
42 DeclareOpInterfaceMethods<PromotableMemOpInterface>]> {
43 let extraClassDefinition = [{
44 /// Required by DestructurableAllocationOpInterface / SROA pass
45 bool $cppClass::canRewire(const ::mlir::DestructurableMemorySlot &slot,
46 ::llvm::SmallPtrSetImpl<::mlir::Attribute> &usedIndices,
47 ::mlir::SmallVectorImpl<::mlir::MemorySlot> &mustBeSafelyUsed,
48 const ::mlir::DataLayout &dataLayout) {
49 return ::llvm::cast<PodAccessOpInterface>(getOperation())
50 .canRewire(slot, usedIndices, mustBeSafelyUsed, dataLayout);
53 /// Required by DestructurableAllocationOpInterface / SROA pass
54 ::mlir::DeletionKind $cppClass::rewire(const ::mlir::DestructurableMemorySlot &slot,
55 ::llvm::DenseMap<::mlir::Attribute, ::mlir::MemorySlot> &subslots,
56 ::mlir::OpBuilder &builder, const ::mlir::DataLayout &dataLayout) {
57 return ::llvm::cast<PodAccessOpInterface>(getOperation())
58 .rewire(slot, subslots, builder, dataLayout);
61 /// Required by PromotableMemOpInterface / mem2reg pass
62 bool $cppClass::loadsFrom(const ::mlir::MemorySlot &slot) {
63 return }]#!if(isRead, "getPodRef() == slot.ptr", "false")#[{;
66 /// Required by PromotableMemOpInterface / mem2reg pass
67 bool $cppClass::storesTo(const ::mlir::MemorySlot &slot) {
68 return }]#!if(isRead, "false", "getPodRef() == slot.ptr")#[{;
71 /// Required by PromotableAllocationOpInterface / mem2reg pass
72 ::mlir::Value $cppClass::getStored(const ::mlir::MemorySlot &, ::mlir::OpBuilder &,
73 ::mlir::Value, const ::mlir::DataLayout &) {
76 "llvm_unreachable(\"getStored() should not be called on $cppClass\")",
77 "return getValue()")#[{;
80 /// Required by PromotableMemOpInterface / mem2reg pass
81 bool $cppClass::canUsesBeRemoved(
82 const ::mlir::MemorySlot &slot,
83 const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &blockingUses,
84 ::llvm::SmallVectorImpl<::mlir::OpOperand *> & /*newBlockingUses*/,
85 const ::mlir::DataLayout & /*datalayout*/) {
86 if (blockingUses.size() != 1) {
89 ::mlir::Value blockingUse = (*blockingUses.begin())->get();
90 return blockingUse == slot.ptr && getPodRef() == slot.ptr &&
91 }]#!if(isRead, "getResult().getType() == slot.elemType",
92 "getValue() != slot.ptr && getValue().getType() == slot.elemType")#[{;
95 /// Required by PromotableMemOpInterface / mem2reg pass
96 ::mlir::DeletionKind $cppClass::removeBlockingUses(
97 const ::mlir::MemorySlot &, const ::llvm::SmallPtrSetImpl<::mlir::OpOperand *> &,
98 ::mlir::OpBuilder &, ::mlir::Value reachingDefinition, const ::mlir::DataLayout &) {
99 }]#!if(isRead, "getResult().replaceAllUsesWith(reachingDefinition)",
101 return ::mlir::DeletionKind::Delete;
104 /// Return `true` if the op is a read, `false` if it's a write.
105 bool $cppClass::isRead() {
106 return }]#!if(isRead, "true", "false")#[{;
114 [Pure, AttrSizedOperandSegments, VerifySizesForMultiAffineOps<1>,
115 DeclareOpInterfaceMethods<PromotableAllocationOpInterface>,
116 DeclareOpInterfaceMethods<DestructurableAllocationOpInterface>,
117 DeclareOpInterfaceMethods<
118 OpAsmOpInterface, ["getAsmResultNames"]>]> {
119 let summary = "create a new plain-old-data struct";
121 Creates a new, uninitialized, pod instance. Optionally, the user can pass a list of record names and values
122 that initialize the records of the pod. Partial initialization is allowed. All records without
123 an explicit initialization are initialized with nondeterministic values.
125 If the types of the pod records have affine map parameters the user can pass values for them similar
126 to how `array.new` does it.
128 This operation returns one value of type `PODType` and, if present, the records passed for initialization
129 must form a subset of the records in the type.
133 // Empty pod instance
134 %0 = pod.new : !pod.type<[]>
136 // Uninitialized/nondeterministic pod instance
137 %0 = pod.new : !pod.type<[@n: !felt.type]>
139 // Initialized pod instance
141 %1 = pod.new { @n = %0 } : !pod.type<[@n: !felt.type]>
143 // Another one, but with 2 fields
146 %2 = pod.new { @n = %0, @inv = %1 } : !pod.type<[@n: !felt.type, @inv: !felt.type]>
150 %1 = pod.new { @n = %0 } : !pod.type<[@n: !felt.type, @inv: !felt.type]>
152 // Affine map args on uninitialized pod instance
153 %0 = arith.constant 1 : index
154 %c = arith.constant 2 : index
155 %1 = pod.new(%0)[%c] : !pod.type<[@a: !array.type<#map, !felt.type>]>
157 // Affine map with initialized records
158 %0 = arith.constant 1 : index
159 %c = arith.constant 2 : index
161 %2 = pod.new { @f = %1 }(%0)[%c] : !pod.type<[@f: !felt.type, @a: !array.type<#map, !felt.type>]>
166 // Initialization values
167 Variadic<AnyLLZKType>:$initialValues,
168 DefaultValuedAttr<StrArrayAttr, "{}">:$initializedRecords,
169 // Affine map arguments
170 VariadicOfVariadic<Index, "mapOpGroupSizes">:$mapOperands,
171 DefaultValuedAttr<DenseI32ArrayAttr, "{}">:$numDimsPerMap,
172 DenseI32ArrayAttr:$mapOpGroupSizes);
173 let results = (outs LLZK_PODType:$result);
174 let skipDefaultBuilders = 1;
177 (ins CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues),
179 auto resultType = ::llzk::pod::PodType::fromInitialValues($_builder.getContext(), initialValues);
180 build($_builder, $_state, resultType, initialValues);
182 OpBuilder<(ins "::llzk::pod::PodType":$resultType,
183 CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues)>,
184 OpBuilder<(ins "::llzk::pod::PodType":$resultType,
185 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
186 "::mlir::DenseI32ArrayAttr":$numDimsPerMap,
187 CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues)>,
189 (ins "::llzk::pod::PodType":$resultType,
190 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
191 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
192 CArg<"::llzk::pod::InitializedRecords", "{}">:$initialValues),
194 build($_builder, $_state, resultType, mapOperands,
195 $_builder.getDenseI32ArrayAttr(numDimsPerMap), initialValues);
197 let hasCustomAssemblyFormat = 1;
200 let extraClassDeclaration = [{
201 ::mlir::SmallVector<::llzk::pod::RecordValue> getInitializedRecordValues();
205def LLZK_ReadPodOp : ScalarPODAccessOp<"read", 1> {
206 let summary = "reads the contents of a plain-old-data struct record";
208 Reads the current value of a named record from a pod instance.
209 Returns one value of the type of the record.
211 The name of the record must be a valid record name for the pod type and the result
212 type must match the type of the record.
216 %1 = pod.read %0[@sym] : !pod.type<[@sym: !type, ...]>, !type
220 let arguments = (ins Arg<LLZK_PODType, "the pod to read from">:$pod_ref,
221 FlatSymbolRefAttr:$record_name);
222 let results = (outs AnyLLZKType:$result);
223 let assemblyFormat = [{
224 $pod_ref `[` custom<RecordName>($record_name) `]` `:` type($pod_ref) `,` type($result) attr-dict
229def LLZK_WritePodOp : ScalarPODAccessOp<"write", 0> {
230 let summary = "writes content into a plain-old-data struct record";
232 Writes a value into a named record of a pod instance. This operation has no result values.
234 The name of the record must be a valid record name for the pod type and the source
235 value type must match the type of the record.
237 Writing a record that was written or initialized earlier overwrites the previous value.
241 pod.write %0[@sym] = %1 : !pod.type<[@sym: !type, ...]>, !type
245 let arguments = (ins Arg<LLZK_PODType, "the pod to write into">:$pod_ref,
246 FlatSymbolRefAttr:$record_name, AnyLLZKType:$value);
248 let assemblyFormat = [{
249 $pod_ref `[` custom<RecordName>($record_name) `]` `=` $value `:` type($pod_ref) `,` type($value) attr-dict
254#endif // LLZK_POD_OPS