LLZK 2.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
ConstraintDependencyGraph.h
Go to the documentation of this file.
1//===-- ConstraintDependencyGraph.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
17
18#include <mlir/Pass/AnalysisManager.h>
19
20#include <llvm/ADT/EquivalenceClasses.h>
21
22namespace mlir {
23
24class DataFlowSolver;
25
26} // namespace mlir
27
28namespace llzk {
29
30using SourceRefRemappings = std::vector<std::pair<SourceRef, SourceRefLatticeValue>>;
31
37public:
39 using OperandValues = mlir::DenseMap<mlir::Value, const Lattice *>;
42
43 static const Lattice *getLattice(mlir::DataFlowSolver &solver, mlir::Value val);
44 static SourceRefLatticeValue getValueState(mlir::DataFlowSolver &solver, mlir::Value val);
45 static mlir::FailureOr<SourceRefLatticeValue>
46 getWriteTargetState(mlir::DataFlowSolver &solver, mlir::Operation *op);
47
50 mlir::LogicalResult visitOperation(
51 mlir::Operation *op, mlir::ArrayRef<const Lattice *> operands,
52 mlir::ArrayRef<Lattice *> results
53 ) override;
54
56 mlir::CallOpInterface call, mlir::ArrayRef<const Lattice *> argumentLattices,
57 mlir::ArrayRef<Lattice *> resultLattices
58 ) override;
59
60protected:
61 void setToEntryState(Lattice *lattice) override;
62
63 // Perform a standard union of operands into the results value.
64 static mlir::ChangeResult fallbackOpUpdate(
65 mlir::Operation *op, const OperandValues &operandVals, mlir::ArrayRef<Lattice *> results
66 );
67
68 // Create the references for either an array.read op or an array.extract op, which
69 // operate very similarly: index into the first operand using a variable number
70 // of provided indices.
73
74private:
75 mlir::SymbolTableCollection tables;
76};
77
80 bool runIntraprocedural = false;
81
83
84 friend bool operator==(const CDGAnalysisContext &a, const CDGAnalysisContext &b) = default;
85};
86
87} // namespace llzk
88
89template <> struct std::hash<llzk::CDGAnalysisContext> {
90 size_t operator()(const llzk::CDGAnalysisContext &c) const {
91 return std::hash<bool> {}(c.runIntraprocedural);
92 }
93};
94
95namespace llzk {
96
124public:
135 static mlir::FailureOr<ConstraintDependencyGraph> compute(
136 mlir::ModuleOp mod, component::StructDefOp s, mlir::DataFlowSolver &solver,
137 mlir::AnalysisManager &am, const CDGAnalysisContext &ctx
138 );
139
141 void dump() const;
144 void print(mlir::raw_ostream &os) const;
145
154
164
165 const SourceRefLattice::Ref2Val &getRef2Val() const { return ref2Val; }
166
167 /*
168 Rule of three, needed for the mlir::SymbolTableCollection, which has no copy constructor.
169 Since the mlir::SymbolTableCollection is a caching mechanism, we simply allow default, empty
170 construction for copies.
171 */
172
174 : mod(other.mod), structDef(other.structDef), ctx(other.ctx), signalSets(other.signalSets),
175 constantSets(other.constantSets), ref2Val(other.ref2Val), tables() {}
176
178 mod = other.mod;
179 structDef = other.structDef;
180 ctx = other.ctx;
181 signalSets = other.signalSets;
182 constantSets = other.constantSets;
183 ref2Val = other.ref2Val;
184 return *this;
185 }
186 virtual ~ConstraintDependencyGraph() = default;
187
188private:
189 mlir::ModuleOp mod;
190 // Using mutable because many operations are not const by default, even for "const"-like
191 // operations, like "getName()", and this reduces const_casts.
192 mutable component::StructDefOp structDef;
194
195 // Transitive closure only over signals.
196 llvm::EquivalenceClasses<SourceRef> signalSets;
197 // A simple set mapping of constants, as we do not want to compute a transitive closure over
198 // constants.
199 std::unordered_map<SourceRef, SourceRefSet, SourceRef::Hash> constantSets;
200
201 // Maps references to the values where they are found.
203
204 // Also mutable for caching within otherwise const lookup operations.
205 mutable mlir::SymbolTableCollection tables;
206
213 : mod(m), structDef(s), ctx(c) {}
214
220 mlir::LogicalResult computeConstraints(mlir::DataFlowSolver &solver, mlir::AnalysisManager &am);
221
228 void walkConstrainOp(mlir::DataFlowSolver &solver, mlir::Operation *emitOp);
229};
230
236 : public StructAnalysis<ConstraintDependencyGraph, CDGAnalysisContext> {
237public:
240
243 mlir::LogicalResult runAnalysis(
244 mlir::DataFlowSolver &solver, mlir::AnalysisManager &moduleAnalysisManager,
245 const CDGAnalysisContext &ctx
246 ) override;
247};
248
252 : public ModuleAnalysis<
253 ConstraintDependencyGraph, CDGAnalysisContext, ConstraintDependencyGraphStructAnalysis> {
254
255public:
256 // We set the SourceRef analysis as intraprocedural so that calls are treated as "external"
257 // calls, and we can leverage the `visitExternalCall` hook to translate `SourceRef`s
258 // between function contexts.
260 : ModuleAnalysis(op, mlir::DataFlowConfig().setInterprocedural(false)) {}
261
263
264 void setIntraprocedural(bool runIntraprocedural) {
265 ctx = {.runIntraprocedural = runIntraprocedural};
266 }
267
268protected:
269 void initializeSolver() override { (void)solver.load<SourceRefAnalysis>(); }
270
271 const CDGAnalysisContext &getContext() const override { return ctx; }
272
273private:
274 // This "intraprocedural" option is related to the CDG construction, not the dataflow analysis
275 // itself.
276 CDGAnalysisContext ctx = {.runIntraprocedural = false};
277};
278
279} // namespace llzk
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.
~ConstraintDependencyGraphModuleAnalysis() override=default
void initializeSolver() override
Initialize the shared dataflow solver with any common analyses required by the contained struct analy...
const CDGAnalysisContext & getContext() const override
Return the current Context object.
An analysis wrapper around the ConstraintDependencyGraph for a given struct.
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 &moduleAnalysisManager, const CDGAnalysisContext &ctx) override
Construct a CDG, using the module's analysis manager to query ConstraintDependencyGraph objects for n...
~ConstraintDependencyGraphStructAnalysis() override=default
ConstraintDependencyGraph & operator=(const ConstraintDependencyGraph &other)
void print(mlir::raw_ostream &os) const
Print the CDG to the specified output stream.
ConstraintDependencyGraph(const ConstraintDependencyGraph &other)
virtual ~ConstraintDependencyGraph()=default
static mlir::FailureOr< ConstraintDependencyGraph > compute(mlir::ModuleOp mod, component::StructDefOp s, mlir::DataFlowSolver &solver, mlir::AnalysisManager &am, const CDGAnalysisContext &ctx)
Compute a ConstraintDependencyGraph (CDG)
SourceRefSet getConstrainingValues(const SourceRef &ref) const
Get the values that are connected to the given ref via emitted constraints.
void dump() const
Dumps the CDG to stderr.
const SourceRefLattice::Ref2Val & getRef2Val() const
ConstraintDependencyGraph translate(SourceRefRemappings translation) const
Translate the SourceRefs in this CDG to that of a different context.
ModuleAnalysis(mlir::Operation *op, const mlir::DataFlowConfig &config=mlir::DataFlowConfig())
The dataflow analysis that computes the set of references that LLZK operations use and produce.
static mlir::ChangeResult fallbackOpUpdate(mlir::Operation *op, const OperandValues &operandVals, mlir::ArrayRef< Lattice * > results)
void visitExternalCall(mlir::CallOpInterface call, mlir::ArrayRef< const Lattice * > argumentLattices, mlir::ArrayRef< Lattice * > resultLattices) override
Visit a call operation to an externally defined function given the lattices of its arguments.
static mlir::FailureOr< SourceRefLatticeValue > getWriteTargetState(mlir::DataFlowSolver &solver, mlir::Operation *op)
static SourceRefLatticeValue arraySubdivisionOpUpdate(array::ArrayAccessOpInterface op, const OperandValues &operandVals)
static SourceRefLatticeValue getValueState(mlir::DataFlowSolver &solver, mlir::Value val)
void setToEntryState(Lattice *lattice) override
Set the given lattice element(s) at control flow entry point(s).
mlir::LogicalResult visitOperation(mlir::Operation *op, mlir::ArrayRef< const Lattice * > operands, mlir::ArrayRef< Lattice * > results) override
Propagate SourceRef lattice values from operands to results.
dataflow::SparseForwardDataFlowAnalysis< Lattice > Base
static const Lattice * getLattice(mlir::DataFlowSolver &solver, mlir::Value val)
mlir::DenseMap< mlir::Value, const Lattice * > OperandValues
A value at a given point of the SourceRefLattice.
Sparse SSA-value lattice for SourceRef propagation.
mlir::DenseMap< SourceRef, mlir::DenseSet< ValueTy > > Ref2Val
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...
A sparse forward data-flow analysis for propagating SSA value lattices across the IR by implementing ...
std::vector< std::pair< SourceRef, SourceRefLatticeValue > > SourceRefRemappings
Parameters and shared objects to pass to child analyses.
friend bool operator==(const CDGAnalysisContext &a, const CDGAnalysisContext &b)=default
size_t operator()(const llzk::CDGAnalysisContext &c) const