LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
SourceRef.h
Go to the documentation of this file.
1//===-- SourceRef.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
22#include "llzk/Util/Hash.h"
23
24#include <mlir/Analysis/DataFlowFramework.h>
25#include <mlir/Dialect/Arith/IR/Arith.h>
26#include <mlir/Pass/AnalysisManager.h>
27
28#include <llvm/ADT/ArrayRef.h>
29#include <llvm/ADT/DynamicAPInt.h>
30#include <llvm/ADT/EquivalenceClasses.h>
31#include <llvm/ADT/TypeSwitch.h>
32
33#include <compare>
34#include <unordered_set>
35#include <variant>
36#include <vector>
37
38namespace llzk {
39
44 using IndexRange = std::pair<llvm::DynamicAPInt, llvm::DynamicAPInt>;
45
46public:
47 explicit SourceRefIndex(component::MemberDefOp f) : index(f) {}
49 explicit SourceRefIndex(mlir::StringAttr recordName) : index(recordName) {}
50 explicit SourceRefIndex(const llvm::DynamicAPInt &i) : index(i) {}
51 explicit SourceRefIndex(const llvm::APInt &i) : index(toDynamicAPInt(i)) {}
52 explicit SourceRefIndex(int64_t i) : index(llvm::DynamicAPInt(i)) {}
53 SourceRefIndex(const llvm::APInt &low, const llvm::APInt &high)
54 : index(IndexRange {toDynamicAPInt(low), toDynamicAPInt(high)}) {}
55 explicit SourceRefIndex(IndexRange r) : index(r) {}
56
57 bool isMember() const {
58 return std::holds_alternative<SymbolLookupResult<component::MemberDefOp>>(index) ||
59 std::holds_alternative<component::MemberDefOp>(index);
60 }
62 ensure(isMember(), "SourceRefIndex: member requested but not contained");
63 if (std::holds_alternative<component::MemberDefOp>(index)) {
64 return std::get<component::MemberDefOp>(index);
65 }
66 return std::get<SymbolLookupResult<component::MemberDefOp>>(index).get();
67 }
68
69 bool isPodRecord() const { return std::holds_alternative<mlir::StringAttr>(index); }
70 mlir::StringAttr getPodRecordNameAttr() const {
71 ensure(isPodRecord(), "SourceRefIndex: pod record requested but not contained");
72 return std::get<mlir::StringAttr>(index);
73 }
74 llvm::StringRef getPodRecordName() const { return getPodRecordNameAttr().getValue(); }
75
76 bool isIndex() const { return std::holds_alternative<llvm::DynamicAPInt>(index); }
77 llvm::DynamicAPInt getIndex() const {
78 ensure(isIndex(), "SourceRefIndex: index requested but not contained");
79 return std::get<llvm::DynamicAPInt>(index);
80 }
81
82 bool isIndexRange() const { return std::holds_alternative<IndexRange>(index); }
83 IndexRange getIndexRange() const {
84 ensure(isIndexRange(), "SourceRefIndex: index range requested but not contained");
85 return std::get<IndexRange>(index);
86 }
87
88 inline void dump() const { print(llvm::errs()); }
89 void print(mlir::raw_ostream &os) const;
90
91 inline bool operator==(const SourceRefIndex &rhs) const {
92 if (isMember() && rhs.isMember()) {
93 // We compare the underlying members, since the member could be in a symbol
94 // lookup or not.
95 return getMember() == rhs.getMember();
96 }
97 if (isIndex() && rhs.isIndex()) {
98 return getIndex() == rhs.getIndex();
99 }
100 return index == rhs.index;
101 }
102
105 bool overlaps(const SourceRefIndex &rhs) const;
106
107 std::strong_ordering operator<=>(const SourceRefIndex &rhs) const;
108
109 struct Hash {
110 size_t operator()(const SourceRefIndex &c) const;
111 };
112
113 inline size_t getHash() const { return Hash {}(*this); }
114
115private:
123 std::variant<
125 llvm::DynamicAPInt, IndexRange>
126 index;
127};
128
129static inline mlir::raw_ostream &operator<<(mlir::raw_ostream &os, const SourceRefIndex &rhs) {
130 rhs.print(os);
131 return os;
132}
133
146class SourceRef {
147public:
148 using Path = std::vector<SourceRefIndex>;
149
150private:
151 // Sort in the following order:
152 // block arg < struct.new < nondet < other rooted result < template const < const index <
153 // const felt.
154 enum class SortCategory : std::uint8_t {
155 BlockArgument,
156 CreateStruct,
157 NonDet,
158 RootResult,
159 TemplateConstant,
160 ConstantIndex,
161 ConstantFelt,
162 };
163
164 template <typename OpT> static mlir::Value getSingleResultValue(OpT op) {
165 ensure(op, "SourceRef requires a non-null operation");
166 ensure(op->getNumResults() == 1, "SourceRef expects a single-result operation");
167 return op->getResult(0);
168 }
169
170 static mlir::Value getRootResultValue(mlir::OpResult result) {
171 ensure(
172 !llvm::isa<
173 felt::FeltConstantOp, mlir::arith::ConstantIndexOp, mlir::arith::ConstantIntOp,
174 polymorphic::ConstReadOp>(result.getOwner()),
175 "SourceRef rooted OpResult constructors must not be used for constant values"
176 );
177 return result;
178 }
179
180 template <typename OpT> mlir::FailureOr<OpT> getDefiningOp() const {
181 if (auto op = llvm::dyn_cast_if_present<OpT>(value.getDefiningOp())) {
182 return op;
183 }
184 return mlir::failure();
185 }
186
187 SourceRef(mlir::Value sourceValue, bool isConstantStorage, Path sourcePath = {})
188 : value(sourceValue), path(std::move(sourcePath)), constant(isConstantStorage) {
189 ensure(value != nullptr, "SourceRef requires a non-null value");
190 ensure(!constant || this->path.empty(), "constant SourceRef cannot have a path");
191 }
192
193 Path &getPathMut() { return path; }
194 const void *getAsOpaquePointer() const { return value.getAsOpaquePointer(); }
195 SortCategory getSortCategory() const;
196 llvm::StringRef getTemplateConstantName() const;
197 std::strong_ordering compareWithinCategory(const SourceRef &rhs, SortCategory category) const;
198
199public:
201 static std::vector<SourceRef>
202 getAllSourceRefs(mlir::SymbolTableCollection &tables, mlir::ModuleOp mod, const SourceRef &root);
203
205 static std::vector<SourceRef>
207
210 static std::vector<SourceRef>
212
213 /* Rooted constructors */
214
215 SourceRef(mlir::BlockArgument b, Path p = {})
216 : SourceRef(b, /*isConstantStorage=*/false, std::move(p)) {}
218 : SourceRef(getSingleResultValue(createOp), /*isConstantStorage=*/false, std::move(p)) {}
219 SourceRef(NonDetOp nondet, Path p = {})
220 : SourceRef(getSingleResultValue(nondet), /*isConstantStorage=*/false, std::move(p)) {}
221 SourceRef(mlir::OpResult rootResult, Path p = {})
222 : SourceRef(getRootResultValue(rootResult), /*isConstantStorage=*/false, std::move(p)) {}
223
224 /* Constant constructors */
225
227 : SourceRef(getSingleResultValue(c), /*isConstantStorage=*/true) {}
228 explicit SourceRef(mlir::arith::ConstantIndexOp c)
229 : SourceRef(getSingleResultValue(c), /*isConstantStorage=*/true) {}
231 : SourceRef(getSingleResultValue(c), /*isConstantStorage=*/true) {}
232
233 mlir::Type getType() const;
234
235 bool isConstantFelt() const {
236 return isConstant() && llvm::isa_and_present<felt::FeltConstantOp>(value.getDefiningOp());
237 }
238 bool isConstantIndex() const {
239 return isConstant() &&
240 llvm::isa_and_present<mlir::arith::ConstantIndexOp>(value.getDefiningOp());
241 }
242 bool isTemplateConstant() const {
243 return isConstant() && llvm::isa_and_present<polymorphic::ConstReadOp>(value.getDefiningOp());
244 }
245
246 bool isConstant() const { return constant; }
247 bool isConstantInt() const { return isConstantFelt() || isConstantIndex(); }
248
249 bool isFeltVal() const { return llvm::isa<felt::FeltType>(getType()); }
250 bool isIndexVal() const { return llvm::isa<mlir::IndexType>(getType()); }
251 bool isIntegerVal() const { return llvm::isa<mlir::IntegerType>(getType()); }
252 bool isTypeVarVal() const { return llvm::isa<polymorphic::TypeVarType>(getType()); }
253 bool isScalar() const {
254 return isConstant() || isFeltVal() || isIndexVal() || isIntegerVal() || isTypeVarVal();
255 }
256
257 bool isRooted() const { return !constant; }
258 bool isBlockArgument() const { return isRooted() && llvm::isa<mlir::BlockArgument>(value); }
259 mlir::FailureOr<mlir::Value> getRoot() const {
260 if (isRooted()) {
261 return value;
262 }
263 return mlir::failure();
264 }
265 mlir::FailureOr<mlir::Value> getConstant() const {
266 if (isConstant()) {
267 return value;
268 }
269 return mlir::failure();
270 }
271 mlir::FailureOr<mlir::BlockArgument> getBlockArgument() const {
272 if (auto blockArg = llvm::dyn_cast<mlir::BlockArgument>(value)) {
273 return blockArg;
274 }
275 return mlir::failure();
276 }
277 mlir::FailureOr<unsigned> getInputNum() const {
278 auto blockArg = getBlockArgument();
279 if (succeeded(blockArg)) {
280 return blockArg->getArgNumber();
281 }
282 return mlir::failure();
283 }
284
285 bool isCreateStructOp() const { return succeeded(getCreateStructOp()); }
286 mlir::FailureOr<component::CreateStructOp> getCreateStructOp() const {
287 return getDefiningOp<component::CreateStructOp>();
288 }
289
290 bool isNonDetOp() const { return succeeded(getNonDetOp()); }
291 mlir::FailureOr<NonDetOp> getNonDetOp() const { return getDefiningOp<NonDetOp>(); }
292
293 bool isCallResult() const { return succeeded(getCallOp()); }
294 mlir::FailureOr<function::CallOp> getCallOp() const { return getDefiningOp<function::CallOp>(); }
295
296 mlir::FailureOr<llvm::DynamicAPInt> getConstantFeltValue() const {
297 auto feltConst = getDefiningOp<felt::FeltConstantOp>();
298 if (succeeded(feltConst)) {
299 llvm::APInt i = feltConst->getValue();
300 return toDynamicAPInt(i);
301 }
302 return mlir::failure();
303 }
304 mlir::FailureOr<llvm::DynamicAPInt> getConstantIndexValue() const {
305 auto indexConst = getDefiningOp<mlir::arith::ConstantIndexOp>();
306 if (succeeded(indexConst)) {
307 return llvm::DynamicAPInt(indexConst->value());
308 }
309 return mlir::failure();
310 }
311 mlir::FailureOr<llvm::DynamicAPInt> getConstantValue() const {
312 auto feltVal = getConstantFeltValue();
313 if (succeeded(feltVal)) {
314 return *feltVal;
315 }
316 auto indexVal = getConstantIndexValue();
317 if (succeeded(indexVal)) {
318 return *indexVal;
319 }
320 return mlir::failure();
321 }
322
324 bool isValidPrefix(const SourceRef &prefix) const;
325
327 bool overlaps(const SourceRef &rhs) const;
328
333 mlir::FailureOr<std::vector<SourceRefIndex>> getSuffix(const SourceRef &prefix) const;
334
341 mlir::FailureOr<SourceRef> translate(const SourceRef &prefix, const SourceRef &other) const;
342
344 mlir::FailureOr<SourceRef> getParentPrefix() const {
345 if (!isRooted() || getPath().empty()) {
346 return mlir::failure();
347 }
348 auto copy = *this;
349 copy.getPathMut().pop_back();
350 return copy;
351 }
352
354 std::vector<SourceRef>
355 getAllChildren(mlir::SymbolTableCollection &tables, mlir::ModuleOp mod) const;
356
357 mlir::FailureOr<SourceRef> createChild(const SourceRefIndex &r) const {
358 if (!isRooted()) {
359 return mlir::failure();
360 }
361 auto copy = *this;
362 copy.getPathMut().push_back(r);
363 return copy;
364 }
365
366 mlir::FailureOr<SourceRef> createChild(const SourceRef &other) const {
367 auto idxVal = other.getConstantIndexValue();
368 if (failed(idxVal)) {
369 return mlir::failure();
370 }
371 return createChild(SourceRefIndex(*idxVal));
372 }
373
374 [[deprecated("Use getPath() instead")]]
375 // NOTE: When this function is removed, do not delete it, rewrite as `... = delete`.
376 llvm::ArrayRef<SourceRefIndex> getPieces() const {
377 return path;
378 }
379 llvm::ArrayRef<SourceRefIndex> getPath() const { return path; }
380
381 void print(mlir::raw_ostream &os) const;
382 void dump() const { print(llvm::errs()); }
383
384 bool operator==(const SourceRef &rhs) const;
385
386 bool operator!=(const SourceRef &rhs) const { return !(*this == rhs); }
387
388 // required for EquivalenceClasses usage
389 std::strong_ordering operator<=>(const SourceRef &rhs) const;
390
391 struct Hash {
392 size_t operator()(const SourceRef &val) const;
393 };
394
395 friend struct llvm::DenseMapInfo<SourceRef>;
396
397private:
398 mlir::Value value;
399 Path path;
400 bool constant;
401};
402
403mlir::raw_ostream &operator<<(mlir::raw_ostream &os, const SourceRef &rhs);
404
405/* SourceRefSet */
406
407class SourceRefSet : public std::unordered_set<SourceRef, SourceRef::Hash> {
408 using Base = std::unordered_set<SourceRef, SourceRef::Hash>;
409
410public:
411 using Base::Base;
412
413 SourceRefSet &join(const SourceRefSet &rhs);
414
415 friend mlir::raw_ostream &operator<<(mlir::raw_ostream &os, const SourceRefSet &rhs);
416};
417
418static_assert(
420 "SourceRefSet must satisfy the ScalarLatticeValue requirements"
421);
422
423} // namespace llzk
424
425namespace llvm {
426
427template <> struct DenseMapInfo<llzk::SourceRef> {
429 return llzk::SourceRef(mlir::BlockArgument(reinterpret_cast<mlir::detail::ValueImpl *>(1)));
430 }
432 return llzk::SourceRef(mlir::BlockArgument(reinterpret_cast<mlir::detail::ValueImpl *>(2)));
433 }
434 static unsigned getHashValue(const llzk::SourceRef &ref) {
435 if (ref == getEmptyKey() || ref == getTombstoneKey()) {
436 return llvm::hash_value(ref.getAsOpaquePointer());
437 }
438 return llzk::SourceRef::Hash {}(ref);
439 }
440 static bool isEqual(const llzk::SourceRef &lhs, const llzk::SourceRef &rhs) { return lhs == rhs; }
441};
442
443} // namespace llvm
This file implements helper methods for constructing DynamicAPInts.
void print(llvm::raw_ostream &os) const
Defines an index into an LLZK object.
Definition SourceRef.h:43
std::strong_ordering operator<=>(const SourceRefIndex &rhs) const
bool operator==(const SourceRefIndex &rhs) const
Definition SourceRef.h:91
bool isIndexRange() const
Definition SourceRef.h:82
size_t getHash() const
Definition SourceRef.h:113
bool isIndex() const
Definition SourceRef.h:76
mlir::StringAttr getPodRecordNameAttr() const
Definition SourceRef.h:70
bool isMember() const
Definition SourceRef.h:57
bool isPodRecord() const
Definition SourceRef.h:69
SourceRefIndex(const llvm::DynamicAPInt &i)
Definition SourceRef.h:50
SourceRefIndex(const llvm::APInt &low, const llvm::APInt &high)
Definition SourceRef.h:53
SourceRefIndex(const llvm::APInt &i)
Definition SourceRef.h:51
void dump() const
Definition SourceRef.h:88
llvm::DynamicAPInt getIndex() const
Definition SourceRef.h:77
void print(mlir::raw_ostream &os) const
Definition SourceRef.cpp:95
IndexRange getIndexRange() const
Definition SourceRef.h:83
bool overlaps(const SourceRefIndex &rhs) const
Return true when these path components select any common storage.
component::MemberDefOp getMember() const
Definition SourceRef.h:61
SourceRefIndex(mlir::StringAttr recordName)
Definition SourceRef.h:49
SourceRefIndex(SymbolLookupResult< component::MemberDefOp > f)
Definition SourceRef.h:48
SourceRefIndex(IndexRange r)
Definition SourceRef.h:55
SourceRefIndex(int64_t i)
Definition SourceRef.h:52
SourceRefIndex(component::MemberDefOp f)
Definition SourceRef.h:47
llvm::StringRef getPodRecordName() const
Definition SourceRef.h:74
SourceRefSet & join(const SourceRefSet &rhs)
friend mlir::raw_ostream & operator<<(mlir::raw_ostream &os, const SourceRefSet &rhs)
A reference to a "source", which is the base value from which other SSA values are derived.
Definition SourceRef.h:146
bool isIntegerVal() const
Definition SourceRef.h:251
bool isBlockArgument() const
Definition SourceRef.h:258
bool overlaps(const SourceRef &rhs) const
Return true when both references select overlapping storage at the same path depth.
mlir::FailureOr< SourceRef > createChild(const SourceRefIndex &r) const
Definition SourceRef.h:357
std::vector< SourceRef > getAllChildren(mlir::SymbolTableCollection &tables, mlir::ModuleOp mod) const
Get all direct children of this SourceRef, assuming this ref is not a scalar.
mlir::FailureOr< std::vector< SourceRefIndex > > getSuffix(const SourceRef &prefix) const
If prefix is a valid prefix of this reference, return the suffix that remains after removing the pref...
mlir::FailureOr< SourceRef > getParentPrefix() const
Create a new reference that is the immediate prefix of this reference if possible.
Definition SourceRef.h:344
mlir::FailureOr< function::CallOp > getCallOp() const
Definition SourceRef.h:294
void print(mlir::raw_ostream &os) const
bool isCallResult() const
Definition SourceRef.h:293
bool isScalar() const
Definition SourceRef.h:253
bool operator==(const SourceRef &rhs) const
mlir::FailureOr< component::CreateStructOp > getCreateStructOp() const
Definition SourceRef.h:286
bool isConstantFelt() const
Definition SourceRef.h:235
bool isRooted() const
Definition SourceRef.h:257
SourceRef(felt::FeltConstantOp c)
Definition SourceRef.h:226
SourceRef(component::CreateStructOp createOp, Path p={})
Definition SourceRef.h:217
llvm::ArrayRef< SourceRefIndex > getPath() const
Definition SourceRef.h:379
bool isValidPrefix(const SourceRef &prefix) const
Returns true iff prefix is a valid prefix of this reference.
std::strong_ordering operator<=>(const SourceRef &rhs) const
SourceRef(mlir::BlockArgument b, Path p={})
Definition SourceRef.h:215
mlir::FailureOr< llvm::DynamicAPInt > getConstantFeltValue() const
Definition SourceRef.h:296
bool isConstantIndex() const
Definition SourceRef.h:238
std::vector< SourceRefIndex > Path
Definition SourceRef.h:148
static std::vector< SourceRef > getAllSourceRefs(mlir::SymbolTableCollection &tables, mlir::ModuleOp mod, const SourceRef &root)
Produce all possible SourceRefs that are present starting from the given root.
mlir::FailureOr< llvm::DynamicAPInt > getConstantValue() const
Definition SourceRef.h:311
mlir::FailureOr< unsigned > getInputNum() const
Definition SourceRef.h:277
mlir::FailureOr< NonDetOp > getNonDetOp() const
Definition SourceRef.h:291
void dump() const
Definition SourceRef.h:382
llvm::ArrayRef< SourceRefIndex > getPieces() const
Definition SourceRef.h:376
SourceRef(mlir::arith::ConstantIndexOp c)
Definition SourceRef.h:228
mlir::FailureOr< mlir::BlockArgument > getBlockArgument() const
Definition SourceRef.h:271
bool isIndexVal() const
Definition SourceRef.h:250
SourceRef(NonDetOp nondet, Path p={})
Definition SourceRef.h:219
SourceRef(mlir::OpResult rootResult, Path p={})
Definition SourceRef.h:221
mlir::FailureOr< SourceRef > createChild(const SourceRef &other) const
Definition SourceRef.h:366
mlir::FailureOr< llvm::DynamicAPInt > getConstantIndexValue() const
Definition SourceRef.h:304
mlir::FailureOr< SourceRef > translate(const SourceRef &prefix, const SourceRef &other) const
Create a new reference with prefix replaced with other iff prefix is a valid prefix for this referenc...
bool isNonDetOp() const
Definition SourceRef.h:290
mlir::FailureOr< mlir::Value > getConstant() const
Definition SourceRef.h:265
SourceRef(polymorphic::ConstReadOp c)
Definition SourceRef.h:230
bool isTemplateConstant() const
Definition SourceRef.h:242
bool isTypeVarVal() const
Definition SourceRef.h:252
bool isConstant() const
Definition SourceRef.h:246
bool operator!=(const SourceRef &rhs) const
Definition SourceRef.h:386
mlir::FailureOr< mlir::Value > getRoot() const
Definition SourceRef.h:259
bool isFeltVal() const
Definition SourceRef.h:249
bool isConstantInt() const
Definition SourceRef.h:247
bool isCreateStructOp() const
Definition SourceRef.h:285
mlir::Type getType() const
ExpressionValue mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
void ensure(bool condition, const llvm::Twine &errMsg)
DynamicAPInt toDynamicAPInt(StringRef str)
Interval operator<<(const Interval &lhs, const Interval &rhs)
static bool isEqual(const llzk::SourceRef &lhs, const llzk::SourceRef &rhs)
Definition SourceRef.h:440
static unsigned getHashValue(const llzk::SourceRef &ref)
Definition SourceRef.h:434
static llzk::SourceRef getTombstoneKey()
Definition SourceRef.h:431
static llzk::SourceRef getEmptyKey()
Definition SourceRef.h:428
size_t operator()(const SourceRefIndex &c) const
size_t operator()(const SourceRef &val) const