LLZK 3.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
57 mlir::LogicalResult visitOperation(
58 mlir::Operation *op, mlir::ArrayRef<const Lattice *> operands,
59 mlir::ArrayRef<Lattice *> results
60 ) override;
61
74 mlir::CallOpInterface call, mlir::ArrayRef<const Lattice *> argumentLattices,
75 mlir::ArrayRef<Lattice *> resultLattices
76 ) override;
77
78protected:
79 void setToEntryState(Lattice *lattice) override;
80
81 // Perform a standard union of operands into the results value.
82 static mlir::ChangeResult fallbackOpUpdate(
83 mlir::Operation *op, const OperandValues &operandVals, mlir::ArrayRef<Lattice *> results
84 );
85
86 // Create the references for either an array.read op or an array.extract op, which
87 // operate very similarly: index into the first operand using a variable number
88 // of provided indices.
91
92private:
93 mlir::SymbolTableCollection tables;
94};
95
98 bool runIntraprocedural = false;
99
101
102 friend bool operator==(const CDGAnalysisContext &a, const CDGAnalysisContext &b) = default;
103};
104
105} // namespace llzk
106
107template <> struct std::hash<llzk::CDGAnalysisContext> {
108 size_t operator()(const llzk::CDGAnalysisContext &c) const {
109 return std::hash<bool> {}(c.runIntraprocedural);
110 }
111};
112
113namespace llzk {
114
140public:
151 static mlir::FailureOr<ConstraintDependencyGraph> compute(
152 mlir::ModuleOp mod, component::StructDefOp s, mlir::DataFlowSolver &solver,
153 mlir::AnalysisManager &am, const CDGAnalysisContext &ctx
154 );
155
157 void dump() const;
160 void print(mlir::raw_ostream &os) const;
161
170
180
181 const SourceRefLattice::Ref2Val &getRef2Val() const { return ref2Val; }
182
183 /*
184 Rule of three, needed for the mlir::SymbolTableCollection, which has no copy constructor.
185 Since the mlir::SymbolTableCollection is a caching mechanism, we simply allow default, empty
186 construction for copies.
187 */
188
190 : mod(other.mod), structDef(other.structDef), ctx(other.ctx), signalSets(other.signalSets),
191 constantSets(other.constantSets), ref2Val(other.ref2Val), tables() {}
192
194 mod = other.mod;
195 structDef = other.structDef;
196 ctx = other.ctx;
197 signalSets = other.signalSets;
198 constantSets = other.constantSets;
199 ref2Val = other.ref2Val;
200 return *this;
201 }
202 virtual ~ConstraintDependencyGraph() = default;
203
204private:
205 mlir::ModuleOp mod;
206 // Using mutable because many operations are not const by default, even for "const"-like
207 // operations, like "getName()", and this reduces const_casts.
208 mutable component::StructDefOp structDef;
210
211 // Transitive closure only over signals.
212 llvm::EquivalenceClasses<SourceRef> signalSets;
213 // A simple set mapping of constants, as we do not want to compute a transitive closure over
214 // constants.
215 std::unordered_map<SourceRef, SourceRefSet, SourceRef::Hash> constantSets;
216
217 // Maps references to the values where they are found.
219
220 // Also mutable for caching within otherwise const lookup operations.
221 mutable mlir::SymbolTableCollection tables;
222
229 : mod(m), structDef(s), ctx(c) {}
230
236 mlir::LogicalResult computeConstraints(mlir::DataFlowSolver &solver, mlir::AnalysisManager &am);
237
244 void walkConstrainOp(mlir::DataFlowSolver &solver, mlir::Operation *emitOp);
245};
246
252 : public StructAnalysis<ConstraintDependencyGraph, CDGAnalysisContext> {
253public:
256
259 mlir::LogicalResult runAnalysis(
260 mlir::DataFlowSolver &solver, mlir::AnalysisManager &moduleAnalysisManager,
261 const CDGAnalysisContext &ctx
262 ) override;
263};
264
268 : public ModuleAnalysis<
269 ConstraintDependencyGraph, CDGAnalysisContext, ConstraintDependencyGraphStructAnalysis> {
270
271public:
272 // We set the SourceRef analysis as intraprocedural so that calls are treated as "external"
273 // calls, and we can leverage the `visitExternalCall` hook to translate `SourceRef`s
274 // between function contexts.
276 : ModuleAnalysis(op, mlir::DataFlowConfig().setInterprocedural(false)) {}
277
279
280 void setIntraprocedural(bool runIntraprocedural) {
281 ctx = {.runIntraprocedural = runIntraprocedural};
282 }
283
284protected:
285 void initializeSolver() override { (void)solver.load<SourceRefAnalysis>(); }
286
287 const CDGAnalysisContext &getContext() const override { return ctx; }
288
289private:
290 // This "intraprocedural" option is related to the CDG construction, not the dataflow analysis
291 // itself.
292 CDGAnalysisContext ctx = {.runIntraprocedural = false};
293};
294
295} // namespace llzk
Convenience classes for a frequent pattern of dataflow analysis used in LLZK, where an analysis is ru...
This file provides LLZK's sparse forward data-flow analysis compatibility layer.
~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
Propagate references across a call treated as external.
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 through an operation.
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:146
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