LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
Builders.cpp
Go to the documentation of this file.
1//===-- Builders.cpp - Operation builder implementations --------*- 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//===----------------------------------------------------------------------===//
9
11
13
14#include <mlir/IR/Builders.h>
15#include <mlir/Support/LLVM.h>
16
17#include <llvm/Support/ErrorHandling.h>
18
19namespace llzk {
20
21using namespace mlir;
22using namespace component;
23using namespace function;
24using namespace polymorphic;
25
26OwningOpRef<ModuleOp> createLLZKModule(MLIRContext * /*context*/, Location loc) {
27 auto mod = ModuleOp::create(loc);
29 return mod;
30}
31
33 MLIRContext *ctx = mod.getContext();
34 if (auto *dialect = ctx->getOrLoadDialect<LLZKDialect>()) {
35 mod->setAttr(LANG_ATTR_NAME, StringAttr::get(ctx, dialect->getNamespace()));
36 } else {
37 llvm::report_fatal_error("Could not load LLZK dialect!");
38 }
39}
40
41/* ModuleLikeBuilder */
42
43template <typename Derived>
44void ModuleLikeBuilder<Derived>::ensureNoSuchFreeFunc(std::string_view funcName) {
45 if (freeFuncMap.find(funcName) != freeFuncMap.end()) {
46 llvm::report_fatal_error("global function " + Twine(funcName) + " already exists!");
47 }
48}
49
50template <typename Derived>
51void ModuleLikeBuilder<Derived>::ensureFreeFnExists(std::string_view funcName) {
52 if (freeFuncMap.find(funcName) == freeFuncMap.end()) {
53 llvm::report_fatal_error("global function " + Twine(funcName) + " does not exist!");
54 }
55}
56
57template <typename Derived>
58void ModuleLikeBuilder<Derived>::ensureNoSuchStruct(std::string_view structName) {
59 if (structMap.find(structName) != structMap.end()) {
60 llvm::report_fatal_error("struct " + Twine(structName) + " already exists!");
61 }
62}
63
64template <typename Derived>
65void ModuleLikeBuilder<Derived>::ensureStructExists(std::string_view structName) {
66 if (structMap.find(structName) == structMap.end()) {
67 llvm::report_fatal_error("struct " + Twine(structName) + " does not exist!");
68 }
69}
70
71template <typename Derived>
72void ModuleLikeBuilder<Derived>::ensureNoSuchComputeFn(std::string_view structName) {
73 if (computeFnMap.find(structName) != computeFnMap.end()) {
74 llvm::report_fatal_error("struct " + Twine(structName) + " already has a compute function!");
75 }
77
78template <typename Derived>
79void ModuleLikeBuilder<Derived>::ensureComputeFnExists(std::string_view structName) {
80 if (computeFnMap.find(structName) == computeFnMap.end()) {
81 llvm::report_fatal_error("struct " + Twine(structName) + " has no compute function!");
82 }
83}
84
85template <typename Derived>
86void ModuleLikeBuilder<Derived>::ensureNoSuchConstrainFn(std::string_view structName) {
87 if (constrainFnMap.find(structName) != constrainFnMap.end()) {
88 llvm::report_fatal_error("struct " + Twine(structName) + " already has a constrain function!");
89 }
90}
92template <typename Derived>
93void ModuleLikeBuilder<Derived>::ensureConstrainFnExists(std::string_view structName) {
94 if (constrainFnMap.find(structName) == constrainFnMap.end()) {
95 llvm::report_fatal_error("struct " + Twine(structName) + " has no constrain function!");
96 }
97}
98
99template <typename Derived>
100void ModuleLikeBuilder<Derived>::ensureNoSuchProductFn(std::string_view structName) {
101 if (productFnMap.find(structName) != productFnMap.end()) {
102 llvm::report_fatal_error("struct " + Twine(structName) + " already has a product function!");
103 }
104}
105
106template <typename Derived>
107void ModuleLikeBuilder<Derived>::ensureProductFnExists(std::string_view structName) {
108 if (productFnMap.find(structName) == productFnMap.end()) {
109 llvm::report_fatal_error("struct " + Twine(structName) + " has no product function!");
110 }
112
113template <typename Derived>
114Derived &ModuleLikeBuilder<Derived>::insertEmptyStruct(std::string_view structName, Location loc) {
115 ensureNoSuchStruct(structName);
116
117 OpBuilder opBuilder(this->getBodyRegion());
118 auto structDef = opBuilder.create<StructDefOp>(loc, StringAttr::get(context, structName));
119 // populate the initial region
120 (void)structDef.getRegion().emplaceBlock();
121 structMap[structName] = structDef;
122
123 return static_cast<Derived &>(*this);
124}
125
126template <typename Derived>
128 MLIRContext *context = op.getContext();
129 OpBuilder opBuilder(op.getBodyRegion());
130 auto fnOp = opBuilder.create<FuncDefOp>(
131 loc, StringAttr::get(context, FUNC_NAME_COMPUTE),
132 FunctionType::get(context, {}, {op.getType()})
133 );
134 fnOp.setAllowWitnessAttr();
135 fnOp.addEntryBlock();
136 return fnOp;
137}
138
139template <typename Derived>
141 ensureNoSuchComputeFn(op.getName());
142 computeFnMap[op.getName()] = buildComputeFn(op, loc);
143 return static_cast<Derived &>(*this);
144}
145
146template <typename Derived>
147Derived &ModuleLikeBuilder<Derived>::insertComputeFn(std::string_view structName, Location loc) {
148 ensureStructExists(structName);
149 return insertComputeFn(structMap.at(structName), loc);
150}
151
152template <typename Derived>
154 MLIRContext *context = op.getContext();
155 OpBuilder opBuilder(op.getBodyRegion());
156 auto fnOp = opBuilder.create<FuncDefOp>(
157 loc, StringAttr::get(context, FUNC_NAME_CONSTRAIN),
158 FunctionType::get(context, {op.getType()}, {})
159 );
160 fnOp.setAllowConstraintAttr();
161 fnOp.addEntryBlock();
162 return fnOp;
163}
164
165template <typename Derived>
167 ensureNoSuchConstrainFn(op.getName());
168 constrainFnMap[op.getName()] = buildConstrainFn(op, loc);
169 return static_cast<Derived &>(*this);
170}
171
172template <typename Derived>
173Derived &ModuleLikeBuilder<Derived>::insertConstrainFn(std::string_view structName, Location loc) {
174 ensureStructExists(structName);
175 return insertConstrainFn(structMap.at(structName), loc);
176}
177
178template <typename Derived>
180 MLIRContext *context = op.getContext();
181 OpBuilder opBuilder(op.getBodyRegion());
182 auto fnOp = opBuilder.create<FuncDefOp>(
183 loc, StringAttr::get(context, FUNC_NAME_PRODUCT),
184 FunctionType::get(context, {}, {op.getType()})
185 );
186 fnOp.setAllowWitnessAttr();
187 fnOp.setAllowConstraintAttr();
188 fnOp.addEntryBlock();
189 return fnOp;
190}
191
192template <typename Derived>
194 ensureNoSuchProductFn(op.getName());
195 productFnMap[op.getName()] = buildProductFn(op, loc);
196 return static_cast<Derived &>(*this);
197}
198
199template <typename Derived>
200Derived &ModuleLikeBuilder<Derived>::insertProductFn(std::string_view structName, Location loc) {
201 ensureStructExists(structName);
202 return insertProductFn(structMap.at(structName), loc);
203}
204
205template <typename Derived>
207 StructDefOp caller, StructDefOp callee, Location callLoc
208) {
209 ensureComputeFnExists(caller.getName());
210 ensureComputeFnExists(callee.getName());
211
212 auto callerFn = computeFnMap.at(caller.getName());
213 auto calleeFn = computeFnMap.at(callee.getName());
214
215 OpBuilder builder(callerFn.getBody());
216 builder.create<CallOp>(callLoc, calleeFn);
217 return static_cast<Derived &>(*this);
218}
219
220template <typename Derived>
222 std::string_view caller, std::string_view callee, Location callLoc
223) {
224 ensureStructExists(caller);
225 ensureStructExists(callee);
226 return insertComputeCall(structMap.at(caller), structMap.at(callee), callLoc);
227}
229template <typename Derived>
231 StructDefOp caller, StructDefOp callee, Location callLoc, Location memberDefLoc
232) {
233 ensureConstrainFnExists(caller.getName());
234 ensureConstrainFnExists(callee.getName());
235
236 FuncDefOp callerFn = constrainFnMap.at(caller.getName());
237 FuncDefOp calleeFn = constrainFnMap.at(callee.getName());
238 StructType calleeTy = callee.getType();
239
240 size_t numOps = caller.getBody()->getOperations().size();
241 auto memberName = StringAttr::get(context, callee.getName().str() + std::to_string(numOps));
242
243 // Insert the member declaration op
244 {
245 OpBuilder builder(caller.getBodyRegion());
246 builder.create<MemberDefOp>(memberDefLoc, memberName, calleeTy);
247 }
248
249 // Insert the constrain function ops
250 {
251 OpBuilder builder(callerFn.getBody());
252
253 auto member = builder.create<MemberReadOp>(
254 callLoc, calleeTy, callerFn.getSelfValueFromConstrain(), memberName
255 );
256 builder.create<CallOp>(
257 callLoc, TypeRange {}, calleeFn.getFullyQualifiedName(), ValueRange {member}
258 );
259 }
260 return static_cast<Derived &>(*this);
261}
262
263template <typename Derived>
265 std::string_view caller, std::string_view callee, Location callLoc, Location memberDefLoc
266) {
267 ensureStructExists(caller);
268 ensureStructExists(callee);
269 return insertConstrainCall(structMap.at(caller), structMap.at(callee), callLoc, memberDefLoc);
270}
271
272template <typename Derived>
274 std::string_view funcName, FunctionType type, Location loc,
275 llvm::function_ref<void(OpBuilder &)> fnBody
276) {
277 ensureNoSuchFreeFunc(funcName);
278
279 OpBuilder opBuilder(this->getBodyRegion());
280 auto funcDef = opBuilder.create<FuncDefOp>(loc, funcName, type);
281 auto *block = funcDef.addEntryBlock();
282 if (fnBody) {
283 OpBuilder::InsertionGuard guard(opBuilder);
284 opBuilder.setInsertionPointToEnd(block);
285 fnBody(opBuilder);
286 }
287 freeFuncMap[funcName] = funcDef;
288
289 return static_cast<Derived &>(*this);
290}
291
292template <typename Derived>
294 FuncDefOp caller, std::string_view callee, Location callLoc
295) {
296 ensureFreeFnExists(callee);
297 FuncDefOp calleeFn = freeFuncMap.at(callee);
298
299 OpBuilder builder(caller.getBody());
300 builder.create<CallOp>(callLoc, calleeFn);
301 return static_cast<Derived &>(*this);
302}
303
304/* ModuleBuilder */
305
306void ModuleBuilder::ensureNoSuchTemplate(std::string_view templateName) {
307 if (templateMap.find(templateName) != templateMap.end()) {
308 llvm::report_fatal_error("template " + Twine(templateName) + " already exists!");
309 }
310}
311
312void ModuleBuilder::ensureTemplateExists(std::string_view templateName) {
313 if (templateMap.find(templateName) == templateMap.end()) {
314 llvm::report_fatal_error("template " + Twine(templateName) + " does not exist!");
315 }
316}
317
319ModuleBuilder::insertTemplate(std::string_view templateName, Location loc, unsigned numParams) {
320 ensureNoSuchTemplate(templateName);
321
322 OpBuilder opBuilder(myModule.getBodyRegion());
323 auto templateDef = opBuilder.create<TemplateOp>(loc, StringAttr::get(context, templateName));
324 opBuilder.setInsertionPointToStart(&templateDef.getBodyRegion().emplaceBlock());
325 for (unsigned i = 0; i < numParams; ++i) {
326 opBuilder.create<TemplateParamOp>(
327 loc, StringAttr::get(context, 'T' + std::to_string(i)), TypeAttr()
328 );
329 }
330
331 auto key = templateDef.getName();
332 templateMap.emplace(key, std::make_unique<TemplateBuilder>(templateDef));
333
334 return *this;
335}
336
337void ModuleBuilder::ensureNoSuchNestedModule(std::string_view moduleName) {
338 if (nestedModuleMap.find(moduleName) != nestedModuleMap.end()) {
339 llvm::report_fatal_error("nested module " + Twine(moduleName) + " already exists!");
340 }
341}
342
343void ModuleBuilder::ensureNestedModuleExists(std::string_view moduleName) {
344 if (nestedModuleMap.find(moduleName) == nestedModuleMap.end()) {
345 llvm::report_fatal_error("nested module " + Twine(moduleName) + " does not exist!");
346 }
347}
348
349ModuleBuilder &ModuleBuilder::insertNestedModule(std::string_view moduleName, Location loc) {
350 ensureNoSuchNestedModule(moduleName);
351
352 OpBuilder opBuilder(myModule.getBodyRegion());
353 auto nestedMod = opBuilder.create<ModuleOp>(loc);
354 nestedMod.setSymName(moduleName);
355
356 auto key = *nestedMod.getSymName();
357 nestedModuleMap.emplace(key, std::make_unique<ModuleBuilder>(nestedMod));
358
359 return *this;
360}
361
362/* Explicit template instantiations */
363
366
367} // namespace llzk
mlir::MLIRContext * context
Definition Builders.h:37
Builds out a LLZK-compliant module and provides utilities for populating that module.
Definition Builders.h:393
ModuleBuilder & insertTemplate(std::string_view templateName, mlir::Location loc, unsigned numParams=0)
ModuleBuilder & insertNestedModule(std::string_view moduleName, mlir::Location loc)
Derived & insertComputeFn(component::StructDefOp op, mlir::Location loc)
Derived & insertConstrainCall(component::StructDefOp caller, component::StructDefOp callee, mlir::Location callLoc, mlir::Location memberDefLoc)
To call a constraint function, you must:
std::unordered_map< std::string_view, function::FuncDefOp > productFnMap
Definition Builders.h:61
void ensureNoSuchFreeFunc(std::string_view funcName)
Ensure that a global function with the given funcName has not been added, reporting a fatal error oth...
Definition Builders.cpp:44
std::unordered_map< std::string_view, function::FuncDefOp > constrainFnMap
Definition Builders.h:59
Derived & insertEmptyStruct(std::string_view structName, mlir::Location loc)
static function::FuncDefOp buildComputeFn(component::StructDefOp op, mlir::Location loc)
compute returns the type of the struct that defines it.
Definition Builders.cpp:127
Derived & insertComputeCall(component::StructDefOp caller, component::StructDefOp callee, mlir::Location callLoc)
Only requirement for compute is the call itself.
void ensureProductFnExists(std::string_view structName)
Ensure that the given struct has a product function, reporting a fatal error otherwise.
Definition Builders.cpp:107
void ensureFreeFnExists(std::string_view funcName)
Ensure that a global function with the given funcName has been added, reporting a fatal error otherwi...
Definition Builders.cpp:51
Derived & insertProductFn(component::StructDefOp op, mlir::Location loc)
Derived & insertConstrainFn(component::StructDefOp op, mlir::Location loc)
Derived & insertFreeFunc(std::string_view funcName, ::mlir::FunctionType type, mlir::Location loc, llvm::function_ref< void(mlir::OpBuilder &)> fnBody=nullptr)
void ensureNoSuchProductFn(std::string_view structName)
Ensure that the given struct does not have a product function, reporting a fatal error otherwise.
Definition Builders.cpp:100
void ensureStructExists(std::string_view structName)
Ensure that a struct with the given structName exists, reporting a fatal error otherwise.
Definition Builders.cpp:65
void ensureNoSuchConstrainFn(std::string_view structName)
Ensure that the given struct does not have a constrain function, reporting a fatal error otherwise.
Definition Builders.cpp:86
Derived & insertFreeCall(function::FuncDefOp caller, std::string_view callee, mlir::Location callLoc)
std::unordered_map< std::string_view, function::FuncDefOp > freeFuncMap
Definition Builders.h:53
void ensureNoSuchComputeFn(std::string_view structName)
Ensure that the given struct does not have a compute function, reporting a fatal error otherwise.
Definition Builders.cpp:72
void ensureComputeFnExists(std::string_view structName)
Ensure that the given struct has a compute function, reporting a fatal error otherwise.
Definition Builders.cpp:79
void ensureConstrainFnExists(std::string_view structName)
Ensure that the given struct has a constrain function, reporting a fatal error otherwise.
Definition Builders.cpp:93
static function::FuncDefOp buildConstrainFn(component::StructDefOp op, mlir::Location loc)
constrain accepts the struct type as the first argument.
Definition Builders.cpp:153
std::unordered_map< std::string_view, function::FuncDefOp > computeFnMap
Definition Builders.h:57
std::unordered_map< std::string_view, component::StructDefOp > structMap
Definition Builders.h:55
void ensureNoSuchStruct(std::string_view structName)
Ensure that a struct with the given structName has not been added, reporting a fatal error otherwise.
Definition Builders.cpp:58
static function::FuncDefOp buildProductFn(component::StructDefOp op, mlir::Location loc)
product returns the type of the struct that defines it.
Definition Builders.cpp:179
StructType getType(::std::optional<::mlir::ArrayAttr > constParams={})
Gets the StructType representing this struct.
::mlir::Region & getBodyRegion()
Definition Ops.h.inc:1194
::mlir::Value getSelfValueFromConstrain()
Return the "self" value (i.e.
Definition Ops.cpp:492
::mlir::SymbolRefAttr getFullyQualifiedName(bool requireParent=true)
Return the full name for this function from the root module, including all surrounding symbol table n...
Definition Ops.cpp:469
::mlir::Region & getBody()
Definition Ops.h.inc:698
constexpr char FUNC_NAME_COMPUTE[]
Symbol name for the witness generation (and resp.
Definition Constants.h:16
ExpressionValue mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
constexpr char LANG_ATTR_NAME[]
Name of the attribute on the top-level ModuleOp that identifies the ModuleOp as the root module and s...
Definition Constants.h:23
constexpr char FUNC_NAME_CONSTRAIN[]
Definition Constants.h:17
constexpr char FUNC_NAME_PRODUCT[]
Definition Constants.h:18
OwningOpRef< ModuleOp > createLLZKModule(MLIRContext *, Location loc)
Definition Builders.cpp:26
void addLangAttrForLLZKDialect(ModuleOp mod)
Definition Builders.cpp:32