21#include <mlir/Analysis/DataFlow/DeadCodeAnalysis.h>
22#include <mlir/IR/Value.h>
24#include <llvm/Support/Debug.h>
27#include <unordered_set>
29#define DEBUG_TYPE "llzk-constrain-ref-lattice"
36using namespace component;
39using namespace polymorphic;
54 return getScalarValue().erase(ref) != 0 ? mlir::ChangeResult::Change
55 : mlir::ChangeResult::NoChange;
58 mlir::ChangeResult changed = mlir::ChangeResult::NoChange;
60 changed |= element->remove(ref);
65std::pair<SourceRefLatticeValue, mlir::ChangeResult>
68 auto res = mlir::ChangeResult::NoChange;
69 if (newVal.isScalar()) {
70 res = newVal.translateScalar(translation);
72 for (
auto &elem : newVal.getArrayValue()) {
73 auto [newElem, elemRes] = elem->translate(translation);
81std::pair<SourceRefLatticeValue, mlir::ChangeResult>
84 auto res = mlir::ChangeResult::NoChange;
85 if (newVal.isScalar()) {
86 res = newVal.replacePrefixesScalar(translation);
88 for (
auto &elem : newVal.getArrayValue()) {
89 auto [newElem, elemRes] = elem->replacePrefixes(translation);
90 *elem = std::move(newElem);
101 ensure(
isArray(),
"SourceRef array write requires an array-shaped value");
104 std::vector<size_t> selected {0};
105 bool hasRangedIndex =
false;
106 for (
unsigned dimIdx = 0; dimIdx < indices.size(); ++dimIdx) {
109 std::vector<size_t> next;
111 const int64_t indexValue(idx.
getIndex());
112 for (
size_t prefix : selected) {
113 next.push_back(prefix * dim + indexValue);
117 hasRangedIndex =
true;
119 const int64_t lowValue(low), highValue(high);
120 for (int64_t indexValue = lowValue; indexValue < highValue; ++indexValue) {
121 for (
size_t prefix : selected) {
122 next.push_back(prefix * dim + indexValue);
126 selected = std::move(next);
130 selectedShape = selectedShape.drop_front(indices.size());
131 size_t chunkSize = 1;
132 for (int64_t dim : selectedShape) {
133 chunkSize *=
static_cast<size_t>(dim);
136 (selectedShape.empty() && rhs.
isScalar()) ||
138 "SourceRef array write value shape does not match selected storage"
141 ChangeResult changed = ChangeResult::NoChange;
142 const bool join = joinWithExisting || hasRangedIndex || selected.size() > 1;
143 for (
size_t chunkStart : selected) {
144 for (
size_t offset = 0; offset < chunkSize; ++offset) {
153mlir::FailureOr<std::pair<SourceRefLatticeValue, mlir::ChangeResult>>
156 auto transform = [&idx](
const SourceRef &r) -> mlir::FailureOr<SourceRef> {
157 return r.createChild(idx);
162mlir::FailureOr<std::pair<SourceRefLatticeValue, mlir::ChangeResult>>
165 auto transform = [&idx](
const SourceRef &r) -> mlir::FailureOr<SourceRef> {
166 return r.createChild(idx);
171mlir::FailureOr<std::pair<SourceRefLatticeValue, mlir::ChangeResult>>
177 std::vector<size_t> currIdxs {0};
178 for (
unsigned i = 0; i < indices.size(); i++) {
179 const auto &idx = indices[i];
182 std::vector<size_t> newIdxs;
183 ensure(idx.isIndex() || idx.isIndexRange(),
"wrong type of index for array");
185 int64_t idxVal(idx.getIndex());
187 currIdxs.begin(), currIdxs.end(), std::back_inserter(newIdxs),
188 [&currDim, &idxVal](
size_t j) { return j * currDim + idxVal; }
191 auto [low, high] = idx.getIndexRange();
192 int64_t lowInt(low), highInt(high);
193 for (int64_t idxVal = lowInt; idxVal < highInt; idxVal++) {
195 currIdxs.begin(), currIdxs.end(), std::back_inserter(newIdxs),
196 [&currDim, &idxVal](
size_t j) { return j * currDim + idxVal; }
203 std::vector<int64_t> newArrayDims;
207 newArrayDims.push_back(dim);
210 if (newArrayDims.empty()) {
213 for (
auto idx : currIdxs) {
216 return std::make_pair(extractedVal, mlir::ChangeResult::Change);
220 for (
auto chunkStart : currIdxs) {
221 for (
size_t i = 0; i < chunkSz; i++) {
225 return std::make_pair(extractedVal, mlir::ChangeResult::Change);
228 auto currVal = *
this;
229 auto res = mlir::ChangeResult::NoChange;
230 for (
const auto &idx : indices) {
231 auto transform = [&idx](
const SourceRef &r) -> mlir::FailureOr<SourceRef> {
232 return r.createChild(idx);
234 auto transformedVal = currVal.elementwiseTransform(transform);
235 if (failed(transformedVal)) {
236 return mlir::failure();
238 auto [newVal, transformRes] = *transformedVal;
239 currVal = std::move(newVal);
242 return std::make_pair(currVal, res);
247 auto res = mlir::ChangeResult::NoChange;
255 for (
const SourceRef &currRef : currVal) {
256 for (
const auto &[prefix, replacementVal] : translation) {
257 if (currRef.isValidPrefix(prefix)) {
258 for (
const SourceRef &replacementPrefix : replacementVal.foldToScalar()) {
259 auto translatedRefRes = currRef.translate(prefix, replacementPrefix);
260 if (succeeded(translatedRefRes)) {
261 res |=
insert(*translatedRefRes);
273 for (
const SourceRef ¤tRef : current) {
274 bool matched =
false;
275 for (
const auto &[prefix, replacementVal] : translation) {
276 if (!currentRef.isValidPrefix(prefix)) {
280 for (
const SourceRef &replacementPrefix : replacementVal.foldToScalar()) {
281 auto translated = currentRef.translate(prefix, replacementPrefix);
282 if (succeeded(translated)) {
283 replaced.insert(*translated);
288 replaced.insert(currentRef);
291 if (replaced == current) {
292 return ChangeResult::NoChange;
295 return ChangeResult::Change;
298mlir::FailureOr<std::pair<SourceRefLatticeValue, mlir::ChangeResult>>
300 llvm::function_ref<mlir::FailureOr<SourceRef>(
const SourceRef &)> transform
303 auto res = mlir::ChangeResult::NoChange;
304 if (newVal.isScalar()) {
306 for (
const auto &ref : newVal.getScalarValue()) {
307 auto transformedRef = transform(ref);
308 if (failed(transformedRef)) {
309 return mlir::failure();
311 auto [_, inserted] = indexed.insert(*transformedRef);
313 res |= mlir::ChangeResult::Change;
316 newVal.getScalarValue() = indexed;
318 for (
auto &elem : newVal.getArrayValue()) {
319 auto transformedElem = elem->elementwiseTransform(transform);
320 if (failed(transformedElem)) {
321 return mlir::failure();
323 auto [newElem, elemRes] = *transformedElem;
324 (*elem) = std::move(newElem);
328 return std::make_pair(newVal, res);
339 if (
auto blockArg = llvm::dyn_cast<mlir::BlockArgument>(val)) {
341 }
else if (
auto *defOp = val.getDefiningOp()) {
342 if (
auto feltConst = llvm::dyn_cast<FeltConstantOp>(defOp)) {
344 }
else if (
auto constIdx = llvm::dyn_cast<mlir::arith::ConstantIndexOp>(defOp)) {
346 }
else if (
auto readConst = llvm::dyn_cast<ConstReadOp>(defOp)) {
348 }
else if (
auto structNew = llvm::dyn_cast<CreateStructOp>(defOp)) {
350 }
else if (
auto nonDet = llvm::dyn_cast<NonDetOp>(defOp)) {
352 }
else if (llvm::isa<global::GlobalReadOp>(defOp)) {
353 return SourceRef(llvm::cast<mlir::OpResult>(val));
354 }
else if (
auto createArray = llvm::dyn_cast<CreateArrayOp>(defOp)) {
355 return SourceRef(createArray->getResult(0));
356 }
else if (
auto newPod = llvm::dyn_cast<NewPodOp>(defOp)) {
358 }
else if (llvm::isa<function::CallOp>(defOp)) {
359 auto callResult = llvm::dyn_cast<mlir::OpResult>(val);
360 ensure(callResult !=
nullptr,
"function.call value should be an OpResult");
364 return mlir::failure();
368 if (
auto asVal = llvm::dyn_cast_if_present<Value>(v)) {
370 if (mlir::succeeded(sourceRef)) {
383 if (rhsValue.isScalar() && rhsValue.getScalarValue().empty()) {
384 return ChangeResult::NoChange;
386 if (value.isScalar() && value.getScalarValue().empty()) {
387 return value.setValue(rhsValue);
389 return value.update(rhsValue);
393 llvm::report_fatal_error(
"meet operation is not supported for SourceRefLattice");
394 return ChangeResult::NoChange;
398 os <<
"SourceRefLattice { " << value <<
" }";
402 return value.setValue(newValue);
413raw_ostream &
operator<<(raw_ostream &os, llvm::PointerUnion<mlir::Value, mlir::Operation *> ptr) {
414 if (
auto asVal = llvm::dyn_cast_if_present<Value>(ptr)) {
416 }
else if (
auto *asOp = llvm::dyn_cast_if_present<Operation *>(ptr)) {
419 os <<
"<<null PointerUnion>>";
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation source
Defines an index into an LLZK object.
bool isIndexRange() const
llvm::DynamicAPInt getIndex() const
IndexRange getIndexRange() const
A value at a given point of the SourceRefLattice.
mlir::ChangeResult write(const std::vector< SourceRefIndex > &indices, const SourceRefLatticeValue &rhs, bool joinWithExisting=false)
Update the element or subarray selected by indices.
mlir::FailureOr< std::pair< SourceRefLatticeValue, mlir::ChangeResult > > referencePodRecord(mlir::StringAttr recordName) const
Add the given pod recordName to the SourceRefs contained within this value.
mlir::ChangeResult remove(const SourceRef &ref)
Remove ref from this value's reference set or, for an array, from every element.
mlir::ChangeResult replacePrefixesScalar(const TranslationMap &translation)
Replace matching prefixes in a scalar value without dropping unmatched references.
const std::vector< int64_t > & getArrayShape() const
Return the dimensions of this array-shaped value.
mlir::FailureOr< std::pair< SourceRefLatticeValue, mlir::ChangeResult > > referenceMember(SymbolLookupResult< component::MemberDefOp > memberRef) const
Add the given memberRef to the SourceRefs contained within this value.
virtual mlir::FailureOr< std::pair< SourceRefLatticeValue, mlir::ChangeResult > > elementwiseTransform(llvm::function_ref< mlir::FailureOr< SourceRef >(const SourceRef &)> transform) const
Perform a recursive transformation over all elements of this value and return a new value with the mo...
mlir::ChangeResult insert(const SourceRef &rhs)
Directly insert the ref into this value.
std::pair< SourceRefLatticeValue, mlir::ChangeResult > translate(const TranslationMap &translation) const
Translate contained references using translation and return the transformed value.
mlir::FailureOr< std::pair< SourceRefLatticeValue, mlir::ChangeResult > > extract(const std::vector< SourceRefIndex > &indices) const
Perform an array.extract or array.read operation, depending on how many indices are provided.
SourceRefLatticeValue(ScalarTy s)
mlir::ChangeResult translateScalar(const TranslationMap &translation)
Translate this value using the translation map, assuming this value is a scalar.
std::pair< SourceRefLatticeValue, mlir::ChangeResult > replacePrefixes(const TranslationMap &translation) const
Replace matching SourceRef prefixes and leave unmatched references unchanged.
Sparse SSA-value lattice for SourceRef propagation.
mlir::ChangeResult join(const AbstractSparseLattice &rhs) override
mlir::ChangeResult setValue(const LatticeValue &newValue)
mlir::ChangeResult meet(const AbstractSparseLattice &rhs) override
static SourceRefLatticeValue getDefaultValue(ValueTy v)
void print(mlir::raw_ostream &os) const override
static mlir::FailureOr< SourceRef > getSourceRef(mlir::Value val)
If val is the source of other values (i.e., a block argument, an allocation-like op result,...
llvm::PointerUnion< mlir::Value, mlir::Operation * > ValueTy
SourceRefLatticeValue LatticeValue
A reference to a "source", which is the base value from which other SSA values are derived.
size_t getNumArrayDims() const
mlir::ChangeResult updateScalar(const ScalarTy &rhs)
int64_t getArrayDim(unsigned i) const
std::variant< ScalarTy, ArrayTy > & getValue()
mlir::ChangeResult setValue(const AbstractLatticeValue &rhs)
Sets this value to be equal to rhs.
mlir::ChangeResult update(const Derived &rhs)
Union this value with that of rhs.
mlir::ChangeResult foldAndUpdate(const SourceRefLatticeValue &rhs)
const ScalarTy & getScalarValue() const
const SourceRefLatticeValue & getElemFlatIdx(size_t i) const
const ArrayTy & getArrayValue() const
void print(mlir::raw_ostream &os) const
raw_ostream & operator<<(raw_ostream &os, llvm::PointerUnion< mlir::Value, mlir::Operation * > ptr)
void ensure(bool condition, const llvm::Twine &errMsg)
Interval operator<<(const Interval &lhs, const Interval &rhs)
std::unordered_map< SourceRef, SourceRefLatticeValue, SourceRef::Hash > TranslationMap