24#include <mlir/Analysis/DataFlowFramework.h>
25#include <mlir/Dialect/Arith/IR/Arith.h>
26#include <mlir/Pass/AnalysisManager.h>
28#include <llvm/ADT/ArrayRef.h>
29#include <llvm/ADT/DynamicAPInt.h>
30#include <llvm/ADT/EquivalenceClasses.h>
31#include <llvm/ADT/TypeSwitch.h>
34#include <unordered_set>
44 using IndexRange = std::pair<llvm::DynamicAPInt, llvm::DynamicAPInt>;
53 SourceRefIndex(
const llvm::DynamicAPInt &low,
const llvm::DynamicAPInt &high)
54 : index(IndexRange {low, high}) {}
64 return std::holds_alternative<SymbolLookupResult<component::MemberDefOp>>(index) ||
65 std::holds_alternative<component::MemberDefOp>(index);
68 ensure(
isMember(),
"SourceRefIndex: member requested but not contained");
69 if (std::holds_alternative<component::MemberDefOp>(index)) {
70 return std::get<component::MemberDefOp>(index);
72 return std::get<SymbolLookupResult<component::MemberDefOp>>(index).get();
75 bool isPodRecord()
const {
return std::holds_alternative<mlir::StringAttr>(index); }
78 return std::get<mlir::StringAttr>(index);
82 bool isIndex()
const {
return std::holds_alternative<llvm::DynamicAPInt>(index); }
84 ensure(
isIndex(),
"SourceRefIndex: index requested but not contained");
85 return std::get<llvm::DynamicAPInt>(index);
88 bool isIndexRange()
const {
return std::holds_alternative<IndexRange>(index); }
91 return std::get<IndexRange>(index);
95 void print(mlir::raw_ostream &os)
const;
106 return index == rhs.index;
131 llvm::DynamicAPInt, IndexRange>
135static inline mlir::raw_ostream &
operator<<(mlir::raw_ostream &os,
const SourceRefIndex &rhs) {
154 using Path = std::vector<SourceRefIndex>;
160 enum class SortCategory : std::uint8_t {
170 template <
typename OpT>
static mlir::Value getSingleResultValue(OpT op) {
171 ensure(op,
"SourceRef requires a non-null operation");
172 ensure(op->getNumResults() == 1,
"SourceRef expects a single-result operation");
173 return op->getResult(0);
176 static mlir::Value getRootResultValue(mlir::OpResult result) {
179 felt::FeltConstantOp, mlir::arith::ConstantIndexOp, mlir::arith::ConstantIntOp,
180 polymorphic::ConstReadOp>(result.getOwner()),
181 "SourceRef rooted OpResult constructors must not be used for constant values"
186 template <
typename OpT> mlir::FailureOr<OpT> getDefiningOp()
const {
187 if (
auto op = llvm::dyn_cast_if_present<OpT>(value.getDefiningOp())) {
190 return mlir::failure();
193 SourceRef(mlir::Value sourceValue,
bool isConstantStorage,
Path sourcePath = {})
194 : value(sourceValue), path(std::move(sourcePath)), constant(isConstantStorage) {
195 ensure(value !=
nullptr,
"SourceRef requires a non-null value");
196 ensure(!constant || this->path.empty(),
"constant SourceRef cannot have a path");
199 Path &getPathMut() {
return path; }
200 const void *getAsOpaquePointer()
const {
return value.getAsOpaquePointer(); }
201 SortCategory getSortCategory()
const;
204 llvm::DynamicAPInt getConstantIdentityValue()
const;
205 llvm::StringRef getTemplateConstantName()
const;
206 std::strong_ordering compareWithinCategory(
const SourceRef &rhs, SortCategory category)
const;
210 static std::vector<SourceRef>
214 static std::vector<SourceRef>
219 static std::vector<SourceRef>
227 :
SourceRef(getSingleResultValue(createOp), false, std::move(p)) {}
229 :
SourceRef(getSingleResultValue(nondet), false, std::move(p)) {}
231 :
SourceRef(getRootResultValue(rootResult), false, std::move(p)) {}
236 : SourceRef(getSingleResultValue(c), true) {}
238 : SourceRef(getSingleResultValue(c), true) {}
240 : SourceRef(getSingleResultValue(c), true) {}
245 return isConstant() && llvm::isa_and_present<felt::FeltConstantOp>(value.getDefiningOp());
249 llvm::isa_and_present<mlir::arith::ConstantIndexOp>(value.getDefiningOp());
253 return isConstant() && llvm::isa_and_present<polymorphic::ConstReadOp>(value.getDefiningOp());
270 mlir::FailureOr<mlir::Value>
getRoot()
const {
274 return mlir::failure();
280 return mlir::failure();
283 if (
auto blockArg = llvm::dyn_cast<mlir::BlockArgument>(value)) {
286 return mlir::failure();
290 if (succeeded(blockArg)) {
291 return blockArg->getArgNumber();
293 return mlir::failure();
298 return getDefiningOp<component::CreateStructOp>();
302 mlir::FailureOr<NonDetOp>
getNonDetOp()
const {
return getDefiningOp<NonDetOp>(); }
305 mlir::FailureOr<function::CallOp>
getCallOp()
const {
return getDefiningOp<function::CallOp>(); }
309 auto feltConst = getDefiningOp<felt::FeltConstantOp>();
310 if (succeeded(feltConst)) {
311 if (
auto reduced = feltConst->getValue().getReducedValue()) {
315 return mlir::failure();
318 auto indexConst = getDefiningOp<mlir::arith::ConstantIndexOp>();
319 if (succeeded(indexConst)) {
320 return llvm::DynamicAPInt(indexConst->value());
322 return mlir::failure();
331 if (succeeded(feltVal)) {
335 if (succeeded(indexVal)) {
338 return mlir::failure();
360 mlir::FailureOr<std::vector<SourceRefIndex>>
getSuffix(
const SourceRef &prefix)
const;
373 return mlir::failure();
376 copy.getPathMut().pop_back();
381 std::vector<SourceRef>
386 return mlir::failure();
389 copy.getPathMut().push_back(r);
393 mlir::FailureOr<SourceRef>
createChild(
const SourceRef &other)
const {
395 if (failed(idxVal)) {
396 return mlir::failure();
401 [[deprecated(
"Use getPath() instead")]]
406 llvm::ArrayRef<SourceRefIndex>
getPath()
const {
return path; }
410 void print(mlir::raw_ostream &os)
const;
415 bool operator!=(
const SourceRef &rhs)
const {
return !(*
this == rhs); }
421 size_t operator()(
const SourceRef &val)
const;
424 friend struct llvm::DenseMapInfo<SourceRef>;
436class SourceRefSet :
public std::unordered_set<SourceRef, SourceRef::Hash> {
437 using Base = std::unordered_set<SourceRef, SourceRef::Hash>;
449 "SourceRefSet must satisfy the ScalarLatticeValue requirements"
456template <>
struct DenseMapInfo<
llzk::SourceRef> {
This file implements helpers for DynamicAPInt operations and conversions that LLVM does not provide.
Information about the prime finite field used for the interval analysis.
void print(llvm::raw_ostream &os) const
Defines an index into an LLZK object.
std::strong_ordering operator<=>(const SourceRefIndex &rhs) const
bool operator==(const SourceRefIndex &rhs) const
bool isIndexRange() const
mlir::StringAttr getPodRecordNameAttr() const
SourceRefIndex(const llvm::DynamicAPInt &i)
static SourceRefIndex forArrayDimension(int64_t size)
Select every nonnegative index in a static or dynamic array dimension.
bool hasUnboundedUpperBound() const
Whether this range has no finite upper bound.
llvm::DynamicAPInt getIndex() const
void print(mlir::raw_ostream &os) const
IndexRange getIndexRange() const
bool overlaps(const SourceRefIndex &rhs) const
Return true when these path components select any common storage.
component::MemberDefOp getMember() const
SourceRefIndex(mlir::StringAttr recordName)
SourceRefIndex(const llvm::DynamicAPInt &low, const llvm::DynamicAPInt &high)
Construct a half-open range; a high bound of ShapedType::kDynamic means unbounded.
SourceRefIndex(SymbolLookupResult< component::MemberDefOp > f)
SourceRefIndex(IndexRange r)
SourceRefIndex(int64_t i)
SourceRefIndex(component::MemberDefOp f)
llvm::StringRef getPodRecordName() const
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.
bool isIntegerVal() const
bool isBlockArgument() const
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
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.
mlir::FailureOr< function::CallOp > getCallOp() const
void print(mlir::raw_ostream &os) const
Print this reference using source-style names.
bool isCallResult() const
bool operator==(const SourceRef &rhs) const
mlir::FailureOr< component::CreateStructOp > getCreateStructOp() const
bool isConstantFelt() const
SourceRef(felt::FeltConstantOp c)
SourceRef(component::CreateStructOp createOp, Path p={})
llvm::ArrayRef< SourceRefIndex > getPath() const
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={})
mlir::FailureOr< llvm::DynamicAPInt > getConstantFeltValue() const
Return the field representative, or failure for a nonconstant or unspecified field.
bool isConstantIndex() const
std::vector< SourceRefIndex > Path
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
Return a known numeric value, failing for fieldless felt constants.
mlir::FailureOr< unsigned > getInputNum() const
SourceRef narrowRanges(const SourceRef &rhs) const
Return a copy with ranged array indices narrowed by concrete indices from rhs.
mlir::FailureOr< NonDetOp > getNonDetOp() const
llvm::ArrayRef< SourceRefIndex > getPieces() const
SourceRef(mlir::arith::ConstantIndexOp c)
mlir::FailureOr< mlir::BlockArgument > getBlockArgument() const
SourceRef(NonDetOp nondet, Path p={})
SourceRef(mlir::OpResult rootResult, Path p={})
mlir::FailureOr< SourceRef > createChild(const SourceRef &other) const
mlir::FailureOr< llvm::DynamicAPInt > getConstantIndexValue() const
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...
mlir::FailureOr< mlir::Value > getConstant() const
SourceRef(polymorphic::ConstReadOp c)
bool isTemplateConstant() const
Return whether this reference originates from a template constant read.
bool isTypeVarVal() const
bool operator!=(const SourceRef &rhs) const
mlir::FailureOr< mlir::Value > getRoot() const
bool isConstantInt() const
bool isCreateStructOp() const
mlir::Type getType() const
ExpressionValue mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
void ensure(bool condition, const llvm::Twine &errMsg)
Interval operator<<(const Interval &lhs, const Interval &rhs)
static bool isEqual(const llzk::SourceRef &lhs, const llzk::SourceRef &rhs)
static unsigned getHashValue(const llzk::SourceRef &ref)
size_t operator()(const SourceRefIndex &c) const
size_t operator()(const SourceRef &val) const