LLZK 2.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
IntervalAnalysis.h
Go to the documentation of this file.
1//===-- IntervalAnalysis.h --------------------------------------*- 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
10#pragma once
11
25#include "llzk/Util/Compare.h"
26#include "llzk/Util/Field.h"
27
28#include <mlir/Analysis/DataFlow/DenseAnalysis.h>
29#include <mlir/IR/BuiltinOps.h>
30#include <mlir/Pass/AnalysisManager.h>
31#include <mlir/Support/LLVM.h>
32
33#include <llvm/ADT/DynamicAPInt.h>
34#include <llvm/ADT/MapVector.h>
35#include <llvm/Support/SMTAPI.h>
36
37#include <array>
38#include <mutex>
39
40namespace llzk {
41
42/* ExpressionValue */
43
47public:
48 /* Must be default initializable to be a ScalarLatticeValue. */
49 ExpressionValue() : i(), expr(nullptr) {}
50
51 explicit ExpressionValue(const Field &f) : i(Interval::Entire(f)), expr(nullptr) {}
52
53 ExpressionValue(const Field &f, llvm::SMTExprRef exprRef)
54 : i(Interval::Entire(f)), expr(exprRef) {}
55
56 ExpressionValue(const Field &f, llvm::SMTExprRef exprRef, const llvm::DynamicAPInt &singleVal)
57 : i(Interval::Degenerate(f, singleVal)), expr(exprRef) {}
58
59 ExpressionValue(llvm::SMTExprRef exprRef, const Interval &interval)
60 : i(interval), expr(exprRef) {}
61
62 llvm::SMTExprRef getExpr() const { return expr; }
63
64 const Interval &getInterval() const { return i; }
65
66 const Field &getField() const { return i.getField(); }
67
71 ExpressionValue withInterval(const Interval &newInterval) const {
72 return ExpressionValue(expr, newInterval);
73 }
74
76 ExpressionValue withExpression(const llvm::SMTExprRef &newExpr) const {
77 return ExpressionValue(newExpr, i);
78 }
79
80 /* Required to be a ScalarLatticeValue. */
84 return *this;
85 }
86
87 bool operator==(const ExpressionValue &rhs) const;
88
89 bool isBoolSort(const llvm::SMTSolverRef &solver) const {
90 return solver->getBoolSort() == solver->getSort(expr);
91 }
92
100 const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs
101 );
102
109 friend ExpressionValue
110 join(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
111
112 // arithmetic ops
113
114 friend ExpressionValue
115 add(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
116
117 friend ExpressionValue
118 sub(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
119
120 friend ExpressionValue
121 mul(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
122
123 friend ExpressionValue
124 div(const llvm::SMTSolverRef &solver, mlir::Operation *op, const ExpressionValue &lhs,
125 const ExpressionValue &rhs);
126
128 const llvm::SMTSolverRef &solver, mlir::Operation *op, const ExpressionValue &lhs,
129 const ExpressionValue &rhs
130 );
131
133 const llvm::SMTSolverRef &solver, mlir::Operation *op, const ExpressionValue &lhs,
134 const ExpressionValue &rhs
135 );
136
137 friend ExpressionValue
138 mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
139
140 friend ExpressionValue
141 bitAnd(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
142
143 friend ExpressionValue
144 bitOr(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
145
146 friend ExpressionValue
147 bitXor(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
148
150 const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs
151 );
152
154 const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs
155 );
156
157 friend ExpressionValue
158 cmp(const llvm::SMTSolverRef &solver, boolean::CmpOp op, const ExpressionValue &lhs,
159 const ExpressionValue &rhs);
160
161 friend ExpressionValue
162 boolAnd(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
163
164 friend ExpressionValue
165 boolOr(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
166
167 friend ExpressionValue
168 boolXor(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs);
169
170 friend ExpressionValue neg(const llvm::SMTSolverRef &solver, const ExpressionValue &val);
171
172 friend ExpressionValue notOp(const llvm::SMTSolverRef &solver, const ExpressionValue &val);
173
174 friend ExpressionValue boolNot(const llvm::SMTSolverRef &solver, const ExpressionValue &val);
175
177 const llvm::SMTSolverRef &solver, mlir::Operation *op, const ExpressionValue &val
178 );
179
180 /* Utility */
181
182 void print(mlir::raw_ostream &os) const;
183
184 friend mlir::raw_ostream &operator<<(mlir::raw_ostream &os, const ExpressionValue &e) {
185 e.print(os);
186 return os;
187 }
188
189 struct Hash {
190 unsigned operator()(const ExpressionValue &e) const {
191 return Interval::Hash {}(e.i) ^ llvm::hash_value(e.expr);
192 }
193 };
194
195private:
196 Interval i;
197 llvm::SMTExprRef expr;
198};
199
200/* IntervalAnalysisLatticeValue */
201
202// NOLINTNEXTLINE(bugprone-exception-escape)
204 : public dataflow::AbstractLatticeValue<IntervalAnalysisLatticeValue, ExpressionValue> {
205public:
206 using AbstractLatticeValue::AbstractLatticeValue;
207};
208
209/* IntervalAnalysisLattice */
210
212
214public:
216 // Map mlir::Values to LatticeValues
217 using ValueMap = mlir::DenseMap<mlir::Value, LatticeValue>;
218 // Map member references to LatticeValues. Used for member reads and writes.
219 // Structure is component value -> member attribute -> latticeValue
220 using MemberMap = mlir::DenseMap<mlir::Value, mlir::DenseMap<mlir::StringAttr, LatticeValue>>;
221 // Expression to interval map for convenience.
222 using ExpressionIntervals = mlir::DenseMap<llvm::SMTExprRef, Interval>;
223 // Tracks all constraints and assignments in insertion order
224 using ConstraintSet = llvm::SetVector<ExpressionValue>;
225
226 using AbstractSparseLattice::AbstractSparseLattice;
227
228 mlir::ChangeResult join(const AbstractSparseLattice &other) override;
229
230 mlir::ChangeResult meet(const AbstractSparseLattice &other) override;
231
232 void print(mlir::raw_ostream &os) const override;
233
234 const LatticeValue &getValue() const { return val; }
235
236 mlir::ChangeResult setValue(const LatticeValue &val);
237 mlir::ChangeResult setValue(const ExpressionValue &e);
238
239 mlir::ChangeResult addSolverConstraint(const ExpressionValue &e);
240
241 friend mlir::raw_ostream &operator<<(mlir::raw_ostream &os, const IntervalAnalysisLattice &l) {
242 l.print(os);
243 return os;
244 }
245
246 const ConstraintSet &getConstraints() const { return constraints; }
247
248 mlir::FailureOr<Interval> findInterval(llvm::SMTExprRef expr) const;
249 mlir::ChangeResult setInterval(llvm::SMTExprRef expr, const Interval &i);
250
251private:
252 LatticeValue val;
253 ConstraintSet constraints;
254};
255
256/* IntervalDataFlowAnalysis */
257
259 : public dataflow::SparseForwardDataFlowAnalysis<IntervalAnalysisLattice> {
261 using Lattice = IntervalAnalysisLattice;
262 using LatticeValue = IntervalAnalysisLattice::LatticeValue;
263
264 // Map SourceRefs to their symbols.
265 using SymbolMap = mlir::DenseMap<SourceRef, llvm::SMTExprRef>;
266
267public:
269 mlir::DataFlowSolver &dataflowSolver, llvm::SMTSolverRef smt, const Field &f,
270 bool propInputConstraints
271 )
272 : Base::SparseForwardDataFlowAnalysis(dataflowSolver), _dataflowSolver(dataflowSolver),
273 smtSolver(std::move(smt)), field(f), propagateInputConstraints(propInputConstraints) {}
274
275 mlir::LogicalResult visitOperation(
276 mlir::Operation *op, mlir::ArrayRef<const Lattice *> operands,
277 mlir::ArrayRef<Lattice *> results
278 ) override;
279
284 llvm::SMTExprRef getOrCreateSymbol(const SourceRef &r);
285
286 const llvm::DenseMap<SourceRef, llvm::DenseSet<Lattice *>> &getReadResults() const {
287 return readResults;
288 }
289
290 const llvm::DenseMap<SourceRef, ExpressionValue> &getWriteResults() const { return writeResults; }
291
292private:
293 mlir::DataFlowSolver &_dataflowSolver;
294 llvm::SMTSolverRef smtSolver;
295 SymbolMap refSymbols;
296 std::reference_wrapper<const Field> field;
297 bool propagateInputConstraints;
298 mlir::SymbolTableCollection tables;
299
300 // Track SourceRef-indexed reads so writes to rooted storage can update existing readers.
301 llvm::DenseMap<SourceRef, llvm::DenseSet<Lattice *>> readResults;
302 // Track SourceRef-indexed writes. For now, we'll overapproximate repeated writes.
303 llvm::DenseMap<SourceRef, ExpressionValue> writeResults;
304
305 void setToEntryState(Lattice *lattice) override {
306 // Initialize the value with an interval in our specified field.
307 (void)lattice->setValue(ExpressionValue(field.get()));
308 }
309
310 static bool isBooleanType(mlir::Type ty) {
311 if (auto intTy = llvm::dyn_cast<mlir::IntegerType>(ty)) {
312 return intTy.getWidth() == 1;
313 }
314 return false;
315 }
316
317 Interval getDefaultIntervalForType(mlir::Type ty) const {
318 return isBooleanType(ty) ? Interval::Boolean(field.get()) : Interval::Entire(field.get());
319 }
320
321 llvm::SMTExprRef createSymbol(mlir::Type ty, const char *name) const;
322
323 llvm::SMTExprRef createSymbol(const SourceRef &r) const;
324
325 llvm::SMTExprRef createSymbol(mlir::Value val) const;
326
327 ExpressionValue createUnknownValue(mlir::Value val) const {
328 return ExpressionValue(createSymbol(val), getDefaultIntervalForType(val.getType()));
329 }
330
331 inline bool isConstOp(mlir::Operation *op) const {
332 return llvm::isa<
333 felt::FeltConstantOp, mlir::arith::ConstantIndexOp, mlir::arith::ConstantIntOp>(op);
334 }
335
336 inline bool isBoolConstOp(mlir::Operation *op) const {
337 if (auto constIntOp = llvm::dyn_cast<mlir::arith::ConstantIntOp>(op)) {
338 auto valAttr = dyn_cast<mlir::IntegerAttr>(constIntOp.getValue());
339 ensure(valAttr != nullptr, "arith::ConstantIntOp must have an IntegerAttr as its value");
340 return valAttr.getValue().getBitWidth() == 1;
341 }
342 return false;
343 }
344
345 llvm::DynamicAPInt getConst(mlir::Operation *op) const;
346
347 inline llvm::SMTExprRef createConstBitvectorExpr(const llvm::DynamicAPInt &v) const {
348 return createConstBitvectorExpr(toAPSInt(v));
349 }
350
351 inline llvm::SMTExprRef createConstBitvectorExpr(const llvm::APSInt &v) const {
352 return smtSolver->mkBitvector(v, field.get().bitWidth());
353 }
354
355 llvm::SMTExprRef createConstBoolExpr(bool v) const { return smtSolver->mkBoolean(v); }
356
357 bool isArithmeticOp(mlir::Operation *op) const {
358 return llvm::isa<
359 felt::AddFeltOp, felt::SubFeltOp, felt::MulFeltOp, felt::DivFeltOp, felt::UnsignedModFeltOp,
360 felt::SignedModFeltOp, felt::SignedIntDivFeltOp, felt::UnsignedIntDivFeltOp,
361 mlir::arith::XOrIOp, felt::NegFeltOp, felt::InvFeltOp, felt::AndFeltOp, felt::OrFeltOp,
362 felt::XorFeltOp, felt::NotFeltOp, felt::ShlFeltOp, felt::ShrFeltOp, boolean::CmpOp,
363 boolean::AndBoolOp, boolean::OrBoolOp, boolean::XorBoolOp, boolean::NotBoolOp>(op);
364 }
365
366 ExpressionValue
367 performBinaryArithmetic(mlir::Operation *op, const LatticeValue &a, const LatticeValue &b);
368
369 ExpressionValue performUnaryArithmetic(mlir::Operation *op, const LatticeValue &a);
370
377 void applyInterval(mlir::Operation *originalOp, mlir::Value val, Interval newInterval);
378
380 mlir::FailureOr<std::pair<llvm::DenseSet<mlir::Value>, Interval>>
381 getGeneralizedDecompInterval(mlir::Operation *baseOp, mlir::Value lhs, mlir::Value rhs);
382
383 bool isReadOp(mlir::Operation *op) const {
384 return llvm::isa<component::MemberReadOp, polymorphic::ConstReadOp, array::ReadArrayOp>(op);
385 }
386
387 bool isDefinitionOp(mlir::Operation *op) const {
388 return llvm::isa<
389 component::StructDefOp, function::FuncDefOp, component::MemberDefOp, global::GlobalDefOp,
390 mlir::ModuleOp>(op);
391 }
392
393 bool isReturnOp(mlir::Operation *op) const { return llvm::isa<function::ReturnOp>(op); }
394
398 std::vector<SourceRefIndex>
399 getArrayAccessIndices(mlir::Operation *baseOp, array::ArrayAccessOpInterface arrayAccessOp);
400
403 mlir::FailureOr<SourceRef>
404 getArrayAccessRef(mlir::Operation *baseOp, array::ArrayAccessOpInterface arrayAccessOp);
405
408 Interval getRefInterval(const SourceRef &ref);
409
413 ExpressionValue getRefValue(const SourceRef &ref, mlir::Value val);
414
417 void recordRefWrite(const SourceRef &writtenRef, const ExpressionValue &writeVal);
418
420 SourceRefLatticeValue getSourceRefState(mlir::Value val);
421};
422
423/* StructIntervals */
424
428 llvm::SMTSolverRef smtSolver;
429 std::optional<std::reference_wrapper<const Field>> field;
431
432 llvm::SMTExprRef getSymbol(const SourceRef &r) const { return intervalDFA->getOrCreateSymbol(r); }
433 bool hasField() const { return field.has_value(); }
434 const Field &getField() const {
435 ensure(field.has_value(), "field not set within context");
436 return field->get();
437 }
439
440 friend bool
442};
443
444} // namespace llzk
445
446template <> struct std::hash<llzk::IntervalAnalysisContext> {
448 return llvm::hash_combine(
449 std::hash<const llzk::IntervalDataFlowAnalysis *> {}(c.intervalDFA),
450 std::hash<const llvm::SMTSolver *> {}(c.smtSolver.get()),
451 std::hash<const llzk::Field *> {}(&c.getField()),
452 std::hash<bool> {}(c.propagateInputConstraints)
453 );
454 }
455};
456
457namespace llzk {
458
459// Suppress false positive from `clang-tidy`
460// NOLINTNEXTLINE(bugprone-exception-escape)
461class StructIntervals {
462public:
472 static mlir::FailureOr<StructIntervals> compute(
473 mlir::ModuleOp mod, component::StructDefOp s, mlir::DataFlowSolver &solver,
474 const IntervalAnalysisContext &ctx
475 ) {
476 StructIntervals si(mod, s);
477 if (si.computeIntervals(solver, ctx).failed()) {
478 return mlir::failure();
479 }
480 return si;
481 }
482
483 mlir::LogicalResult
484 computeIntervals(mlir::DataFlowSolver &solver, const IntervalAnalysisContext &ctx);
485
486 void print(mlir::raw_ostream &os, bool withConstraints = false, bool printCompute = false) const;
487
488 const llvm::MapVector<SourceRef, Interval> &getConstrainIntervals() const {
489 return constrainMemberRanges;
490 }
491
492 const llvm::SetVector<ExpressionValue> getConstrainSolverConstraints() const {
493 return constrainSolverConstraints;
494 }
495
496 const llvm::MapVector<SourceRef, Interval> &getComputeIntervals() const {
497 return computeMemberRanges;
498 }
499
500 const llvm::SetVector<ExpressionValue> getComputeSolverConstraints() const {
501 return computeSolverConstraints;
502 }
503
504 friend mlir::raw_ostream &operator<<(mlir::raw_ostream &os, const StructIntervals &si) {
505 si.print(os);
506 return os;
507 }
508
509private:
510 mlir::ModuleOp mod;
511 component::StructDefOp structDef;
512 llvm::SMTSolverRef smtSolver;
513 // llvm::MapVector keeps insertion order for consistent iteration
514 llvm::MapVector<SourceRef, Interval> constrainMemberRanges, computeMemberRanges;
515 // llvm::SetVector for the same reasons as above
516 llvm::SetVector<ExpressionValue> constrainSolverConstraints, computeSolverConstraints;
517
518 StructIntervals(mlir::ModuleOp m, component::StructDefOp s) : mod(m), structDef(s) {}
519};
520
521/* StructIntervalAnalysis */
522
524
525class StructIntervalAnalysis : public StructAnalysis<StructIntervals, IntervalAnalysisContext> {
526public:
528 ~StructIntervalAnalysis() override = default;
529
530 mlir::LogicalResult runAnalysis(
531 mlir::DataFlowSolver &solver, mlir::AnalysisManager &, const IntervalAnalysisContext &ctx
532 ) override {
533 auto computeRes = StructIntervals::compute(getModule(), getStruct(), solver, ctx);
534 if (mlir::failed(computeRes)) {
535 return mlir::failure();
536 }
537 setResult(ctx, std::move(*computeRes));
538 return mlir::success();
539 }
540};
541
542/* ModuleIntervalAnalysis */
543
545 : public ModuleAnalysis<StructIntervals, IntervalAnalysisContext, StructIntervalAnalysis> {
546
547public:
548 // We set intraprocedural to false for the sake of the SourceRefAnalysis
549 ModuleIntervalAnalysis(mlir::Operation *op)
550 : ModuleAnalysis(op, mlir::DataFlowConfig().setInterprocedural(false)), ctx {} {
551 ctx.smtSolver = llvm::CreateZ3Solver();
552 }
553 ~ModuleIntervalAnalysis() override = default;
554
555 void setField(const Field &f) { ctx.field = f; }
556 void setPropagateInputConstraints(bool prop) { ctx.propagateInputConstraints = prop; }
557
558protected:
559 void initializeSolver() override {
560 ensure(ctx.hasField(), "field not set, could not generate analysis context");
561 (void)solver.load<SourceRefAnalysis>();
562 auto smtSolverRef = ctx.smtSolver;
563 bool prop = ctx.propagateInputConstraints;
564 ctx.intervalDFA =
565 solver.load<IntervalDataFlowAnalysis, llvm::SMTSolverRef, const Field &, bool>(
566 std::move(smtSolverRef), ctx.getField(),
567 std::move(prop) // NOLINT(performance-move-const-arg)
568 );
569 }
570
571 const IntervalAnalysisContext &getContext() const override {
572 ensure(ctx.field.has_value(), "field not set, could not generate analysis context");
573 return ctx;
574 }
575
576private:
578};
579
580} // namespace llzk
581
582namespace llvm {
583
584template <> struct DenseMapInfo<llzk::ExpressionValue> {
585
586 static SMTExprRef getEmptyExpr() {
587 static const auto *emptyPtr = reinterpret_cast<SMTExprRef>(1);
588 return emptyPtr;
589 }
590 static SMTExprRef getTombstoneExpr() {
591 static const auto *tombstonePtr = reinterpret_cast<SMTExprRef>(2);
592 return tombstonePtr;
593 }
594
601 static unsigned getHashValue(const llzk::ExpressionValue &e) {
602 return llzk::ExpressionValue::Hash {}(e);
603 }
604 static bool isEqual(const llzk::ExpressionValue &lhs, const llzk::ExpressionValue &rhs) {
605 if (lhs.getExpr() == getEmptyExpr() || lhs.getExpr() == getTombstoneExpr() ||
606 rhs.getExpr() == getEmptyExpr() || rhs.getExpr() == getTombstoneExpr()) {
607 return lhs.getExpr() == rhs.getExpr();
608 }
609 return lhs == rhs;
610 }
611};
612
613} // namespace llvm
Convenience classes for a frequent pattern of dataflow analysis used in LLZK, where an analysis is ru...
This file implements sparse data-flow analysis using the data-flow analysis framework.
Tracks a solver expression and an interval range for that expression.
friend ExpressionValue boolAnd(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue(llvm::SMTExprRef exprRef, const Interval &interval)
friend ExpressionValue sintDiv(const llvm::SMTSolverRef &solver, mlir::Operation *op, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue withExpression(const llvm::SMTExprRef &newExpr) const
Return the current expression with a new SMT expression.
friend ExpressionValue fallbackUnaryOp(const llvm::SMTSolverRef &solver, mlir::Operation *op, const ExpressionValue &val)
friend ExpressionValue bitOr(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue notOp(const llvm::SMTSolverRef &solver, const ExpressionValue &val)
friend ExpressionValue shiftRight(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue intersection(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
Compute the intersection of the lhs and rhs intervals, and create a solver expression that constrains...
friend ExpressionValue uintDiv(const llvm::SMTSolverRef &solver, mlir::Operation *op, const ExpressionValue &lhs, const ExpressionValue &rhs)
const Interval & getInterval() const
friend ExpressionValue mul(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue div(const llvm::SMTSolverRef &solver, mlir::Operation *op, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue(const Field &f, llvm::SMTExprRef exprRef)
ExpressionValue(const Field &f)
friend ExpressionValue sub(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue withInterval(const Interval &newInterval) const
Return the current expression with a new interval.
friend ExpressionValue boolXor(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
void print(mlir::raw_ostream &os) const
bool operator==(const ExpressionValue &rhs) const
friend ExpressionValue shiftLeft(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue bitXor(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue bitAnd(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
llvm::SMTExprRef getExpr() const
bool isBoolSort(const llvm::SMTSolverRef &solver) const
friend ExpressionValue cmp(const llvm::SMTSolverRef &solver, boolean::CmpOp op, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue join(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
Compute the union of the lhs and rhs intervals, and create a solver expression that constrains both s...
ExpressionValue(const Field &f, llvm::SMTExprRef exprRef, const llvm::DynamicAPInt &singleVal)
friend ExpressionValue boolOr(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue boolNot(const llvm::SMTSolverRef &solver, const ExpressionValue &val)
friend mlir::raw_ostream & operator<<(mlir::raw_ostream &os, const ExpressionValue &e)
const Field & getField() const
ExpressionValue & join(const ExpressionValue &)
Fold two expressions together when overapproximating array elements.
friend ExpressionValue neg(const llvm::SMTSolverRef &solver, const ExpressionValue &val)
friend ExpressionValue add(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
Information about the prime finite field used for the interval analysis.
Definition Field.h:35
static const Field & getField(llvm::StringRef fieldName, EmitErrorFn errFn)
Get a Field from a given field name string.
friend mlir::raw_ostream & operator<<(mlir::raw_ostream &os, const IntervalAnalysisLattice &l)
const LatticeValue & getValue() const
llvm::SetVector< ExpressionValue > ConstraintSet
mlir::ChangeResult setValue(const LatticeValue &val)
IntervalAnalysisLatticeValue LatticeValue
mlir::DenseMap< mlir::Value, LatticeValue > ValueMap
mlir::ChangeResult meet(const AbstractSparseLattice &other) override
const ConstraintSet & getConstraints() const
mlir::DenseMap< mlir::Value, mlir::DenseMap< mlir::StringAttr, LatticeValue > > MemberMap
void print(mlir::raw_ostream &os) const override
mlir::ChangeResult join(const AbstractSparseLattice &other) override
mlir::ChangeResult setInterval(llvm::SMTExprRef expr, const Interval &i)
mlir::DenseMap< llvm::SMTExprRef, Interval > ExpressionIntervals
mlir::ChangeResult addSolverConstraint(const ExpressionValue &e)
mlir::FailureOr< Interval > findInterval(llvm::SMTExprRef expr) const
mlir::LogicalResult visitOperation(mlir::Operation *op, mlir::ArrayRef< const Lattice * > operands, mlir::ArrayRef< Lattice * > results) override
Visit an operation with the lattices of its operands.
llvm::SMTExprRef getOrCreateSymbol(const SourceRef &r)
Either return the existing SMT expression that corresponds to the SourceRef, or create one.
const llvm::DenseMap< SourceRef, ExpressionValue > & getWriteResults() const
const llvm::DenseMap< SourceRef, llvm::DenseSet< Lattice * > > & getReadResults() const
IntervalDataFlowAnalysis(mlir::DataFlowSolver &dataflowSolver, llvm::SMTSolverRef smt, const Field &f, bool propInputConstraints)
Intervals over a finite field.
Definition Intervals.h:200
static Interval Boolean(const Field &f)
Definition Intervals.h:221
ModuleAnalysis(mlir::Operation *op, const mlir::DataFlowConfig &config=mlir::DataFlowConfig())
ModuleIntervalAnalysis(mlir::Operation *op)
~ModuleIntervalAnalysis() override=default
void setPropagateInputConstraints(bool prop)
void initializeSolver() override
Initialize the shared dataflow solver with any common analyses required by the contained struct analy...
const IntervalAnalysisContext & getContext() const override
Return the current Context object.
void setField(const Field &f)
The dataflow analysis that computes the set of references that LLZK operations use and produce.
A reference to a "source", which is the base value from which other SSA values are derived.
Definition SourceRef.h:132
StructAnalysis(mlir::Operation *op)
Assert that this analysis is being run on a StructDefOp and initializes the analysis with the current...
void setResult(const IntervalAnalysisContext &ctx, StructIntervals &&r)
~StructIntervalAnalysis() override=default
StructAnalysis(mlir::Operation *op)
Assert that this analysis is being run on a StructDefOp and initializes the analysis with the current...
mlir::LogicalResult runAnalysis(mlir::DataFlowSolver &solver, mlir::AnalysisManager &, const IntervalAnalysisContext &ctx) override
Perform the analysis and construct the Result output.
const llvm::MapVector< SourceRef, Interval > & getConstrainIntervals() const
const llvm::SetVector< ExpressionValue > getConstrainSolverConstraints() const
const llvm::SetVector< ExpressionValue > getComputeSolverConstraints() const
const llvm::MapVector< SourceRef, Interval > & getComputeIntervals() const
void print(mlir::raw_ostream &os, bool withConstraints=false, bool printCompute=false) const
static mlir::FailureOr< StructIntervals > compute(mlir::ModuleOp mod, component::StructDefOp s, mlir::DataFlowSolver &solver, const IntervalAnalysisContext &ctx)
Compute the struct intervals.
friend mlir::raw_ostream & operator<<(mlir::raw_ostream &os, const StructIntervals &si)
mlir::LogicalResult computeIntervals(mlir::DataFlowSolver &solver, const IntervalAnalysisContext &ctx)
mlir::SymbolTableCollection tables
LLZK: Added for use of symbol helper caching.
A sparse forward data-flow analysis for propagating SSA value lattices across the IR by implementing ...
mlir::dataflow::AbstractSparseLattice AbstractSparseLattice
ExpressionValue mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
void ensure(bool condition, const llvm::Twine &errMsg)
APSInt toAPSInt(const DynamicAPInt &i)
static unsigned getHashValue(const llzk::ExpressionValue &e)
static bool isEqual(const llzk::ExpressionValue &lhs, const llzk::ExpressionValue &rhs)
static llzk::ExpressionValue getTombstoneKey()
static llzk::ExpressionValue getEmptyKey()
unsigned operator()(const ExpressionValue &e) const
Parameters and shared objects to pass to child analyses.
const Field & getField() const
friend bool operator==(const IntervalAnalysisContext &a, const IntervalAnalysisContext &b)=default
std::optional< std::reference_wrapper< const Field > > field
IntervalDataFlowAnalysis * intervalDFA
llvm::SMTExprRef getSymbol(const SourceRef &r) const
size_t operator()(const llzk::IntervalAnalysisContext &c) const