LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
IntervalAnalysisPass.cpp
Go to the documentation of this file.
1//===-- IntervalAnalysisPass.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
20#include "llzk/Util/Constants.h"
22
23#include <mlir/IR/AsmState.h>
24
25#include <llvm/ADT/STLExtras.h>
26#include <llvm/ADT/SmallVector.h>
27#include <llvm/Support/Debug.h>
28#include <llvm/Support/ErrorHandling.h>
29
30namespace llzk {
31#define GEN_PASS_DEF_INTERVALANALYSISPRINTERPASS
33} // namespace llzk
34
35#define DEBUG_TYPE "llzk-interval-analysis-pass"
36
37using namespace mlir;
38using namespace llzk;
39using namespace llzk::component;
40using namespace llzk::function;
41
42namespace {
43
44class PassImpl : public llzk::impl::IntervalAnalysisPrinterPassBase<PassImpl> {
45 using Base = IntervalAnalysisPrinterPassBase<PassImpl>;
46 using Base::Base;
47
48 void runOnOperation() override {
49 markAllAnalysesPreserved();
50
51 // Suppress false positive from `clang-tidy`
52 // NOLINTNEXTLINE(clang-analyzer-core.NonNullParamChecker)
53 auto modOp = llvm::dyn_cast<ModuleOp>(getOperation());
54 if (!modOp) {
55 constexpr const char *msg = "IntervalAnalysisPrinterPass error: should be run on ModuleOp!";
56 getOperation()->emitError(msg).report();
57 return;
58 }
59
60 // Initialize to the fallback field value
61 FieldRef selectedField = Field::getField("bn128");
62 if (!fieldName.empty()) {
63 auto fieldLookupRes = Field::tryGetField(fieldName.c_str());
64 if (failed(fieldLookupRes)) {
65 modOp->emitError()
66 .append(
67 "IntervalAnalysisPrinterPass error: unknown field \"", fieldName, "\" specified"
68 )
69 .report();
70 return;
71 }
72 selectedField = fieldLookupRes.value();
73 LLVM_DEBUG(
74 llvm::dbgs() << "[IntervalAnalysisPrinterPass] using explicit -field override '"
75 << selectedField.get().name() << "'\n";
76 );
77 } else if (auto detectedField = tryDetectSpecifiedField(modOp)) {
78 selectedField = detectedField.value();
79 LLVM_DEBUG(
80 llvm::dbgs() << "[IntervalAnalysisPrinterPass] detected module field '"
81 << selectedField.get().name() << "' from module felt usage\n";
82 );
83 } else {
84 modOp->emitWarning() << "could not detect a unique module field; falling back to '"
85 << selectedField.get().name() << '\'';
86 LLVM_DEBUG(
87 llvm::dbgs() << "[IntervalAnalysisPrinterPass] no explicit or detectable module field; "
88 "falling back to '"
89 << selectedField.get().name() << "'\n";
90 );
91 }
92
93 auto &mia = getAnalysis<ModuleIntervalAnalysis>();
94 mia.setField(selectedField);
95 mia.setPropagateInputConstraints(propagateInputConstraints);
96 mia.setTrackUnreducedIntervals(printUnreducedIntervals);
97 auto am = getAnalysisManager();
98 mia.ensureAnalysisRun(am);
99 AsmState asmState(modOp);
100
101 auto printValueInterval = [this, &asmState, &mia](raw_ostream &out, int indent, Value value) {
102 if (llvm::isa<llzk::array::ArrayType, StructType, llzk::pod::PodType>(value.getType())) {
103 return;
104 }
105 const auto *lattice = mia.getSolver().lookupState<IntervalAnalysisLattice>(value);
106 if (!lattice) {
107 return;
108 }
109 const ExpressionValue &expr = lattice->getValue().getScalarValue();
110 out << '\n';
111 out.indent(indent);
112 value.printAsOperand(out, asmState);
113 if (auto opResult = llvm::dyn_cast<OpResult>(value)) {
114 out << " [" << opResult.getOwner()->getName().getStringRef() << "]";
115 }
116 out << " in " << expr.getInterval();
117 if (printUnreducedIntervals && expr.hasUnreducedInterval()) {
118 out << " ( unreduced: " << expr.getUnreducedInterval() << " )";
119 }
120 };
121
122 auto printFunctionSSAIntervals =
123 [&printValueInterval](raw_ostream &out, FuncDefOp fn, llvm::StringRef fnName) {
124 if (!fn) {
125 return;
126 }
127
128 out << '\n';
129 out.indent(4) << fnName << " {";
130 for (BlockArgument arg : fn.getArguments()) {
131 printValueInterval(out, 8, arg);
132 }
133 fn.walk([&](Operation *op) {
134 if (op == fn.getOperation()) {
135 return;
136 }
137 for (Value result : op->getResults()) {
138 printValueInterval(out, 8, result);
139 }
140 });
141 out << '\n';
142 out.indent(4) << '}';
143 };
144
145 auto &os = llzk::toStream(outputStream);
146 for (const auto &[s, si] : mia.getCurrentResults()) {
147 auto &structDef = const_cast<StructDefOp &>(s);
148 auto fullName = getPathFromTopRoot(structDef);
149 ensure(
150 succeeded(fullName),
151 "could not resolve fully qualified name of struct " + Twine(structDef.getName())
152 );
153 os << fullName.value() << ' ';
154 si.get().print(os, printSolverConstraints, printComputeIntervals, printUnreducedIntervals);
155 if (printSSAIntervals) {
156 os << fullName.value() << " SSAIntervals {";
157 if (printComputeIntervals) {
158 printFunctionSSAIntervals(os, structDef.getComputeFuncOp(), FUNC_NAME_COMPUTE);
159 }
160 printFunctionSSAIntervals(os, structDef.getConstrainFuncOp(), FUNC_NAME_CONSTRAIN);
161 if (auto productFn = structDef.getProductFuncOp();
162 productFn && (!structDef.getConstrainFuncOp() || printComputeIntervals)) {
163 printFunctionSSAIntervals(os, productFn, FUNC_NAME_PRODUCT);
164 }
165 os << "\n}\n";
166 }
167 }
168 }
169};
170
171} // namespace
Tracks a solver expression and an interval range for that expression.
const Interval & getInterval() const
bool hasUnreducedInterval() const
const UnreducedInterval & getUnreducedInterval() const
static llvm::FailureOr< std::reference_wrapper< const Field > > tryGetField(llvm::StringRef fieldName)
Get a Field from a given field name string, or failure if the field is not defined.
Definition Field.cpp:56
static const Field & getField(llvm::StringRef fieldName, EmitErrorFn errFn)
Get a Field from a given field name string.
constexpr char FUNC_NAME_COMPUTE[]
Symbol name for the witness generation (and resp.
Definition Constants.h:16
std::reference_wrapper< const Field > FieldRef
Typealias for a stable reference to a known Field.
Definition Field.h:156
constexpr char FUNC_NAME_CONSTRAIN[]
Definition Constants.h:17
llvm::raw_ostream & toStream(OutputStream val)
void ensure(bool condition, const llvm::Twine &errMsg)
constexpr char FUNC_NAME_PRODUCT[]
Definition Constants.h:18
FailureOr< SymbolRefAttr > getPathFromTopRoot(SymbolOpInterface to, ModuleOp *foundRoot)
std::optional< std::reference_wrapper< const Field > > tryDetectSpecifiedField(mlir::Operation *root)
Try to detect a uniquely used field from the enclosing LLZK module.