25#include <mlir/Dialect/SCF/IR/SCF.h>
26#include <mlir/IR/BuiltinOps.h>
28#include <llvm/ADT/ArrayRef.h>
29#include <llvm/ADT/DenseMap.h>
30#include <llvm/ADT/DenseMapInfo.h>
31#include <llvm/ADT/SmallVector.h>
32#include <llvm/Support/Debug.h>
39#define GEN_PASS_DEF_REDUNDANTREADANDWRITEELIMINATIONPASS
50#define DEBUG_TYPE "llzk-redundant-read-write-pass"
58 explicit ReferenceID(Value v) {
60 if (v == llvm::DenseMapInfo<Value>::getEmptyKey() ||
61 v == llvm::DenseMapInfo<Value>::getTombstoneKey()) {
63 }
else if (
auto constVal = dyn_cast_if_present<FeltConstantOp>(v.getDefiningOp())) {
64 identifier = constVal.getValue();
65 }
else if (
auto constIdxVal = dyn_cast_if_present<arith::ConstantIndexOp>(v.getDefiningOp())) {
66 identifier = llvm::cast<IntegerAttr>(constIdxVal.getValue()).getValue();
71 explicit ReferenceID(Attribute attr) : identifier(attr) {}
72 explicit ReferenceID(
const APInt &i) : identifier(i) {}
73 explicit ReferenceID(
unsigned i) : identifier(APInt(64, i)) {}
75 bool isValue()
const {
return std::holds_alternative<Value>(identifier); }
76 bool isAttribute()
const {
return std::holds_alternative<Attribute>(identifier); }
77 bool isConst()
const {
return std::holds_alternative<APInt>(identifier); }
79 Value getValue()
const {
80 ensure(isValue(),
"does not hold Value");
81 return std::get<Value>(identifier);
84 Attribute getAttribute()
const {
85 ensure(isAttribute(),
"does not hold Attribute");
86 return std::get<Attribute>(identifier);
89 APInt getConst()
const {
90 ensure(isConst(),
"does not hold const");
91 return std::get<APInt>(identifier);
94 void print(raw_ostream &os)
const {
95 if (
const auto *v = std::get_if<Value>(&identifier)) {
96 if (
auto opres = dyn_cast<OpResult>(*v)) {
97 os <<
'%' << opres.getResultNumber();
101 }
else if (
const auto *attr = std::get_if<Attribute>(&identifier)) {
104 os << std::get<APInt>(identifier);
108 friend bool operator==(
const ReferenceID &lhs,
const ReferenceID &rhs) {
109 return lhs.identifier == rhs.identifier;
112 friend raw_ostream &
operator<<(raw_ostream &os,
const ReferenceID &
id) {
122 std::variant<Attribute, APInt, Value> identifier;
130template <>
struct DenseMapInfo<ReferenceID> {
131 static ReferenceID
getEmptyKey() {
return ReferenceID(DenseMapInfo<Value>::getEmptyKey()); }
133 return ReferenceID(DenseMapInfo<Value>::getTombstoneKey());
137 return hash_value(r.getValue());
138 }
else if (r.isAttribute()) {
139 return hash_value(r.getAttribute());
141 return hash_value(r.getConst());
143 static bool isEqual(
const ReferenceID &lhs,
const ReferenceID &rhs) {
return lhs == rhs; }
168 template <
typename IdType>
static std::shared_ptr<ReferenceNode>
create(IdType
id, Value v) {
169 ReferenceNode n(
id, v);
171 return std::make_shared<ReferenceNode>(std::move(n));
176 std::shared_ptr<ReferenceNode> clone(
bool withChildren =
true)
const {
177 ReferenceNode copy(identifier, storedValue);
178 copy.updateLastWrite(lastWrite);
180 copy.dynamicChildCount = dynamicChildCount;
181 for (
const auto &[
id, child] : children) {
182 copy.children[id] = child->clone(withChildren);
185 return std::make_shared<ReferenceNode>(std::move(copy));
188 template <
typename IdType>
189 std::shared_ptr<ReferenceNode>
190 createChild(IdType
id, Value storedVal,
const std::shared_ptr<ReferenceNode> &valTree =
nullptr) {
191 std::shared_ptr<ReferenceNode> child =
create(
id, storedVal);
192 child->setCurrentValue(storedVal, valTree);
193 if (child->identifier.isValue() && children.find(child->identifier) == children.end()) {
196 children[child->identifier] = child;
202 template <
typename IdType> std::shared_ptr<ReferenceNode> getChild(IdType
id)
const {
203 auto it = children.find(ReferenceID(
id));
204 if (it != children.end()) {
213 template <
typename IdType>
214 std::shared_ptr<ReferenceNode> getOrCreateChild(IdType
id, Value storedVal =
nullptr) {
215 auto it = children.find(ReferenceID(
id));
216 if (it != children.end()) {
219 return createChild(
id, storedVal);
224 Operation *updateLastWrite(Operation *writeOp) {
225 Operation *old = lastWrite;
230 void clearLastWrite() { lastWrite =
nullptr; }
239 void clearLastWritesObservedBy(ArrayRef<ReferenceID> indices) {
240 if (indices.empty()) {
241 clearLastWritesInSubtree();
246 const ReferenceID &index = indices.front();
247 ArrayRef<ReferenceID> remaining = indices.drop_front();
248 if (!index.isConst()) {
249 for (
const auto &[
id, child] : children) {
250 if (!
id.isAttribute()) {
251 child->clearLastWritesObservedBy(remaining);
257 if (
auto it = children.find(index); it != children.end()) {
258 it->second->clearLastWritesObservedBy(remaining);
260 for (
const auto &[
id, child] : children) {
262 child->clearLastWritesObservedBy(remaining);
271 void clearLastWritesInSubtree() {
272 for (
const auto &[_, child] : children) {
273 child->clearLastWritesInSubtree();
278 void setCurrentValue(Value v,
const std::shared_ptr<ReferenceNode> &valTree =
nullptr) {
280 if (valTree !=
nullptr) {
283 children = valTree->children;
284 dynamicChildCount = valTree->dynamicChildCount;
288 void invalidateChildren() {
290 dynamicChildCount = 0;
297 void invalidateDynamicChildren() {
298 if (dynamicChildCount == 0) {
301 SmallVector<ReferenceID> invalidChildren;
302 for (
const auto &[
id, _] : children) {
304 invalidChildren.push_back(
id);
307 for (
const ReferenceID &
id : invalidChildren) {
310 dynamicChildCount = 0;
313 bool invalidateNonIntegerOffsetChildren() {
314 SmallVector<ReferenceID> invalidChildren;
315 size_t invalidDynamicChildCount = 0;
316 for (
const auto &[
id, _] : children) {
317 if (!
id.isAttribute() || !isa<IntegerAttr>(
id.getAttribute())) {
318 invalidChildren.push_back(
id);
320 ++invalidDynamicChildCount;
324 for (
const ReferenceID &
id : invalidChildren) {
327 dynamicChildCount -= invalidDynamicChildCount;
328 return !invalidChildren.empty();
331 bool isLeaf()
const {
return children.empty(); }
333 Value getStoredValue()
const {
return storedValue; }
335 bool hasStoredValue()
const {
return storedValue !=
nullptr; }
337 void print(raw_ostream &os,
int indent = 0)
const {
338 os.indent(indent) <<
'[' << identifier;
339 if (storedValue !=
nullptr) {
340 os <<
" => " << storedValue;
343 if (!children.empty()) {
345 for (
const auto &[_, child] : children) {
346 child->print(os, indent + 4);
349 os.indent(indent) <<
'}';
354 friend raw_ostream &
operator<<(raw_ostream &os,
const ReferenceNode &r) {
361 topLevelEq(
const std::shared_ptr<ReferenceNode> &lhs,
const std::shared_ptr<ReferenceNode> &rhs) {
362 return lhs->identifier == rhs->identifier && lhs->storedValue == rhs->storedValue &&
363 lhs->lastWrite == rhs->lastWrite;
366 friend std::shared_ptr<ReferenceNode> greatestCommonSubtree(
367 const std::shared_ptr<ReferenceNode> &lhs,
const std::shared_ptr<ReferenceNode> &rhs
369 if (!topLevelEq(lhs, rhs)) {
372 auto res = lhs->clone(
false);
374 for (
auto &[
id, lhsChild] : lhs->children) {
375 if (
auto it = rhs->children.find(
id); it != rhs->children.end()) {
376 auto &rhsChild = it->second;
377 if (
auto gcs = greatestCommonSubtree(lhsChild, rhsChild)) {
378 res->children[id] = gcs;
380 ++res->dynamicChildCount;
389 ReferenceID identifier;
390 mlir::Value storedValue;
391 Operation *lastWrite;
392 DenseMap<ReferenceID, std::shared_ptr<ReferenceNode>> children;
395 size_t dynamicChildCount;
397 template <
typename IdType>
398 ReferenceNode(IdType
id, Value initialVal)
399 : identifier(std::move(id)), storedValue(initialVal), lastWrite(nullptr), children(),
400 dynamicChildCount(0) {}
403using ValueMap = DenseMap<mlir::Value, std::shared_ptr<ReferenceNode>>;
409 DenseMap<SymbolRefAttr, Value> globals;
410 DenseMap<ReferenceID, Value> ram;
412 DenseMap<Value, Value> ramExact;
417struct BlockWriteCandidates {
418 DenseMap<SymbolRefAttr, Operation *> globals;
419 DenseMap<Value, Operation *> ram;
428ValueMap intersectValueMap(
const ValueMap &lhs,
const ValueMap &rhs) {
430 for (
const auto &[
id, lhsValTree] : lhs) {
434 if (
auto it = rhs.find(
id); it != rhs.end() && it->second) {
436 if (
auto common = greatestCommonSubtree(lhsValTree, it->second)) {
437 res[id] = std::move(common);
445template <
typename KeyT>
447intersectValueLookup(
const DenseMap<KeyT, Value> &lhs,
const DenseMap<KeyT, Value> &rhs) {
448 DenseMap<KeyT, Value> res;
449 for (
const auto &[
id, lhsVal] : lhs) {
450 if (
auto it = rhs.find(
id); it != rhs.end() && it->second == lhsVal) {
458KnownState intersect(
const KnownState &lhs,
const KnownState &rhs) {
460 intersectValueMap(lhs.values, rhs.values), intersectValueLookup(lhs.globals, rhs.globals),
461 intersectValueLookup(lhs.ram, rhs.ram), intersectValueLookup(lhs.ramExact, rhs.ramExact)
467ValueMap cloneValueMap(
const ValueMap &orig) {
469 for (
const auto &[
id, tree] : orig) {
470 res[id] = tree->clone();
477KnownState cloneKnownState(
const KnownState &orig) {
478 return {cloneValueMap(orig.values), orig.globals, orig.ram, orig.ramExact};
482 using Base = RedundantReadAndWriteEliminationPassBase<PassImpl>;
490 void runOnOperation()
override {
491 getOperation().walk([
this](FuncDefOp fn) { runOnFunc(fn); });
496 void runOnFunc(FuncDefOp fn) {
502 LLVM_DEBUG(llvm::dbgs() <<
"Running on " << fn.getName() <<
'\n');
505 DenseMap<Value, Value> replacementMap;
507 SmallVector<Value> readVals;
510 SmallVector<Operation *> redundantWrites;
512 KnownState initState;
514 for (
auto arg : fn.getArguments()) {
515 initState.values[arg] = ReferenceNode::create(arg, arg);
519 *fn.
getCallableRegion(), std::move(initState), replacementMap, readVals, redundantWrites
524 for (
auto &[orig, replace] : replacementMap) {
525 LLVM_DEBUG(llvm::dbgs() <<
"replacing " << orig <<
" with " << replace <<
'\n');
526 orig.replaceAllUsesWith(replace);
530 for (
auto *writeOp : redundantWrites) {
531 LLVM_DEBUG(llvm::dbgs() <<
"erase write: " << *writeOp <<
'\n');
537 for (
auto it = readVals.rbegin(); it != readVals.rend(); it++) {
539 if (readVal.use_empty()) {
540 LLVM_DEBUG(llvm::dbgs() <<
"erase read: " << readVal <<
'\n');
541 readVal.getDefiningOp()->erase();
546 KnownState runOnRegion(
547 Region &r, KnownState &&initState, DenseMap<Value, Value> &replacementMap,
548 SmallVector<Value> &readVals, SmallVector<Operation *> &redundantWrites
551 DenseMap<Block *, KnownState> endStates;
553 endStates[
nullptr] = initState;
554 auto getBlockState = [&endStates](Block *blockPtr) {
555 auto it = endStates.find(blockPtr);
556 ensure(it != endStates.end(),
"unknown end state means we have an unsupported backedge");
557 return cloneKnownState(it->second);
559 auto hasBlockState = [&endStates](Block *blockPtr) {
560 return endStates.find(blockPtr) != endStates.end();
562 std::deque<Block *> frontier;
563 DenseSet<Block *> queued;
564 DenseSet<Block *> processed;
565 auto enqueue = [&](Block *blockPtr) {
566 if (processed.find(blockPtr) == processed.end() && queued.insert(blockPtr).second) {
567 frontier.push_back(blockPtr);
572 SmallVector<KnownState> terminalStates;
573 size_t deferralsWithoutProgress = 0;
575 while (!frontier.empty()) {
576 Block *currentBlock = frontier.front();
577 frontier.pop_front();
578 queued.erase(currentBlock);
581 KnownState currentState;
582 auto it = currentBlock->pred_begin();
583 auto itEnd = currentBlock->pred_end();
586 currentState = getBlockState(
nullptr);
589 for (
auto predIt = it; predIt != itEnd; predIt++) {
590 ready &= hasBlockState(*predIt);
593 deferralsWithoutProgress++;
595 deferralsWithoutProgress <= frontier.size(),
596 "unknown end state means we have an unsupported backedge"
598 enqueue(currentBlock);
602 currentState = getBlockState(*it);
606 for (it++; it != itEnd; it++) {
607 currentState = intersect(currentState, getBlockState(*it));
612 deferralsWithoutProgress = 0;
613 auto endState = runOnBlock(
614 *currentBlock, std::move(currentState), replacementMap, readVals, redundantWrites
620 ensure(processed.find(currentBlock) == processed.end(),
"backedge");
621 endStates[currentBlock] = std::move(endState);
622 processed.insert(currentBlock);
625 if (currentBlock->hasNoSuccessors()) {
626 terminalStates.push_back(cloneKnownState(endStates[currentBlock]));
628 for (Block *succ : currentBlock->getSuccessors()) {
635 ensure(!terminalStates.empty(),
"computed no states");
636 auto finalState = terminalStates.front();
637 for (
const auto *it = terminalStates.begin() + 1; it != terminalStates.end(); it++) {
638 finalState = intersect(finalState, *it);
643 KnownState runOnBlock(
644 Block &b, KnownState &&state, DenseMap<Value, Value> &replacementMap,
645 SmallVector<Value> &readVals, SmallVector<Operation *> &redundantWrites
647 BlockWriteCandidates writeCandidates;
648 auto clearTreeWriteCandidates = [](KnownState &knownState) {
649 for (
auto &[_, valueTree] : knownState.values) {
651 valueTree->clearLastWritesInSubtree();
656 for (Operation &op : b) {
660 if (!op.getRegions().empty()) {
661 KnownState parentState = cloneKnownState(state);
666 KnownState regionEntryState = cloneKnownState(state);
671 clearTreeWriteCandidates(regionEntryState);
672 if (isa<scf::ForOp, scf::WhileOp>(op)) {
673 regionEntryState.globals.clear();
674 regionEntryState.ram.clear();
675 regionEntryState.ramExact.clear();
677 SmallVector<KnownState> regionStates;
678 for (Region ®ion : op.getRegions()) {
679 if (region.empty()) {
682 auto regionState = runOnRegion(
683 region, cloneKnownState(regionEntryState), replacementMap, readVals, redundantWrites
685 regionStates.push_back(regionState);
687 if (regionStates.empty()) {
689 runOperation(&op, state, replacementMap, readVals, redundantWrites, writeCandidates);
690 writeCandidates.clear();
694 KnownState finalState = regionStates.front();
695 for (
const auto *it = regionStates.begin() + 1; it != regionStates.end(); it++) {
696 finalState = intersect(finalState, *it);
702 finalState.globals = intersectValueLookup(parentState.globals, finalState.globals);
703 finalState.ram = intersectValueLookup(parentState.ram, finalState.ram);
704 finalState.ramExact = intersectValueLookup(parentState.ramExact, finalState.ramExact);
708 clearTreeWriteCandidates(finalState);
709 state = std::move(finalState);
710 writeCandidates.clear();
713 runOperation(&op, state, replacementMap, readVals, redundantWrites, writeCandidates);
715 return std::move(state);
726 Operation *op, KnownState &state, DenseMap<Value, Value> &replacementMap,
727 SmallVector<Value> &readVals, SmallVector<Operation *> &redundantWrites,
728 BlockWriteCandidates &writeCandidates
733 auto translate = [&replacementMap](Value v) {
734 if (
auto it = replacementMap.find(v); it != replacementMap.end()) {
741 auto tryGetValTree = [&state](Value v) -> std::shared_ptr<ReferenceNode> {
742 if (
auto it = state.values.find(v); it != state.values.end()) {
748 auto doStatefulRead =
749 [&]<
typename KeyT>(Value resVal, DenseMap<KeyT, Value> &knownValues,
const KeyT &key) {
750 readVals.push_back(resVal);
751 if (
auto it = knownValues.find(key); it != knownValues.end()) {
752 replacementMap[resVal] = it->second;
755 knownValues[key] = resVal;
756 state.values[resVal] = ReferenceNode::create(resVal, resVal);
762 const IntegerAttr zeroTableOffset = IntegerAttr::get(IndexType::get(op->getContext()), 0);
763 auto getMemberNode = [&](Value component, FlatSymbolRefAttr member) {
764 std::shared_ptr<ReferenceNode> componentNode = tryGetValTree(translate(component));
765 if (componentNode ==
nullptr) {
766 return std::shared_ptr<ReferenceNode>();
768 return componentNode->getOrCreateChild(member);
771 auto getMemberAccessNode = [&](MemberReadOp readm) {
772 std::shared_ptr<ReferenceNode> access =
773 getMemberNode(readm.getComponent(), readm.getMemberNameAttr());
774 if (access ==
nullptr) {
777 access = access->getOrCreateChild(readm.getTableOffset().value_or(zeroTableOffset));
778 if (!readm.getMapOperands().empty()) {
779 access = access->getOrCreateChild(readm.getMapOpGroupSizesAttr());
780 access = access->getOrCreateChild(readm.getNumDimsPerMapAttr());
782 for (
auto mapOperands : readm.getMapOperands()) {
783 for (Value operand : mapOperands) {
784 access = access->getOrCreateChild(translate(operand));
792 auto doArrayReadLike = [&]<HasInterface<ArrayAccessOpInterface> OpClass>(OpClass readarr) {
793 Value resVal = readarr.getResult();
794 std::shared_ptr<ReferenceNode> currValTree = tryGetValTree(translate(readarr.getArrRef()));
795 if (currValTree ==
nullptr) {
796 state.values[resVal] = ReferenceNode::create(resVal, resVal);
797 readVals.push_back(resVal);
801 std::shared_ptr<ReferenceNode> rootValTree = currValTree;
802 SmallVector<ReferenceID> indices;
803 bool hasDynamicIndex =
false;
804 for (Value origIdx : readarr.getIndices()) {
805 Value idxVal = translate(origIdx);
806 ReferenceID indexId(idxVal);
807 hasDynamicIndex |= !indexId.isConst();
808 indices.push_back(indexId);
809 currValTree = currValTree->getOrCreateChild(idxVal);
812 if (!currValTree->hasStoredValue()) {
813 currValTree->setCurrentValue(resVal);
816 if (currValTree->getStoredValue() != resVal) {
818 llvm::dbgs() << readarr.getOperationName() <<
": replace " << resVal <<
" with "
819 << currValTree->getStoredValue() <<
'\n'
821 replacementMap[resVal] = currValTree->getStoredValue();
823 if (hasDynamicIndex) {
824 rootValTree->clearLastWritesObservedBy(indices);
826 state.values[resVal] = currValTree;
828 llvm::dbgs() << readarr.getOperationName() <<
": " << resVal <<
" => " << *currValTree
833 readVals.push_back(resVal);
839 auto doArrayWriteLike = [&]<HasInterface<ArrayAccessOpInterface> OpClass>(OpClass writearr) {
840 std::shared_ptr<ReferenceNode> currValTree = tryGetValTree(translate(writearr.getArrRef()));
841 if (currValTree ==
nullptr) {
844 Value newVal = translate(writearr.getRvalue());
845 std::shared_ptr<ReferenceNode> valTree = tryGetValTree(newVal);
847 for (Value origIdx : writearr.getIndices()) {
848 Value idxVal = translate(origIdx);
851 if (ReferenceID(idxVal).isConst()) {
852 currValTree->invalidateDynamicChildren();
854 LLVM_DEBUG(llvm::dbgs() << writearr.getOperationName() <<
": invalidate alias\n");
855 currValTree->invalidateChildren();
857 currValTree = currValTree->getOrCreateChild(idxVal);
860 if (currValTree->getStoredValue() == newVal) {
862 llvm::dbgs() << writearr.getOperationName() <<
": subsequent " << writearr
865 redundantWrites.push_back(writearr);
867 if (Operation *lastWrite = currValTree->updateLastWrite(writearr)) {
869 llvm::dbgs() << writearr.getOperationName() <<
"writearr: replacing " << lastWrite
870 <<
" with prior write " << *lastWrite <<
'\n'
872 redundantWrites.push_back(lastWrite);
874 currValTree->setCurrentValue(newVal, valTree);
879 if (
auto readGlobal = dyn_cast<global::GlobalReadOp>(op)) {
880 const auto name = readGlobal.getNameRef();
881 if (!doStatefulRead(readGlobal.getVal(), state.globals, name)) {
882 writeCandidates.globals.erase(name);
884 }
else if (
auto writeGlobal = dyn_cast<global::GlobalWriteOp>(op)) {
885 const auto name = writeGlobal.getNameRef();
886 Value value = translate(writeGlobal.getVal());
887 if (
auto known = state.globals.find(name);
888 known != state.globals.end() && known->second == value) {
889 redundantWrites.push_back(writeGlobal.getOperation());
891 if (
auto previous = writeCandidates.globals.find(name);
892 previous != writeCandidates.globals.end()) {
893 redundantWrites.push_back(previous->second);
895 state.globals[name] = value;
896 writeCandidates.globals[name] = writeGlobal.getOperation();
900 else if (
auto load = dyn_cast<ram::LoadOp>(op)) {
901 Value address = translate(load.getAddr());
902 if (!doStatefulRead(load.getVal(), state.ram, ReferenceID(address))) {
903 writeCandidates.ram.clear();
905 state.ramExact[address] = translate(load.getVal());
906 }
else if (
auto store = dyn_cast<ram::StoreOp>(op)) {
907 Value address = translate(store.getAddr());
908 Value value = translate(store.getVal());
909 if (
auto known = state.ramExact.find(address);
910 known != state.ramExact.end() && known->second == value) {
911 redundantWrites.push_back(store.getOperation());
913 if (
auto previous = writeCandidates.ram.find(address);
914 previous != writeCandidates.ram.end()) {
915 redundantWrites.push_back(previous->second);
917 writeCandidates.ram[address] = store.getOperation();
919 state.ramExact.clear();
920 state.ram[ReferenceID(address)] = value;
921 state.ramExact[address] = value;
925 else if (
auto newStruct = dyn_cast<CreateStructOp>(op)) {
927 auto structVal = ReferenceNode::create(newStruct, newStruct);
928 state.values[newStruct] = structVal;
930 llvm::dbgs() << newStruct.getOperationName() <<
": " << *state.values[newStruct] <<
'\n'
933 readVals.push_back(newStruct);
934 }
else if (
auto readm = dyn_cast<MemberReadOp>(op)) {
935 std::shared_ptr<ReferenceNode> access = getMemberAccessNode(readm);
936 Value resVal = readm.getVal();
937 if (access ==
nullptr) {
938 state.values[resVal] = ReferenceNode::create(resVal, resVal);
939 readVals.push_back(resVal);
942 if (!access->hasStoredValue()) {
943 access->setCurrentValue(resVal);
945 if (access->getStoredValue() != resVal) {
947 llvm::dbgs() << readm.getOperationName() <<
": adding replacement map entry { "
948 << resVal <<
" => " << access->getStoredValue() <<
" }\n"
950 replacementMap[resVal] = access->getStoredValue();
952 state.values[resVal] = access;
953 LLVM_DEBUG(llvm::dbgs() << readm.getOperationName() <<
": " << *access <<
'\n');
955 readVals.push_back(resVal);
956 }
else if (
auto writem = dyn_cast<MemberWriteOp>(op)) {
957 std::shared_ptr<ReferenceNode> member =
958 getMemberNode(writem.getComponent(), writem.getMemberNameAttr());
959 if (member ==
nullptr) {
964 bool invalidatedMayAliasRead = member->invalidateNonIntegerOffsetChildren();
965 Value writeVal = translate(writem.getVal());
966 auto valTree = tryGetValTree(writeVal);
968 auto access = member->getOrCreateChild(zeroTableOffset);
969 if (invalidatedMayAliasRead) {
970 access->clearLastWrite();
972 if (access->getStoredValue() == writeVal) {
974 llvm::dbgs() << writem.getOperationName() <<
": recording redundant write " << writem
977 redundantWrites.push_back(writem);
979 if (
auto *lastWrite = access->updateLastWrite(writem)) {
981 llvm::dbgs() << writem.getOperationName() <<
": recording overwritten write "
982 << *lastWrite <<
'\n'
984 redundantWrites.push_back(lastWrite);
986 access->setCurrentValue(writeVal, valTree);
988 llvm::dbgs() << writem.getOperationName() <<
": " << *access <<
" set to " << writeVal
994 else if (
auto newArray = dyn_cast<CreateArrayOp>(op)) {
995 auto arrayVal = ReferenceNode::create(newArray, newArray);
996 state.values[newArray] = arrayVal;
1001 for (
auto elem : newArray.getElements()) {
1002 Value elemVal = translate(elem);
1003 auto valTree = tryGetValTree(elemVal);
1004 auto elemChild = arrayVal->createChild(idx, elemVal, valTree);
1006 llvm::dbgs() << newArray.getOperationName() <<
": element " << idx <<
" initialized to "
1007 << *elemChild <<
'\n'
1012 readVals.push_back(newArray);
1013 }
else if (
auto readarr = dyn_cast<ReadArrayOp>(op)) {
1014 doArrayReadLike(readarr);
1015 }
else if (
auto writearr = dyn_cast<WriteArrayOp>(op)) {
1016 doArrayWriteLike(writearr);
1017 }
else if (
auto extractarr = dyn_cast<ExtractArrayOp>(op)) {
1019 doArrayReadLike(extractarr);
1020 }
else if (
auto insertarr = dyn_cast<InsertArrayOp>(op)) {
1022 doArrayWriteLike(insertarr);
1024 state.globals.clear();
1026 state.ramExact.clear();
1027 writeCandidates.clear();
1031 writeCandidates.clear();
void print(llvm::raw_ostream &os) const
::mlir::Region * getCallableRegion()
Required by FunctionOpInterface.
void ensure(bool condition, const llvm::Twine &errMsg)
Interval operator<<(const Interval &lhs, const Interval &rhs)
bool hasReadEffect(mlir::Operation *op)
Returns true when op has a memory read effect.
bool hasUnknownOrNonReadEffect(mlir::Operation *op)
Returns true when op may have an unknown effect or any effect other than memory read.
mlir::Operation * create(MlirOpBuilder cBuilder, MlirLocation cLocation, Args &&...args)
Creates a new operation using an ODS build method.
static bool isEqual(const ReferenceID &lhs, const ReferenceID &rhs)
static ReferenceID getEmptyKey()
static ReferenceID getTombstoneKey()
static unsigned getHashValue(const ReferenceID &r)