LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
ExecutionEngineBackend.cpp
Go to the documentation of this file.
1//===-- ExecutionEngineBackend.cpp - llzk-witgen JIT backend ----*- 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//===----------------------------------------------------------------------===//
9
11
12#include "Errors.h"
13#include "JSON.h"
14#include "ValueModel.h"
15#include "WitgenLowering.h"
16#include "WitgenUtils.h"
17#include "WitnessSelection.h"
18
22#include "llzk/Util/Compare.h"
25
26#include <mlir/Conversion/AffineToStandard/AffineToStandard.h>
27#include <mlir/Conversion/ArithToLLVM/ArithToLLVM.h>
28#include <mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h>
29#include <mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h>
30#include <mlir/Conversion/IndexToLLVM/IndexToLLVM.h>
31#include <mlir/Conversion/MemRefToLLVM/MemRefToLLVM.h>
32#include <mlir/Conversion/Passes.h>
33#include <mlir/Conversion/SCFToControlFlow/SCFToControlFlow.h>
34#include <mlir/Conversion/UBToLLVM/UBToLLVM.h>
35#include <mlir/Conversion/VectorToLLVM/ConvertVectorToLLVM.h>
36#include <mlir/Dialect/LLVMIR/Transforms/InlinerInterfaceImpl.h>
37#include <mlir/Dialect/MemRef/Transforms/Passes.h>
38#include <mlir/Dialect/Utils/IndexingUtils.h>
39#include <mlir/ExecutionEngine/CRunnerUtils.h>
40#include <mlir/ExecutionEngine/ExecutionEngine.h>
41#include <mlir/Pass/PassManager.h>
42#include <mlir/Target/LLVMIR/Dialect/All.h>
43#include <mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h>
44#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
45#include <mlir/Transforms/Passes.h>
46
47#include <llvm/ADT/APInt.h>
48#include <llvm/Support/Endian.h>
49#include <llvm/Support/MathExtras.h>
50#include <llvm/Support/TargetSelect.h>
51#include <llvm/Support/raw_ostream.h>
52
53#include <cstdint>
54#include <cstdio>
55#include <cstdlib>
56
57using namespace mlir;
58
59namespace llzk::witgen {
60
61namespace {
62
64[[noreturn]] static void abortWithFlushedStdout() {
65 // cf.assert lowers to puts followed by abort, which may not flush stdout.
66 std::fflush(stdout);
67 std::abort();
68}
69
71struct BufferPack {
72 Type originalType;
73 unsigned feltBitWidth = 0;
74 size_t elemBytes = 0;
75 std::vector<int64_t> shape;
76 std::vector<int64_t> strides;
77 std::vector<uint8_t> descriptor;
78 std::vector<uint8_t> storage;
79};
80
82static size_t getElementBytes(unsigned bitWidth) { return (bitWidth + 7U) / 8U; }
83
85static llvm::Expected<std::vector<int64_t>> getBoundaryShape(Type type) {
86 if (isa<felt::FeltType>(type)) {
87 return std::vector<int64_t> {1};
88 }
89 if (auto arrayType = dyn_cast<array::ArrayType>(type)) {
90 if (!isa<felt::FeltType>(arrayType.getElementType())) {
91 return makeError(
92 "execution-engine backend only supports arrays of felt values at the main boundary"
93 );
94 }
95 if (!arrayType.hasStaticShape()) {
96 return makeError(
97 "execution-engine backend only supports statically shaped arrays at the main boundary"
98 );
99 }
100 return std::vector<int64_t>(arrayType.getShape().begin(), arrayType.getShape().end());
101 }
102 return makeError(
103 "execution-engine backend only supports felt and array<...xfelt> main boundaries"
104 );
105}
106
108static llvm::Expected<std::vector<int64_t>> computeStaticStrides(ArrayRef<int64_t> shape) {
109 for (int64_t dim : shape) {
110 auto checkedDim = checkedShapeDimToSize(dim, "execution-engine buffer shape");
111 if (!checkedDim) {
112 return checkedDim.takeError();
113 }
114 (void)*checkedDim;
115 }
116 auto strides = mlir::computeStrides(shape);
117 return std::vector<int64_t>(strides.begin(), strides.end());
118}
119
121static llvm::Error buildDescriptor(BufferPack &buffer) {
122 auto rank = checkedCast<int64_t>(buffer.shape.size());
123 if (!rank) {
124 return rank.takeError();
125 }
126 constexpr int64_t descriptorSize = sizeof(void *) * 2;
127 auto shapeAndStrideCount = llvm::DynamicAPInt(1) + *rank + *rank;
128 auto dynamicPart = static_cast<int64_t>(sizeof(int64_t)) * shapeAndStrideCount;
129 auto totalSize = descriptorSize + dynamicPart;
130 auto checkedTotalSize = checkedToSize(totalSize, "execution-engine memref descriptor");
131 if (!checkedTotalSize) {
132 return checkedTotalSize.takeError();
133 }
134 buffer.descriptor.resize(*checkedTotalSize);
135 uint8_t *cursor = buffer.descriptor.data();
136 uint8_t *base = buffer.storage.data();
137 std::memcpy(cursor, static_cast<const void *>(&base), sizeof(void *));
138 cursor += sizeof(void *);
139 std::memcpy(cursor, static_cast<const void *>(&base), sizeof(void *));
140 cursor += sizeof(void *);
141 const int64_t offset = 0;
142 std::memcpy(cursor, &offset, sizeof(int64_t));
143 cursor += sizeof(int64_t);
144 for (int64_t size : buffer.shape) {
145 std::memcpy(cursor, &size, sizeof(int64_t));
146 cursor += sizeof(int64_t);
147 }
148 for (int64_t stride : buffer.strides) {
149 std::memcpy(cursor, &stride, sizeof(int64_t));
150 cursor += sizeof(int64_t);
151 }
152 return llvm::Error::success();
153}
154
156static llvm::Expected<BufferPack> createBufferPack(Type type, const Field &field) {
157 auto shape = getBoundaryShape(type);
158 if (!shape) {
159 return shape.takeError();
160 }
161 BufferPack buffer;
162 buffer.originalType = type;
163 buffer.feltBitWidth = field.bitWidth();
164 buffer.elemBytes = getElementBytes(buffer.feltBitWidth);
165 buffer.shape = std::move(*shape);
166 auto strides = computeStaticStrides(buffer.shape);
167 if (!strides) {
168 return strides.takeError();
169 }
170 buffer.strides = std::move(*strides);
171 auto elementCount = getStaticShapeElementCount(buffer.shape, "execution-engine buffer storage");
172 if (!elementCount) {
173 return elementCount.takeError();
174 }
175 bool overflow = false;
176 size_t storageBytes = llvm::SaturatingMultiply(*elementCount, buffer.elemBytes, &overflow);
177 if (overflow) {
178 return makeError("execution-engine buffer storage would overflow size_t");
179 }
180 buffer.storage.resize(storageBytes);
181 if (auto error = buildDescriptor(buffer)) {
182 return std::move(error);
183 }
184 return buffer;
185}
186
188static llvm::Error
189storeElement(BufferPack &buffer, size_t flatIndex, const llvm::DynamicAPInt &value) {
190 bool overflow = false;
191 size_t byteOffset = llvm::SaturatingMultiply(flatIndex, buffer.elemBytes, &overflow);
192 if (overflow) {
193 return makeError("execution-engine buffer store offset would overflow size_t");
194 }
195 auto elemBytesU = checkedCast<unsigned>(buffer.elemBytes);
196 if (!elemBytesU) {
197 return elemBytesU.takeError();
198 }
199 auto raw = checkedToAPInt(value, buffer.feltBitWidth, false);
200 if (!raw) {
201 return raw.takeError();
202 }
203 llvm::StoreIntToMemory(*raw, buffer.storage.data() + byteOffset, *elemBytesU);
204 return llvm::Error::success();
205}
206
208static llvm::Expected<llvm::DynamicAPInt> loadElement(const BufferPack &buffer, size_t flatIndex) {
209 bool overflow = false;
210 size_t byteOffset = llvm::SaturatingMultiply(flatIndex, buffer.elemBytes, &overflow);
211 if (overflow) {
212 return makeError("execution-engine buffer load offset would overflow size_t");
213 }
214 auto elemBytesU = checkedCast<unsigned>(buffer.elemBytes);
215 if (!elemBytesU) {
216 return elemBytesU.takeError();
217 }
218 llvm::APInt raw(buffer.feltBitWidth, 0);
219 llvm::LoadIntFromMemory(raw, buffer.storage.data() + byteOffset, *elemBytesU);
220 return toDynamicAPInt(raw);
221}
222
224static llvm::Error fillInputBuffer(BufferPack &buffer, const WitnessVal &value) {
225 if (isa<felt::FeltType>(buffer.originalType)) {
226 auto feltValue = asFelt(value);
227 if (!feltValue) {
228 return feltValue.takeError();
229 }
230 return storeElement(buffer, 0, *feltValue);
231 }
232
233 auto arrayValue = asArray(value);
234 if (!arrayValue) {
235 return arrayValue.takeError();
236 }
237 auto elementCount = getStaticShapeElementCount(buffer.shape, "execution-engine input array");
238 if (!elementCount) {
239 return elementCount.takeError();
240 }
241 if ((*arrayValue)->elements.size() != *elementCount) {
242 return makeError("input array element count mismatch");
243 }
244 for (size_t i = 0; i < (*arrayValue)->elements.size(); ++i) {
245 auto feltValue = asFelt((*arrayValue)->elements[i]);
246 if (!feltValue) {
247 return feltValue.takeError();
248 }
249 if (auto err = storeElement(buffer, i, *feltValue)) {
250 return err;
251 }
252 }
253 return llvm::Error::success();
254}
255
257static llvm::Expected<llvm::json::Value>
258feltElementToJSON(const BufferPack &buffer, size_t flatIndex, const Field &field) {
259 auto element = loadElement(buffer, flatIndex);
260 if (!element) {
261 return element.takeError();
262 }
263 return serializeFeltToJSON(*element, field);
264}
265
267static llvm::Expected<llvm::json::Value> bufferToJSONArray(
268 const BufferPack &buffer, const Field &field, size_t dimIndex, size_t flatOffset
269) {
270 if (dimIndex == SIZE_MAX) {
271 return makeError("execution-engine JSON output would overflow size_t");
272 }
273 auto dimSize = checkedShapeDimToSize(buffer.shape[dimIndex], "execution-engine JSON output");
274 if (!dimSize) {
275 return dimSize.takeError();
276 }
277 if (dimIndex + 1 == buffer.shape.size()) {
278 llvm::json::Array result;
279 for (size_t i = 0; i < *dimSize; ++i) {
280 bool overflow = false;
281 size_t elementOffset = llvm::SaturatingAdd(i, flatOffset, &overflow);
282 if (overflow) {
283 return makeError("execution-engine JSON output would overflow size_t");
284 }
285 auto element = feltElementToJSON(buffer, elementOffset, field);
286 if (!element) {
287 return element.takeError();
288 }
289 result.push_back(*element);
290 }
291 return llvm::json::Value(std::move(result));
292 }
293
294 auto subArraySize = getStaticShapeElementCount(
295 llvm::ArrayRef<int64_t>(buffer.shape).drop_front(dimIndex + 1), "execution-engine JSON output"
296 );
297 if (!subArraySize) {
298 return subArraySize.takeError();
299 }
300
301 llvm::json::Array result;
302 for (size_t i = 0; i < *dimSize; ++i) {
303 bool overflow = false;
304 size_t nextOffset = llvm::SaturatingMultiplyAdd(i, *subArraySize, flatOffset, &overflow);
305 if (overflow) {
306 return makeError("execution-engine JSON output would overflow size_t");
307 }
308 auto subArray = bufferToJSONArray(buffer, field, dimIndex + 1, nextOffset);
309 if (!subArray) {
310 return subArray.takeError();
311 }
312 result.push_back(*subArray);
313 }
314 return llvm::json::Value(std::move(result));
315}
316
318static llvm::Expected<llvm::json::Value>
319bufferToJSON(const BufferPack &buffer, const Field &field) {
320 if (isa<felt::FeltType>(buffer.originalType)) {
321 return feltElementToJSON(buffer, 0, field);
322 }
323 return bufferToJSONArray(buffer, field, 0, 0);
324}
325
327static llvm::Expected<OwningOpRef<ModuleOp>> buildExecutionEngineModule(
328 ModuleOp moduleOp, OutputScope outputScope, const WitgenOptions &options
329) {
330 OwningOpRef<ModuleOp> cloned = cast<ModuleOp>(moduleOp->clone());
331 PassManager pm(cloned->getContext());
332 pm.addPass(createLowerComputeToCorePass(options));
333 pm.addPass(createCreateWitgenEntryPass(outputScope));
334 if (failed(pm.run(*cloned))) {
335 return makeError("failed to lower LLZK compute IR to execution-engine core dialects");
336 }
337 return cloned;
338}
339
341static llvm::Error finalizeExecutionEngineModule(ModuleOp moduleOp) {
342 PassManager pm(moduleOp.getContext());
343 pm.addPass(mlir::createCanonicalizerPass());
344 pm.addPass(mlir::createCSEPass());
345 pm.addPass(mlir::memref::createExpandStridedMetadataPass());
346 pm.addPass(mlir::createLowerAffinePass());
347 pm.addPass(mlir::createSCFToControlFlowPass());
348 pm.addPass(mlir::createCanonicalizerPass());
349 pm.addPass(mlir::createCSEPass());
350 pm.addPass(mlir::createConvertToLLVMPass());
351 pm.addPass(mlir::createReconcileUnrealizedCastsPass());
352 if (failed(pm.run(moduleOp))) {
353 return makeError("failed to lower execution-engine module to LLVM dialect");
354 }
355 return llvm::Error::success();
356}
357
359static void maybeDumpModule(ModuleOp moduleOp, bool enabled, llvm::StringRef title) {
360 if (!enabled) {
361 return;
362 }
363 llvm::errs() << title << ":\n";
364 moduleOp.print(llvm::errs());
365 llvm::errs() << '\n';
366}
367
368} // namespace
369
371llvm::Expected<llvm::json::Value> runWithExecutionEngine(
372 ModuleOp moduleOp, SymbolTableCollection &tables, const Field &field,
373 const llvm::json::Value &input, const WitgenOptions &options
374) {
375 auto mainDef = getMainInstanceDef(tables, moduleOp.getOperation());
376 if (failed(mainDef) || !mainDef.value()) {
377 return makeError("module is missing a concrete llzk.main struct");
378 }
379 function::FuncDefOp computeFunc = mainDef->get().getComputeFuncOp();
380 if (!computeFunc) {
381 return makeError("main struct is missing @compute");
382 }
383
385 Interpreter interpreter(
386 moduleOp, tables, field, options.uninitializedBehavior, makeDefaultValueRng(options)
387 );
388 interpreter.setOutputScope(options.outputScope);
389 return interpreter.runMainFromJSON(input);
390 }
391
392 auto parsedArgs = [&]() -> llvm::Expected<llvm::SmallVector<WitnessVal>> {
393 llvm::SmallVector<WitnessVal> args;
394 const auto *jsonObject = input.getAsObject();
395 const auto *jsonArray = input.getAsArray();
396 if (!jsonObject && !jsonArray) {
397 return makeError("inputs JSON must be either an object or an array");
398 }
399 if (jsonObject) {
400 for (unsigned i = 0; i < computeFunc.getNumArguments(); ++i) {
401 std::optional<StringAttr> argName = computeFunc.getArgNameAttr(i);
402 if (!argName) {
403 return makeError("JSON object input requires function.arg_name on every main argument");
404 }
405 const llvm::json::Value *value = jsonObject->get(argName->getValue());
406 if (!value) {
407 return makeError(llvm::Twine("missing JSON input field: ") + argName->getValue());
408 }
409 auto parsed = parseJSONValue(
410 value, computeFunc.getArgumentTypes()[i], field, computeFunc.getOperation()
411 );
412 if (!parsed) {
413 return parsed.takeError();
414 }
415 args.push_back(*parsed);
416 }
417 return args;
418 }
419 if (jsonArray->size() != computeFunc.getNumArguments()) {
420 return makeError("JSON positional input length does not match main compute arity");
421 }
422 for (unsigned i = 0; i < computeFunc.getNumArguments(); ++i) {
423 auto parsed = parseJSONValue(
424 &(*jsonArray)[i], computeFunc.getArgumentTypes()[i], field, computeFunc.getOperation()
425 );
426 if (!parsed) {
427 return parsed.takeError();
428 }
429 args.push_back(*parsed);
430 }
431 return args;
432 }();
433 if (!parsedArgs) {
434 return parsedArgs.takeError();
435 }
436
437 auto inputBindings = collectInputBindings(computeFunc);
438 auto outputs = collectOutputBindings(
439 mainDef->get(), tables, computeFunc.getOperation(), options.outputScope
440 );
441 if (failed(outputs)) {
442 return makeError("failed to select witness outputs for execution-engine mode");
443 }
444
445 llvm::SmallVector<BufferPack> inputBuffers;
446 for (auto [argType, parsed] : llvm::zip(computeFunc.getArgumentTypes(), *parsedArgs)) {
447 auto buffer = createBufferPack(argType, field);
448 if (!buffer) {
449 return buffer.takeError();
450 }
451 if (auto err = fillInputBuffer(*buffer, parsed)) {
452 return std::move(err);
453 }
454 inputBuffers.push_back(std::move(*buffer));
455 }
456
457 llvm::SmallVector<BufferPack> outputBuffers;
458 for (const OutputBinding &output : *outputs) {
459 auto buffer = createBufferPack(output.type, field);
460 if (!buffer) {
461 return buffer.takeError();
462 }
463 outputBuffers.push_back(std::move(*buffer));
464 }
465
466 auto loweredModule = buildExecutionEngineModule(moduleOp, options.outputScope, options);
467 if (!loweredModule) {
468 return loweredModule.takeError();
469 }
470 {
471 DialectRegistry registry;
472 // WTNS lowering may run the inliner in this context after LLVM is loaded.
473 mlir::LLVM::registerInlinerInterface(registry);
474 mlir::arith::registerConvertArithToLLVMInterface(registry);
475 mlir::cf::registerConvertControlFlowToLLVMInterface(registry);
476 mlir::registerConvertFuncToLLVMInterface(registry);
477 mlir::index::registerConvertIndexToLLVMInterface(registry);
478 mlir::registerConvertMemRefToLLVMInterface(registry);
479 mlir::ub::registerConvertUBToLLVMInterface(registry);
480 mlir::vector::registerConvertVectorToLLVMInterface(registry);
481 (*loweredModule)->getContext()->appendDialectRegistry(registry);
482 (*loweredModule)->getContext()->loadAllAvailableDialects();
483 }
484 maybeDumpModule(**loweredModule, options.dumpJITCore, "llzk-witgen JIT core");
485 if (auto err = finalizeExecutionEngineModule(**loweredModule)) {
486 return std::move(err);
487 }
488 maybeDumpModule(**loweredModule, options.dumpJITLLVM, "llzk-witgen JIT LLVM");
489
490 llvm::InitializeNativeTarget();
491 llvm::InitializeNativeTargetAsmPrinter();
492 mlir::registerBuiltinDialectTranslation(*(*loweredModule)->getContext());
493 mlir::registerLLVMDialectTranslation(*(*loweredModule)->getContext());
494
495 auto maybeEngine = mlir::ExecutionEngine::create(loweredModule->get());
496 if (!maybeEngine) {
497 return maybeEngine.takeError();
498 }
499 (*maybeEngine)->registerSymbols([](llvm::orc::MangleAndInterner interner) {
500 llvm::orc::SymbolMap symbolMap;
501 symbolMap[interner("memrefCopy")] = {
502 llvm::orc::ExecutorAddr::fromPtr(&memrefCopy), llvm::JITSymbolFlags::Exported
503 };
504 symbolMap[interner("abort")] = {
505 llvm::orc::ExecutorAddr::fromPtr(&abortWithFlushedStdout), llvm::JITSymbolFlags::Exported
506 };
507 return symbolMap;
508 });
509
510 llvm::SmallVector<void *> descriptorPtrs;
511 descriptorPtrs.reserve(inputBuffers.size() + outputBuffers.size());
512 for (BufferPack &buffer : inputBuffers) {
513 descriptorPtrs.push_back(buffer.descriptor.data());
514 }
515 for (BufferPack &buffer : outputBuffers) {
516 descriptorPtrs.push_back(buffer.descriptor.data());
517 }
518
519 llvm::SmallVector<void *> packedArgs;
520 packedArgs.reserve(descriptorPtrs.size());
521 for (void *&descriptorPtr : descriptorPtrs) {
522 packedArgs.push_back(static_cast<void *>(&descriptorPtr));
523 }
524
525 if (auto err = (*maybeEngine)->invokePacked("_mlir_ciface___llzk_witgen_main", packedArgs)) {
526 return std::move(err);
527 }
528
529 llvm::SmallVector<llvm::json::Value> serializedOutputs;
530 serializedOutputs.reserve(outputBuffers.size());
531 for (const BufferPack &buffer : outputBuffers) {
532 auto serialized = bufferToJSON(buffer, field);
533 if (!serialized) {
534 return serialized.takeError();
535 }
536 serializedOutputs.push_back(*serialized);
537 }
538
539 if (options.outputScope == OutputScope::Public) {
540 return buildSignalsJSONObject(*outputs, serializedOutputs);
541 }
542
543 auto inputsJSON =
544 buildInputsJSONObject(inputBindings, *parsedArgs, tables, computeFunc.getOperation());
545 if (!inputsJSON) {
546 return inputsJSON.takeError();
547 }
548 llvm::json::Object result;
549 result["inputs"] = llvm::json::Value(std::move(*inputsJSON));
550 result["signals"] = buildSignalsJSONObject(*outputs, serializedOutputs);
551 return llvm::json::Value(std::move(result));
552}
553
554} // namespace llzk::witgen
This file implements helpers for DynamicAPInt operations and conversions that LLVM does not provide.
Information about the prime finite field used for the interval analysis.
Definition Field.h:36
::llvm::ArrayRef<::mlir::Type > getArgumentTypes()
Required by FunctionOpInterface.
Definition Ops.h.inc:1067
::std::optional<::mlir::StringAttr > getArgNameAttr(unsigned index)
Return the function.arg_name attribute for the argument at the given index.
Definition Ops.cpp:318
Drive witness generation for the concrete llzk.main instance.
void setOutputScope(OutputScope newOutputScope)
Select which witness JSON scope this interpreter emits.
llvm::Expected< llvm::json::Value > runMainFromJSON(const llvm::json::Value &input)
Execute the main compute() function using JSON inputs.
llvm::Expected< llvm::json::Value > runWithExecutionEngine(ModuleOp moduleOp, SymbolTableCollection &tables, const Field &field, const llvm::json::Value &input, const WitgenOptions &options)
Execute witness generation through MLIR lowering and the LLVM execution engine.
llvm::Expected< T > checkedCast(U u)
Definition WitgenUtils.h:28
llvm::Expected< llvm::json::Object > buildInputsJSONObject(ArrayRef< InputBinding > bindings, ArrayRef< WitnessVal > values, SymbolTableCollection &tables, Operation *origin)
Serialize named input values into a JSON object.
Definition JSON.cpp:406
llvm::SmallVector< InputBinding > collectInputBindings(function::FuncDefOp computeFunc)
Collect stable JSON bindings for the main compute inputs.
std::mt19937_64 makeDefaultValueRng(const WitgenOptions &options)
Seed an RNG for random/default witness value materialization.
OutputScope
Select the JSON scope emitted by llzk-witgen.
@ Public
Emit only public outputs.
std::variant< std::monostate, bool, int64_t, llvm::DynamicAPInt, ArrayValueRef, PodValueRef, StructValueRef > WitnessVal
Runtime value representation used by the tool-local interpreter.
Definition ValueModel.h:51
FailureOr< llvm::SmallVector< OutputBinding > > collectOutputBindings(component::StructDefOp mainDef, SymbolTableCollection &tables, Operation *origin, OutputScope scope)
Collect the selected output bindings for the requested scope.
llvm::json::Value buildSignalsJSONObject(ArrayRef< OutputBinding > bindings, ArrayRef< llvm::json::Value > serializedLeaves)
Assemble a nested JSON object from selected witness leaves.
llvm::json::Value serializeFeltToJSON(const llvm::DynamicAPInt &value, const Field &field)
Render a runtime field element as its canonical decimal string without modifying the raw runtime valu...
Definition JSON.cpp:199
std::unique_ptr< Pass > createLowerComputeToCorePass(const WitgenOptions &options)
Create the pass that lowers supported LLZK compute IR into core MLIR dialects suitable for LLVM lower...
llvm::Expected< size_t > getStaticShapeElementCount(llvm::ArrayRef< int64_t > shape, llvm::StringRef context)
Return the static element count for one shape, rejecting dynamic sizes.
std::unique_ptr< Pass > createCreateWitgenEntryPass(OutputScope outputScope)
Create the pass that synthesizes the stable llzk-witgen JIT entry wrapper.
llvm::Expected< size_t > checkedShapeDimToSize(int64_t dim, llvm::StringRef context)
Convert one static dimension to size_t, rejecting dynamic or invalid sizes.
llvm::Expected< WitnessVal > parseJSONValue(const llvm::json::Value *json, Type type, const Field &field, Operation *origin)
Parse a supported LLZK input type from JSON.
Definition JSON.cpp:265
llvm::Expected< llvm::DynamicAPInt > asFelt(const WitnessVal &value)
Require a felt value from the runtime variant.
llvm::Error makeError(const llvm::Twine &msg)
Build a string-backed error for user-facing witgen failures.
Definition Errors.h:18
llvm::Expected< ArrayValueRef > asArray(const WitnessVal &value)
Require an array value from the runtime variant.
Expected< size_t > checkedToSize(const DynamicAPInt &value, const Twine &context)
DynamicAPInt toDynamicAPInt(StringRef str)
FailureOr< SymbolLookupResult< StructDefOp > > getMainInstanceDef(SymbolTableCollection &symbolTable, Operation *lookupFrom)
Expected< APInt > checkedToAPInt(const DynamicAPInt &value, unsigned bitWidth, bool isSigned)
Describe one selected witness output leaf.
Configure one llzk-witgen execution.
UninitializedBehavior uninitializedBehavior