LLZK
3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Toggle main menu visibility
Loading...
Searching...
No Matches
SpecializedMemoryPasses.h
Go to the documentation of this file.
1
//===-- SpecializedMemoryPasses.h - Targeted SROA / mem2reg -----*- C++ -*-===//
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
//===----------------------------------------------------------------------===//
16
//===----------------------------------------------------------------------===//
17
18
#pragma once
19
20
#include "
llzk/Dialect/LLZK/IR/Dialect.h
"
21
#include "
llzk/Dialect/LLZK/IR/Ops.h
"
22
#include "
llzk/Util/Walk.h
"
23
24
#include <mlir/Analysis/DataLayoutAnalysis.h>
25
#include <mlir/Dialect/SCF/IR/SCF.h>
26
#include <mlir/Dialect/UB/IR/UBOps.h>
27
#include <mlir/IR/Dominance.h>
28
#include <mlir/Interfaces/MemorySlotInterfaces.h>
29
#include <mlir/Pass/Pass.h>
30
#include <mlir/Pass/PassManager.h>
31
#include <mlir/Transforms/Mem2Reg.h>
32
#include <mlir/Transforms/Passes.h>
33
#include <mlir/Transforms/SROA.h>
34
35
#include <llvm/ADT/StringRef.h>
36
37
#include <memory>
38
39
namespace
llzk
{
40
44
template
<
typename
AllocOpTy>
45
struct
SpecializedSROA
: mlir::PassWrapper<SpecializedSROA<AllocOpTy>, mlir::OperationPass<>> {
46
47
mlir::StringRef
getArgument
()
const override
{
return
"llzk-specialized-sroa"
; }
48
49
mlir::StringRef
getDescription
()
const override
{
50
return
"Scalar replacement of aggregates for a specific allocator op type"
;
51
}
52
53
void
runOnOperation
()
override
{
54
mlir::Operation *scopeOp = this->getOperation();
55
56
auto
&dataLayoutAnalysis = this->
template
getAnalysis<mlir::DataLayoutAnalysis>();
57
const
mlir::DataLayout &dataLayout = dataLayoutAnalysis.getAtOrAbove(scopeOp);
58
59
bool
changed =
false
;
60
61
for
(mlir::Region ®ion : scopeOp->getRegions()) {
62
if
(region.getBlocks().empty()) {
63
continue
;
64
}
65
66
mlir::OpBuilder builder(®ion.front(), region.front().begin());
67
68
auto
allocators = walkCollectMapped<AllocOpTy>(region, [](
auto
allocator) {
69
return
mlir::DestructurableAllocationOpInterface(allocator);
70
});
71
72
if
(mlir::succeeded(mlir::tryToDestructureMemorySlots(allocators, builder, dataLayout))) {
73
changed =
true
;
74
}
75
}
76
77
if
(!changed) {
78
this->markAllAnalysesPreserved();
79
}
80
}
81
};
82
83
// Pass factory for `SpecializedSROA`.
84
template
<
typename
AllocOpTy>
85
std::unique_ptr<SpecializedSROA<AllocOpTy>>
createSpecializedSROAPass
() {
86
return
std::make_unique<SpecializedSROA<AllocOpTy>>();
87
}
88
92
template
<
typename
AllocOpTy>
93
struct
SpecializedMem2Reg
94
: mlir::PassWrapper<SpecializedMem2Reg<AllocOpTy>, mlir::OperationPass<>> {
95
96
mlir::StringRef
getArgument
()
const override
{
return
"llzk-specialized-mem2reg"
; }
97
98
mlir::StringRef
getDescription
()
const override
{
99
return
"Promotes memory slots of a specific allocator op type into values"
;
100
}
101
102
void
runOnOperation
()
override
{
103
mlir::Operation *scopeOp = this->getOperation();
104
105
auto
&dataLayoutAnalysis = this->
template
getAnalysis<mlir::DataLayoutAnalysis>();
106
const
mlir::DataLayout &dataLayout = dataLayoutAnalysis.getAtOrAbove(scopeOp);
107
auto
&dominance = this->
template
getAnalysis<mlir::DominanceInfo>();
108
109
bool
changed =
false
;
110
111
for
(mlir::Region ®ion : scopeOp->getRegions()) {
112
if
(region.getBlocks().empty()) {
113
continue
;
114
}
115
116
mlir::OpBuilder builder(®ion.front(), region.front().begin());
117
118
auto
allocators = walkCollectMapped<AllocOpTy>(region, [](
auto
allocator) {
119
return
mlir::PromotableAllocationOpInterface(allocator);
120
});
121
122
auto
promoteRes = mlir::tryToPromoteMemorySlots(allocators, builder, dataLayout, dominance);
123
if
(mlir::succeeded(promoteRes)) {
124
changed =
true
;
125
}
126
}
127
128
if
(!changed) {
129
this->markAllAnalysesPreserved();
130
}
131
}
132
};
133
134
// Pass factory for `SpecializedMem2Reg`.
135
template
<
typename
AllocOpTy>
136
std::unique_ptr<SpecializedMem2Reg<AllocOpTy>>
createSpecializedMem2RegPass
() {
137
return
std::make_unique<SpecializedMem2Reg<AllocOpTy>>();
138
}
139
140
namespace
detail {
141
145
class
RemoveDeadValuesWorkaroundPass
146
:
public
mlir::PassWrapper<RemoveDeadValuesWorkaroundPass, mlir::OperationPass<>> {
147
148
static
inline
std::unique_ptr<mlir::Pass> createRemoveDeadValuesPass() {
149
return
mlir::createRemoveDeadValuesPass(
150
mlir::RemoveDeadValuesPassOptions {.canonicalize =
true
}
151
);
152
}
153
154
public
:
155
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(
RemoveDeadValuesWorkaroundPass
)
156
157
llvm
::StringRef
getArgument
()
const override
{
return
"remove-dead-values"
; }
158
llvm::StringRef
getDescription
()
const override
{
return
"Remove dead values"
; }
159
160
void
getDependentDialects
(mlir::DialectRegistry ®istry)
const override
{
161
createRemoveDeadValuesPass()->getDependentDialects(registry);
162
registry.insert<
LLZKDialect
>();
163
}
164
165
void
runOnOperation
() final {
166
mlir::Operation *scopeOp = this->getOperation();
167
168
mlir::OpPassManager pm(scopeOp->getName().getStringRef());
169
pm.addPass(createRemoveDeadValuesPass());
170
if
(mlir::failed(runPipeline(pm, scopeOp))) {
171
signalPassFailure();
172
}
173
174
// Upstream RDV only tracks poison values created during its current invocation. This wrapper
175
// may run repeatedly, so values created by an earlier invocation can become unused later.
176
// Additionally, replace any remaining uses of `ub.poison` with LLZK native `llzk.nondet` op.
177
for
(mlir::ub::PoisonOp poisonOp : walkCollect<mlir::ub::PoisonOp>(*scopeOp)) {
178
auto
poisonResult = poisonOp.getResult();
179
if
(!poisonResult.use_empty()) {
180
mlir::OpBuilder builder(poisonOp);
181
auto
nondetOp =
NonDetOp::create
(builder, poisonOp.getLoc(), poisonOp.getType());
182
poisonResult.replaceAllUsesWith(nondetOp.getResult());
183
}
184
poisonOp.erase();
185
}
186
187
// Finally, those `ub.poison` removals can leave behind trivial `else` blocks that would have
188
// otherwise been removed by RDV so go ahead and remove them now.
189
scopeOp->walk([](mlir::scf::IfOp ifOp) {
190
if
(ifOp.getResults().empty()) {
191
mlir::Region &elseRegion = ifOp.getElseRegion();
192
if
(!llvm::hasSingleElement(elseRegion)) {
193
return
;
194
}
195
mlir::Block &elseBlock = elseRegion.front();
196
if
(!llvm::hasSingleElement(elseBlock)) {
197
return
;
198
}
199
if
(!llvm::isa<mlir::scf::YieldOp>(elseBlock.front())) {
200
return
;
201
}
202
elseRegion.dropAllReferences();
203
elseBlock.clear();
204
elseRegion.getBlocks().clear();
205
}
206
});
207
}
208
};
209
210
}
// namespace detail
211
212
inline
std::unique_ptr<mlir::Pass>
createRemoveDeadValuesWorkaroundPass
() {
213
return
std::make_unique<detail::RemoveDeadValuesWorkaroundPass>();
214
}
215
216
}
// namespace llzk
Dialect.h
Ops.h
Walk.h
llzk::LLZKDialect
Common LLZK attributes.
Definition
Dialect.h.inc:13
llzk::NonDetOp::create
static NonDetOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type res)
Definition
Ops.cpp.inc:94
llzk::detail::RemoveDeadValuesWorkaroundPass
A workaround wrapper around MLIR's remove-dead-values pass that normalizes empty scf....
Definition
SpecializedMemoryPasses.h:146
llzk::detail::RemoveDeadValuesWorkaroundPass::getDependentDialects
void getDependentDialects(mlir::DialectRegistry ®istry) const override
Definition
SpecializedMemoryPasses.h:160
llzk::detail::RemoveDeadValuesWorkaroundPass::runOnOperation
void runOnOperation() final
Definition
SpecializedMemoryPasses.h:165
llzk::detail::RemoveDeadValuesWorkaroundPass::getDescription
llvm::StringRef getDescription() const override
Definition
SpecializedMemoryPasses.h:158
llzk::detail::RemoveDeadValuesWorkaroundPass::getArgument
llvm::StringRef getArgument() const override
Definition
SpecializedMemoryPasses.h:157
llvm
Definition
SourceRefLattice.cpp:411
llzk
Definition
AnalysisPassEnums.cpp:19
llzk::createRemoveDeadValuesWorkaroundPass
std::unique_ptr< mlir::Pass > createRemoveDeadValuesWorkaroundPass()
Definition
SpecializedMemoryPasses.h:212
llzk::createSpecializedMem2RegPass
std::unique_ptr< SpecializedMem2Reg< AllocOpTy > > createSpecializedMem2RegPass()
Definition
SpecializedMemoryPasses.h:136
llzk::createSpecializedSROAPass
std::unique_ptr< SpecializedSROA< AllocOpTy > > createSpecializedSROAPass()
Definition
SpecializedMemoryPasses.h:85
llzk::SpecializedMem2Reg
A variant of the MLIR mem2reg pass that only promotes memory slots belonging to allocators of type Al...
Definition
SpecializedMemoryPasses.h:94
llzk::SpecializedMem2Reg::runOnOperation
void runOnOperation() override
Definition
SpecializedMemoryPasses.h:102
llzk::SpecializedMem2Reg::getArgument
mlir::StringRef getArgument() const override
Definition
SpecializedMemoryPasses.h:96
llzk::SpecializedMem2Reg::getDescription
mlir::StringRef getDescription() const override
Definition
SpecializedMemoryPasses.h:98
llzk::SpecializedSROA
A variant of the MLIR sroa pass that only destructures memory slots belonging to allocators of type A...
Definition
SpecializedMemoryPasses.h:45
llzk::SpecializedSROA::getDescription
mlir::StringRef getDescription() const override
Definition
SpecializedMemoryPasses.h:49
llzk::SpecializedSROA::runOnOperation
void runOnOperation() override
Definition
SpecializedMemoryPasses.h:53
llzk::SpecializedSROA::getArgument
mlir::StringRef getArgument() const override
Definition
SpecializedMemoryPasses.h:47
include
llzk
Transforms
SpecializedMemoryPasses.h
Generated by
1.17.0
Copyright 2025 Veridise Inc. under the Apache License v2.0. Copyright 2026 Project LLZK under the Apache License v2.0.