LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
LLZKTransformationPassPipelines.cpp
Go to the documentation of this file.
1//===-- LLZKTransformationPassPipelines.cpp ---------------------*- C++ -*-===//
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//===----------------------------------------------------------------------===//
13//===----------------------------------------------------------------------===//
14
16
19
20#include <mlir/Pass/PassManager.h>
21#include <mlir/Pass/PassRegistry.h>
22#include <mlir/Transforms/Passes.h>
23
24#include <utility>
25
26using namespace mlir;
27
28namespace llzk {
29
30//===----------------------------------------------------------------------===//
31// Pipeline implementation.
32//===----------------------------------------------------------------------===//
33
34namespace {
35
36template <typename NestedPassOptionT>
37inline std::unique_ptr<Pass> createConfiguredPass(const NestedPassOptionT &options) {
38 return options.getValue().createPass();
39}
40
41void buildFullStructInliningPipelineImpl(
42 OpPassManager &pm, polymorphic::FlatteningPassOptions flattening, bool arrayToScalar,
43 bool podToScalar, std::unique_ptr<Pass> inliningPass
44) {
45 // default to `main-as-root` if unspecified to avoid leaving parameterized templates
46 // that cause the later struct inlining pass to crash
47 if (flattening.cleanupMode == polymorphic::FlatteningCleanupMode::Unspecified) {
49 }
50
51 // Circom-style loops are commonly emitted as scf.while operations whose bounds are
52 // field constants converted to index values. Normalize the loops and fold those
53 // conversions before flattening so the unroller can determine their trip counts.
54 pm.addPass(createWhileToForPass());
55 pm.addPass(mlir::createCanonicalizerPass());
56 pm.addPass(polymorphic::createFlatteningPass(flattening));
57
58 // Unrolling substitutes constant induction variables into array accesses. Materialize
59 // those constant indices before array-to-scalar requires them to be attributes.
60 pm.addPass(mlir::createCanonicalizerPass());
61
62 // Run pod-to-scalar first because it is able to split `pod.type` used as array element type
63 // (into parallel arrays) so it should be able to fully remove all `pod.type` usages.
64 if (podToScalar) {
65 pm.addPass(pod::createPodToScalarPass());
66 }
67 if (arrayToScalar) {
69 }
70 // Canonicalize to remove known-condition `scf.if` regions so struct inlining
71 // can link "@compute" calls to struct members.
72 pm.addPass(mlir::createCanonicalizerPass());
73 pm.addPass(std::move(inliningPass));
74
75 // Remove struct and member definitions that are no longer used after inlining.
77 UnusedDeclarationEliminationPassOptions {.removeStructs = true}
78 ));
79}
80
81void buildFullPolyLoweringPipelineImpl(
82 OpPassManager &pm, polymorphic::FlatteningPassOptions flattening, bool arrayToScalar,
83 bool podToScalar, std::unique_ptr<Pass> inliningPass, std::unique_ptr<Pass> polyLoweringPass
84) {
85 // 1. Struct flattening and inlining
86 buildFullStructInliningPipelineImpl(
87 pm, flattening, arrayToScalar, podToScalar, std::move(inliningPass)
88 );
89 // 2. Degree lowering
90 pm.addPass(std::move(polyLoweringPass));
91 // 3. Cleanup
93}
94
95} // namespace
96
101
106
107void buildProductProgramPipeline(OpPassManager &pm) {
110}
111
113 buildFullStructInliningPipelineImpl(
114 pm, cfg.flattening, cfg.arrayToScalar, cfg.podToScalar,
116 );
117}
118
119void buildFullInliningPipeline(OpPassManager &pm, const FullStructInliningConfig &cfg) {
120 // Inline free-function bodies before struct cleanup so their struct uses
121 // participate in flattening and keep the referenced definitions alive.
122 pm.addPass(createInlineFreeFunctionsPass());
123 buildFullStructInliningPipelineImpl(
124 pm, cfg.flattening, cfg.arrayToScalar, cfg.podToScalar,
126 );
127}
128
129void buildFullPolyLoweringPipeline(OpPassManager &pm, const FullPolyLoweringConfig &cfg) {
130 buildFullPolyLoweringPipelineImpl(
135 );
136}
137
138//===----------------------------------------------------------------------===//
139// Pipeline registration.
140//===----------------------------------------------------------------------===//
141
142void buildConstraintFlatteningPipeline(OpPassManager &pm) {
143 // Inline frontend execute_region wrappers before specialization discovery.
144 pm.addPass(mlir::createCanonicalizerPass());
147 // Fold specialized constants and remove empty branches before expansion.
148 pm.addPass(mlir::createCanonicalizerPass());
150}
151
153 PassPipelineRegistration<>(
154 "llzk-constraint-flattening",
155 "Monomorphize templates, expose child members, and expand main constrain",
157 );
158
159 PassPipelineRegistration<>(
160 "llzk-remove-unnecessary-ops",
161 "Remove unnecessary operations, such as redundant reads or repeated constraints",
163 );
164
165 PassPipelineRegistration<>(
166 "llzk-remove-unnecessary-ops-and-defs",
167 "Remove unnecessary operations, member definitions, and struct definitions",
169 );
170
171 PassPipelineRegistration<>(
172 "llzk-product-program",
173 "Convert @compute/@constrain functions to @product function and perform alignment",
175 );
176
177 PassPipelineRegistration<FullStructInliningOptions>(
178 "llzk-full-struct-inlining",
179 "Run flattening and inlining of all struct definitions into the `main` struct. This "
180 "pipeline uses the `main-as-root` cleanup mode in the flattening pass by default. It "
181 "is not recommended to override this cleanup mode because other cleanup modes may "
182 "leave behind parameterized templates that later cause `llzk-inline-structs` to crash.",
183 [](OpPassManager &pm, const FullStructInliningOptions &opts) {
184 auto flattening = opts.flattening.getValue().createOptions();
185 buildFullStructInliningPipelineImpl(
186 pm, flattening->createPassOptions(), opts.arrayToScalar, opts.podToScalar,
187 createConfiguredPass(opts.inlining)
188 );
189 }
190 );
191
192 PassPipelineRegistration<FullStructInliningOptions>(
193 "llzk-full-inlining",
194 "Run free function inlining, flattening, and struct inlining. This is the "
195 "recommended pipeline before any downstream pass that does not understand `function.call`.",
196 [](OpPassManager &pm, const FullStructInliningOptions &opts) {
197 auto flattening = opts.flattening.getValue().createOptions();
198 // Inline free-function bodies before struct cleanup so their struct uses
199 // participate in flattening and keep the referenced definitions alive.
200 pm.addPass(createInlineFreeFunctionsPass());
201 buildFullStructInliningPipelineImpl(
202 pm, flattening->createPassOptions(), opts.arrayToScalar, opts.podToScalar,
203 createConfiguredPass(opts.inlining)
204 );
205 }
206 );
207
208 PassPipelineRegistration<FullPolyLoweringOptions>(
209 "llzk-full-poly-lowering",
210 "Run flattening and inlining of all struct definitions into the `main` struct, then lower "
211 "polynomial constraints to a given max degree, and finally remove unnecessary operations and "
212 "definitions. This pipeline uses the `main-as-root` cleanup mode in the flattening pass by "
213 "default. It is not recommended to override this cleanup mode because other cleanup modes "
214 "may leave behind parameterized templates that later cause `llzk-inline-structs` to crash.",
215 [](OpPassManager &pm, const FullPolyLoweringOptions &opts) {
216 auto structInlining = opts.structInlining.getValue().createOptions();
217 auto flattening = structInlining->flattening.getValue().createOptions();
218 buildFullPolyLoweringPipelineImpl(
219 pm, flattening->createPassOptions(), structInlining->arrayToScalar,
220 structInlining->podToScalar, createConfiguredPass(structInlining->inlining),
221 createConfiguredPass(opts.polyLowering)
222 );
223 }
224 );
225}
226
227} // namespace llzk
std::unique_ptr<::mlir::Pass > createArrayToScalarPass()
std::unique_ptr<::mlir::Pass > createExposeChildMembersPass()
std::unique_ptr<::mlir::Pass > createInlineStructsPass()
std::unique_ptr<::mlir::Pass > createPodToScalarPass()
std::unique_ptr<::mlir::Pass > createConstraintFlatteningPass()
std::unique_ptr<::mlir::Pass > createTemplateMonomorphizationPass()
std::unique_ptr<::mlir::Pass > createFlatteningPass()
void buildRemoveUnnecessaryOpsAndDefsPipeline(mlir::OpPassManager &pm)
std::unique_ptr<::mlir::Pass > createInlineFreeFunctionsPass()
std::unique_ptr<::mlir::Pass > createFuseProductControlFlowPass()
std::unique_ptr<::mlir::Pass > createPolyLoweringPass()
std::unique_ptr<::mlir::Pass > createWhileToForPass()
std::unique_ptr<::mlir::Pass > createUnusedDeclarationEliminationPass()
void buildProductProgramPipeline(OpPassManager &pm)
void buildFullStructInliningPipeline(OpPassManager &pm, const FullStructInliningConfig &cfg)
void buildFullPolyLoweringPipeline(OpPassManager &pm, const FullPolyLoweringConfig &cfg)
void buildConstraintFlatteningPipeline(OpPassManager &pm)
std::unique_ptr<::mlir::Pass > createComputeConstrainToProductPass()
std::unique_ptr<::mlir::Pass > createRedundantOperationEliminationPass()
void buildFullInliningPipeline(OpPassManager &pm, const FullStructInliningConfig &cfg)
std::unique_ptr<::mlir::Pass > createRedundantReadAndWriteEliminationPass()
void buildRemoveUnnecessaryOpsPipeline(mlir::OpPassManager &pm)
Pure C++ configuration for the full polynomial lowering pipeline.
CLI Option configuration for the full polynomial lowering pipeline.
Pure C++ configuration for the full struct inlining pipeline.
polymorphic::FlatteningPassOptions flattening
component::InlineStructsPassOptions inlining
CLI Option configuration for the full struct inlining pipeline.