29#include <mlir/IR/BuiltinOps.h>
30#include <mlir/Pass/AnalysisManager.h>
31#include <mlir/Support/LLVM.h>
33#include <llvm/ADT/DynamicAPInt.h>
34#include <llvm/ADT/MapVector.h>
35#include <llvm/Support/SMTAPI.h>
54 : i(
Interval::Entire(f)), expr(exprRef) {}
57 : i(
Interval::Degenerate(f, singleVal)), expr(exprRef) {}
60 : i(interval), expr(exprRef) {}
62 llvm::SMTExprRef
getExpr()
const {
return expr; }
90 return solver->getBoolSort() == solver->getSort(expr);
160 llvm::SMTSolverRef solver, mlir::Operation *op,
const ExpressionValue &lhs,
175 void print(mlir::raw_ostream &os)
const;
190 llvm::SMTExprRef expr;
198 using AbstractLatticeValue::AbstractLatticeValue;
209 using ValueMap = mlir::DenseMap<mlir::Value, LatticeValue>;
212 using FieldMap = mlir::DenseMap<mlir::Value, mlir::DenseMap<mlir::StringAttr, LatticeValue>>;
218 using AbstractSparseLattice::AbstractSparseLattice;
220 mlir::ChangeResult
join(
const AbstractSparseLattice &other)
override;
222 mlir::ChangeResult
meet(
const AbstractSparseLattice &other)
override;
224 void print(mlir::raw_ostream &os)
const override;
257 using SymbolMap = mlir::DenseMap<SourceRef, llvm::SMTExprRef>;
261 mlir::DataFlowSolver &dataflowSolver, llvm::SMTSolverRef smt,
const Field &f,
262 bool propInputConstraints
265 smtSolver(smt), field(f), propagateInputConstraints(propInputConstraints) {}
268 mlir::Operation *op, mlir::ArrayRef<const Lattice *> operands,
269 mlir::ArrayRef<Lattice *> results
279 return fieldReadResults;
283 return fieldWriteResults;
287 mlir::DataFlowSolver &_dataflowSolver;
288 llvm::SMTSolverRef smtSolver;
289 SymbolMap refSymbols;
290 std::reference_wrapper<const Field> field;
291 bool propagateInputConstraints;
292 mlir::SymbolTableCollection
tables;
295 llvm::DenseMap<SourceRef, llvm::DenseSet<Lattice *>> fieldReadResults;
297 llvm::DenseMap<SourceRef, ExpressionValue> fieldWriteResults;
299 void setToEntryState(Lattice *lattice)
override {
304 llvm::SMTExprRef createFeltSymbol(
const SourceRef &r)
const;
306 llvm::SMTExprRef createFeltSymbol(mlir::Value val)
const;
308 llvm::SMTExprRef createFeltSymbol(
const char *name)
const;
310 bool isConstOp(mlir::Operation *op)
const {
312 felt::FeltConstantOp, mlir::arith::ConstantIndexOp, mlir::arith::ConstantIntOp>(op);
315 llvm::DynamicAPInt getConst(mlir::Operation *op)
const;
317 inline llvm::SMTExprRef createConstBitvectorExpr(
const llvm::DynamicAPInt &v)
const {
318 return createConstBitvectorExpr(
toAPSInt(v));
321 inline llvm::SMTExprRef createConstBitvectorExpr(
const llvm::APSInt &v)
const {
322 return smtSolver->mkBitvector(v, field.get().bitWidth());
325 llvm::SMTExprRef createConstBoolExpr(
bool v)
const {
326 return smtSolver->mkBitvector(mlir::APSInt((
int)v), field.get().bitWidth());
329 bool isArithmeticOp(mlir::Operation *op)
const {
331 felt::AddFeltOp, felt::SubFeltOp, felt::MulFeltOp, felt::DivFeltOp, felt::ModFeltOp,
332 felt::NegFeltOp, felt::InvFeltOp, felt::AndFeltOp, felt::OrFeltOp, felt::XorFeltOp,
333 felt::NotFeltOp, felt::ShlFeltOp, felt::ShrFeltOp, boolean::CmpOp, boolean::AndBoolOp,
334 boolean::OrBoolOp, boolean::XorBoolOp, boolean::NotBoolOp>(op);
338 performBinaryArithmetic(mlir::Operation *op,
const LatticeValue &a,
const LatticeValue &b);
340 ExpressionValue performUnaryArithmetic(mlir::Operation *op,
const LatticeValue &a);
348 void applyInterval(mlir::Operation *originalOp, mlir::Value val, Interval newInterval);
351 mlir::FailureOr<std::pair<llvm::DenseSet<mlir::Value>, Interval>>
352 getGeneralizedDecompInterval(mlir::Operation *baseOp, mlir::Value lhs, mlir::Value rhs);
354 bool isReadOp(mlir::Operation *op)
const {
355 return llvm::isa<component::FieldReadOp, polymorphic::ConstReadOp, array::ReadArrayOp>(op);
358 bool isDefinitionOp(mlir::Operation *op)
const {
360 component::StructDefOp, function::FuncDefOp, component::FieldDefOp, global::GlobalDefOp,
364 bool isReturnOp(mlir::Operation *op)
const {
return llvm::isa<function::ReturnOp>(op); }
368 const SourceRefLattice *getSourceRefLattice(mlir::Operation *baseOp, mlir::Value val);
377 std::optional<std::reference_wrapper<const Field>>
field;
383 ensure(
field.has_value(),
"field not set within context");
394template <>
struct std::hash<
llzk::IntervalAnalysisContext> {
396 return llvm::hash_combine(
397 std::hash<const llzk::IntervalDataFlowAnalysis *> {}(c.
intervalDFA),
398 std::hash<const llvm::SMTSolver *> {}(c.
smtSolver.get()),
399 std::hash<const llzk::Field *> {}(&c.
getField()),
407class StructIntervals {
418 static mlir::FailureOr<StructIntervals>
compute(
422 StructIntervals si(mod, s);
424 return mlir::failure();
432 void print(mlir::raw_ostream &os,
bool withConstraints =
false,
bool printCompute =
false)
const;
435 return constrainFieldRanges;
439 return constrainSolverConstraints;
443 return computeFieldRanges;
447 return computeSolverConstraints;
450 friend mlir::raw_ostream &
operator<<(mlir::raw_ostream &os,
const StructIntervals &si) {
458 llvm::SMTSolverRef smtSolver;
460 llvm::MapVector<SourceRef, Interval> constrainFieldRanges, computeFieldRanges;
462 llvm::SetVector<ExpressionValue> constrainSolverConstraints, computeSolverConstraints;
480 if (mlir::failed(computeRes)) {
481 return mlir::failure();
484 return mlir::success();
491 :
public ModuleAnalysis<StructIntervals, IntervalAnalysisContext, StructIntervalAnalysis> {
495 ctx.smtSolver = llvm::CreateZ3Solver();
504 ensure(ctx.hasField(),
"field not set, could not generate analysis context");
506 auto smtSolverRef = ctx.smtSolver;
507 bool prop = ctx.propagateInputConstraints;
510 std::move(smtSolverRef), ctx.getField(), std::move(prop)
515 ensure(ctx.field.has_value(),
"field not set, could not generate analysis context");
527template <>
struct DenseMapInfo<
llzk::ExpressionValue> {
530 static auto emptyPtr =
reinterpret_cast<SMTExprRef
>(1);
534 static auto tombstonePtr =
reinterpret_cast<SMTExprRef
>(2);
Convenience classes for a frequent pattern of dataflow analysis used in LLZK, where an analysis is ru...
This file implements (LLZK-tailored) dense data-flow analysis using the data-flow analysis framework.
This file implements sparse data-flow analysis using the data-flow analysis framework.
Tracks a solver expression and an interval range for that expression.
ExpressionValue(llvm::SMTExprRef exprRef, const Interval &interval)
ExpressionValue withExpression(const llvm::SMTExprRef &newExpr) const
Return the current expression with a new SMT expression.
friend ExpressionValue neg(llvm::SMTSolverRef solver, const ExpressionValue &val)
friend ExpressionValue notOp(llvm::SMTSolverRef solver, const ExpressionValue &val)
const Interval & getInterval() const
friend ExpressionValue cmp(llvm::SMTSolverRef solver, boolean::CmpOp op, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue(const Field &f, llvm::SMTExprRef exprRef)
friend ExpressionValue sub(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue(const Field &f)
friend ExpressionValue mul(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
bool isBoolSort(llvm::SMTSolverRef solver) const
friend ExpressionValue fallbackUnaryOp(llvm::SMTSolverRef solver, mlir::Operation *op, const ExpressionValue &val)
friend ExpressionValue boolOr(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue div(llvm::SMTSolverRef solver, felt::DivFeltOp op, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue withInterval(const Interval &newInterval) const
Return the current expression with a new interval.
void print(mlir::raw_ostream &os) const
bool operator==(const ExpressionValue &rhs) const
friend ExpressionValue add(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue shiftRight(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
llvm::SMTExprRef getExpr() const
friend ExpressionValue fallbackBinaryOp(llvm::SMTSolverRef solver, mlir::Operation *op, const ExpressionValue &lhs, const ExpressionValue &rhs)
Computes a solver expression based on the operation, but computes a fallback interval (which is just ...
ExpressionValue(const Field &f, llvm::SMTExprRef exprRef, const llvm::DynamicAPInt &singleVal)
friend ExpressionValue bitAnd(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue boolXor(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue join(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...
friend mlir::raw_ostream & operator<<(mlir::raw_ostream &os, const ExpressionValue &e)
friend ExpressionValue boolAnd(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
const Field & getField() const
friend ExpressionValue mod(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
friend ExpressionValue shiftLeft(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
ExpressionValue & join(const ExpressionValue &)
Fold two expressions together when overapproximating array elements.
friend ExpressionValue intersection(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 boolNot(llvm::SMTSolverRef solver, const ExpressionValue &val)
Information about the prime finite field used for the interval analysis.
static const Field & getField(const char *fieldName)
Get a Field from a given field name string.
friend mlir::raw_ostream & operator<<(mlir::raw_ostream &os, const IntervalAnalysisLattice &l)
mlir::DenseMap< mlir::Value, mlir::DenseMap< mlir::StringAttr, LatticeValue > > FieldMap
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
mlir::ChangeResult addSolverConstraint(ExpressionValue e)
const ConstraintSet & getConstraints() const
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::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, llvm::DenseSet< Lattice * > > & getFieldReadResults() const
IntervalDataFlowAnalysis(mlir::DataFlowSolver &dataflowSolver, llvm::SMTSolverRef smt, const Field &f, bool propInputConstraints)
const llvm::DenseMap< SourceRef, ExpressionValue > & getFieldWriteResults() const
Intervals over a finite field.
mlir::DataFlowSolver solver
ModuleAnalysis(mlir::Operation *op)
ModuleIntervalAnalysis(mlir::Operation *op)
virtual ~ModuleIntervalAnalysis()=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.
StructAnalysis(mlir::Operation *op)
Assert that this analysis is being run on a StructDefOp and initializes the analysis with the current...
component::StructDefOp getStruct() const
void setResult(const IntervalAnalysisContext &ctx, StructIntervals &&r)
mlir::ModuleOp getModule() const
StructAnalysis(mlir::Operation *op)
Assert that this analysis is being run on a StructDefOp and initializes the analysis with the current...
virtual ~StructIntervalAnalysis()=default
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 ...
SparseForwardDataFlowAnalysis(mlir::DataFlowSolver &s)
mlir::dataflow::AbstractSparseLattice AbstractSparseLattice
void ensure(bool condition, const llvm::Twine &errMsg)
APSInt toAPSInt(const DynamicAPInt &i)
ExpressionValue mod(llvm::SMTSolverRef solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
static unsigned getHashValue(const llzk::ExpressionValue &e)
static SMTExprRef getTombstoneExpr()
static bool isEqual(const llzk::ExpressionValue &lhs, const llzk::ExpressionValue &rhs)
static llzk::ExpressionValue getTombstoneKey()
static llzk::ExpressionValue getEmptyKey()
static SMTExprRef getEmptyExpr()
unsigned operator()(const ExpressionValue &e) const
Parameters and shared objects to pass to child analyses.
bool doInputConstraintPropagation() const
const Field & getField() const
friend bool operator==(const IntervalAnalysisContext &a, const IntervalAnalysisContext &b)=default
std::optional< std::reference_wrapper< const Field > > field
IntervalDataFlowAnalysis * intervalDFA
bool propagateInputConstraints
llvm::SMTSolverRef smtSolver
llvm::SMTExprRef getSymbol(const SourceRef &r) const
size_t operator()(const llzk::IntervalAnalysisContext &c) const