MLIR: lib/Dialect/Affine/Analysis/Utils.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
15
25 #include "llvm/ADT/SetVector.h"
26 #include "llvm/ADT/SmallPtrSet.h"
27 #include "llvm/Support/Debug.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include
30
31 #define DEBUG_TYPE "analysis-utils"
32
33 using namespace mlir;
34 using namespace affine;
35 using namespace presburger;
36
37 using llvm::SmallDenseMap;
38
40
41
42
43
46 if (auto forOp = dyn_cast(op)) {
47 forOps.push_back(forOp);
48 } else if (isa(op)) {
49 loadOpInsts.push_back(op);
50 } else if (isa(op)) {
51 storeOpInsts.push_back(op);
52 } else {
53 auto memInterface = dyn_cast(op);
54 if (!memInterface) {
56
57 return;
58
60 if (!isa(v.getType()))
61 continue;
62
63 memrefLoads.push_back(op);
64 memrefStores.push_back(op);
65 }
66 } else {
67
68 if (hasEffectMemoryEffects::Read(op))
69 memrefLoads.push_back(op);
70 if (hasEffectMemoryEffects::Write(op))
71 memrefStores.push_back(op);
72 if (hasEffectMemoryEffects::Free(op))
73 memrefFrees.push_back(op);
74 }
75 }
76 });
77 }
78
79 unsigned Node::getLoadOpCount(Value memref) const {
80 unsigned loadOpCount = 0;
82
83 if (auto affineLoad = dyn_cast(loadOp)) {
84 if (memref == affineLoad.getMemRef())
85 ++loadOpCount;
86 } else if (hasEffectMemoryEffects::Read(loadOp, memref)) {
87 ++loadOpCount;
88 }
89 }
90 return loadOpCount;
91 }
92
93
94 unsigned Node::getStoreOpCount(Value memref) const {
95 unsigned storeOpCount = 0;
96 for (auto *storeOp : llvm::concat<Operation *const>(stores, memrefStores)) {
97
98 if (auto affineStore = dyn_cast(storeOp)) {
99 if (memref == affineStore.getMemRef())
100 ++storeOpCount;
101 } else if (hasEffectMemoryEffects::Write(const_cast<Operation *>(storeOp),
102 memref)) {
103 ++storeOpCount;
104 }
105 }
106 return storeOpCount;
107 }
108
109
110 unsigned Node::hasStore(Value memref) const {
111 return llvm::any_of(
112 llvm::concat<Operation *const>(stores, memrefStores),
114 if (auto affineStore = dyn_cast(storeOp)) {
115 if (memref == affineStore.getMemRef())
116 return true;
117 } else if (hasEffectMemoryEffects::Write(storeOp, memref)) {
118 return true;
119 }
120 return false;
121 });
122 }
123
124 unsigned Node::hasFree(Value memref) const {
125 return llvm::any_of(memrefFrees, [&](Operation *freeOp) {
126 return hasEffectMemoryEffects::Free(freeOp, memref);
127 });
128 }
129
130
131 void Node::getStoreOpsForMemref(Value memref,
133 for (Operation *storeOp : stores) {
134 if (memref == cast(storeOp).getMemRef())
135 storeOps->push_back(storeOp);
136 }
137 }
138
139
140 void Node::getLoadOpsForMemref(Value memref,
142 for (Operation *loadOp : loads) {
143 if (memref == cast(loadOp).getMemRef())
144 loadOps->push_back(loadOp);
145 }
146 }
147
148
149
150 void Node::getLoadAndStoreMemrefSet(
152 llvm::SmallDenseSet<Value, 2> loadMemrefs;
153 for (Operation *loadOp : loads) {
154 loadMemrefs.insert(cast(loadOp).getMemRef());
155 }
156 for (Operation *storeOp : stores) {
157 auto memref = cast(storeOp).getMemRef();
158 if (loadMemrefs.count(memref) > 0)
159 loadAndStoreMemrefSet->insert(memref);
160 }
161 }
162
163
164
165 template <typename... EffectTys>
167 auto memOp = dyn_cast(op);
168 if (!memOp) {
170
171 return;
172
174 if (isa(operand.getType()))
175 values.push_back(operand);
176 }
177 return;
178 }
180 memOp.getEffects(effects);
181 for (auto &effect : effects) {
182 Value effectVal = effect.getValue();
183 if (isa<EffectTys...>(effect.getEffect()) && effectVal &&
184 isa(effectVal.getType()))
185 values.push_back(effectVal);
186 };
187 }
188
189
190
191 static Node *
194 auto &nodes = mdg.nodes;
195
196
198 collector.collect(nodeOp);
199 unsigned newNodeId = mdg.nextNodeId++;
200 Node &node = nodes.insert({newNodeId, Node(newNodeId, nodeOp)}).first->second;
202 node.loads.push_back(op);
203 auto memref = cast(op).getMemRef();
204 memrefAccesses[memref].insert(node.id);
205 }
207 node.stores.push_back(op);
208 auto memref = cast(op).getMemRef();
209 memrefAccesses[memref].insert(node.id);
210 }
213 getEffectedValuesMemoryEffects::Read(op, effectedValues);
214 if (llvm::any_of(((ValueRange)effectedValues).getTypes(),
215 [](Type type) { return !isa(type); }))
216
217 return nullptr;
218 for (Value memref : effectedValues)
219 memrefAccesses[memref].insert(node.id);
220 node.memrefLoads.push_back(op);
221 }
224 getEffectedValuesMemoryEffects::Write(op, effectedValues);
225 if (llvm::any_of((ValueRange(effectedValues)).getTypes(),
226 [](Type type) { return !isa(type); }))
227 return nullptr;
228 for (Value memref : effectedValues)
229 memrefAccesses[memref].insert(node.id);
230 node.memrefStores.push_back(op);
231 }
234 getEffectedValuesMemoryEffects::Free(op, effectedValues);
235 if (llvm::any_of((ValueRange(effectedValues)).getTypes(),
236 [](Type type) { return !isa(type); }))
237 return nullptr;
238 for (Value memref : effectedValues)
239 memrefAccesses[memref].insert(node.id);
240 node.memrefFrees.push_back(op);
241 }
242
243 return &node;
244 }
245
247 LLVM_DEBUG(llvm::dbgs() << "--- Initializing MDG ---\n");
248
249
251
252
255 if (auto forOp = dyn_cast(op)) {
257 if (!node)
258 return false;
259 forToNodeMap[&op] = node->id;
260 } else if (isa(op)) {
261
262 Node node(nextNodeId++, &op);
263 node.loads.push_back(&op);
264 auto memref = cast(op).getMemRef();
265 memrefAccesses[memref].insert(node.id);
266 nodes.insert({node.id, node});
267 } else if (isa(op)) {
268
269 Node node(nextNodeId++, &op);
270 node.stores.push_back(&op);
271 auto memref = cast(op).getMemRef();
272 memrefAccesses[memref].insert(node.id);
273 nodes.insert({node.id, node});
274 } else if (op.getNumResults() > 0 && !op.use_empty()) {
275
276
278 if (!node)
279 return false;
281 (op.getNumRegions() == 0 || isa(op))) {
282
283
284
285
286
288 if (!node)
289 return false;
290 } else if (op.getNumRegions() != 0 && !isa(op)) {
291
292
293
294 LLVM_DEBUG(llvm::dbgs()
295 << "MDG init failed; unknown region-holding op found!\n");
296 return false;
297 }
298
299
300
301 }
302
303 LLVM_DEBUG(llvm::dbgs() << "Created " << nodes.size() << " nodes\n");
304
305
306
307 for (auto &idAndNode : nodes) {
308 const Node &node = idAndNode.second;
309
310 if (!node.stores.empty())
311 continue;
315
316 if (block.getParent()->findAncestorOpInRegion(*user)->getBlock() !=
317 &block)
318 continue;
321
322
323 auto *it = llvm::find_if(loops, [&](AffineForOp loop) {
324 return loop->getBlock() == █
325 });
326 if (it == loops.end())
327 continue;
328 assert(forToNodeMap.count(*it) > 0 && "missing mapping");
329 unsigned userLoopNestId = forToNodeMap[*it];
330 addEdge(node.id, userLoopNestId, value);
331 }
332 }
333 }
334
335
336 for (auto &memrefAndList : memrefAccesses) {
337 unsigned n = memrefAndList.second.size();
338 Value srcMemRef = memrefAndList.first;
339
340 for (unsigned i = 0; i < n; ++i) {
341 unsigned srcId = memrefAndList.second[i];
342 Node *srcNode = getNode(srcId);
343 bool srcHasStoreOrFree =
344 srcNode->hasStore(srcMemRef) || srcNode->hasFree(srcMemRef);
345 for (unsigned j = i + 1; j < n; ++j) {
346 unsigned dstId = memrefAndList.second[j];
347 Node *dstNode = getNode(dstId);
348 bool dstHasStoreOrFree =
349 dstNode->hasStore(srcMemRef) || dstNode->hasFree(srcMemRef);
350 if (srcHasStoreOrFree || dstHasStoreOrFree)
351 addEdge(srcId, dstId, srcMemRef);
352 }
353 }
354 }
355 return true;
356 }
357
358
360 auto it = nodes.find(id);
361 assert(it != nodes.end());
362 return &it->second;
363 }
364
365
367 for (auto &idAndNode : nodes)
368 if (idAndNode.second.op == forOp)
369 return &idAndNode.second;
370 return nullptr;
371 }
372
373
375 Node node(nextNodeId++, op);
376 nodes.insert({node.id, node});
377 return node.id;
378 }
379
380
382
383 if (inEdges.count(id) > 0) {
385 for (auto &inEdge : oldInEdges) {
386 removeEdge(inEdge.id, id, inEdge.value);
387 }
388 }
389
390 if (outEdges.contains(id)) {
392 for (auto &outEdge : oldOutEdges) {
393 removeEdge(id, outEdge.id, outEdge.value);
394 }
395 }
396
397 inEdges.erase(id);
398 outEdges.erase(id);
399 nodes.erase(id);
400 }
401
402
403
405 const Node *node = getNode(id);
406 for (auto *storeOpInst : node->stores) {
407 auto memref = cast(storeOpInst).getMemRef();
408 auto *op = memref.getDefiningOp();
409
410 if (!op)
411 return true;
412
413
414 for (auto *user : memref.getUsers())
415 if (!isa(*user))
416 return true;
417 }
418 return false;
419 }
420
421
422
423
425 Value value) const {
426 if (!outEdges.contains(srcId) || !inEdges.contains(dstId)) {
427 return false;
428 }
429 bool hasOutEdge = llvm::any_of(outEdges.lookup(srcId), [=](const Edge &edge) {
430 return edge.id == dstId && (!value || edge.value == value);
431 });
432 bool hasInEdge = llvm::any_of(inEdges.lookup(dstId), [=](const Edge &edge) {
433 return edge.id == srcId && (!value || edge.value == value);
434 });
435 return hasOutEdge && hasInEdge;
436 }
437
438
441 if (!hasEdge(srcId, dstId, value)) {
442 outEdges[srcId].push_back({dstId, value});
443 inEdges[dstId].push_back({srcId, value});
444 if (isa(value.getType()))
445 memrefEdgeCount[value]++;
446 }
447 }
448
449
452 assert(inEdges.count(dstId) > 0);
453 assert(outEdges.count(srcId) > 0);
454 if (isa(value.getType())) {
455 assert(memrefEdgeCount.count(value) > 0);
456 memrefEdgeCount[value]--;
457 }
458
459 for (auto *it = inEdges[dstId].begin(); it != inEdges[dstId].end(); ++it) {
460 if ((*it).id == srcId && (*it).value == value) {
461 inEdges[dstId].erase(it);
462 break;
463 }
464 }
465
466 for (auto *it = outEdges[srcId].begin(); it != outEdges[srcId].end(); ++it) {
467 if ((*it).id == dstId && (*it).value == value) {
468 outEdges[srcId].erase(it);
469 break;
470 }
471 }
472 }
473
474
475
476
478 unsigned dstId) const {
479
481 worklist.push_back({srcId, 0});
482 Operation *dstOp = getNode(dstId)->op;
483
484 while (!worklist.empty()) {
485 auto &idAndIndex = worklist.back();
486
487 if (idAndIndex.first == dstId)
488 return true;
489
490
491 if (!outEdges.contains(idAndIndex.first) ||
492 idAndIndex.second == outEdges.lookup(idAndIndex.first).size()) {
493 worklist.pop_back();
494 continue;
495 }
496
497 const Edge edge = outEdges.lookup(idAndIndex.first)[idAndIndex.second];
498
499 ++idAndIndex.second;
500
501
502
504 if (!afterDst && edge.id != idAndIndex.first)
505 worklist.push_back({edge.id, 0});
506 }
507 return false;
508 }
509
510
511
513 Value memref) const {
514 unsigned inEdgeCount = 0;
515 for (const Edge &inEdge : inEdges.lookup(id)) {
516 if (inEdge.value == memref) {
517 const Node *srcNode = getNode(inEdge.id);
518
520 ++inEdgeCount;
521 }
522 }
523 return inEdgeCount;
524 }
525
526
527
529 Value memref) const {
530 unsigned outEdgeCount = 0;
531 for (const auto &outEdge : outEdges.lookup(id))
532 if (!memref || outEdge.value == memref)
533 ++outEdgeCount;
534 return outEdgeCount;
535 }
536
537
540 for (const Edge &edge : inEdges.lookup(id))
541
542
543
544 if (!isa(edge.value.getType()))
545 definingNodes.insert(edge.id);
546 }
547
548
549
550
553 unsigned dstId) const {
554 if (!outEdges.contains(srcId))
555 return getNode(dstId)->op;
556
557
559 gatherDefiningNodes(dstId, definingNodes);
560 if (llvm::any_of(definingNodes,
561 [&](unsigned id) { return hasDependencePath(srcId, id); })) {
562 LLVM_DEBUG(llvm::dbgs()
563 << "Can't fuse: a defining op with a user in the dst "
564 "loop has dependence from the src loop\n");
565 return nullptr;
566 }
567
568
570 for (auto &outEdge : outEdges.lookup(srcId))
571 if (outEdge.id != dstId)
572 srcDepInsts.insert(getNode(outEdge.id)->op);
573
574
576 for (auto &inEdge : inEdges.lookup(dstId))
577 if (inEdge.id != srcId)
578 dstDepInsts.insert(getNode(inEdge.id)->op);
579
580 Operation *srcNodeInst = getNode(srcId)->op;
581 Operation *dstNodeInst = getNode(dstId)->op;
582
583
584
585
586
587
588
589
590
591
592
594 std::optional firstSrcDepPos;
595 std::optional lastDstDepPos;
596 unsigned pos = 0;
600 if (srcDepInsts.count(op) > 0 && firstSrcDepPos == std::nullopt)
601 firstSrcDepPos = pos;
602 if (dstDepInsts.count(op) > 0)
603 lastDstDepPos = pos;
604 depInsts.push_back(op);
605 ++pos;
606 }
607
608 if (firstSrcDepPos.has_value()) {
609 if (lastDstDepPos.has_value()) {
610 if (*firstSrcDepPos <= *lastDstDepPos) {
611
612 return nullptr;
613 }
614 }
615
616 return depInsts[*firstSrcDepPos];
617 }
618
619
620 return dstNodeInst;
621 }
622
623
624
625
626
627
630 bool removeSrcId) {
631
632 if (inEdges.count(srcId) > 0) {
634 for (auto &inEdge : oldInEdges) {
635
636 if (!privateMemRefs.contains(inEdge.value))
637 addEdge(inEdge.id, dstId, inEdge.value);
638 }
639 }
640
641
642 if (outEdges.count(srcId) > 0) {
644 for (auto &outEdge : oldOutEdges) {
645
646 if (outEdge.id == dstId)
647 removeEdge(srcId, outEdge.id, outEdge.value);
648 else if (removeSrcId) {
649 addEdge(dstId, outEdge.id, outEdge.value);
650 removeEdge(srcId, outEdge.id, outEdge.value);
651 }
652 }
653 }
654
655
656
657 if (inEdges.count(dstId) > 0 && !privateMemRefs.empty()) {
659 for (auto &inEdge : oldInEdges)
660 if (privateMemRefs.count(inEdge.value) > 0)
661 removeEdge(inEdge.id, dstId, inEdge.value);
662 }
663 }
664
665
666
668
669
670
671 if (inEdges.count(sibId) > 0) {
673 for (auto &inEdge : oldInEdges) {
674 addEdge(inEdge.id, dstId, inEdge.value);
675 removeEdge(inEdge.id, sibId, inEdge.value);
676 }
677 }
678
679
680
681
682 if (outEdges.count(sibId) > 0) {
684 for (auto &outEdge : oldOutEdges) {
685 addEdge(dstId, outEdge.id, outEdge.value);
686 removeEdge(sibId, outEdge.id, outEdge.value);
687 }
688 }
689 }
690
691
697 Node *node = getNode(id);
698 llvm::append_range(node->loads, loads);
699 llvm::append_range(node->stores, stores);
700 llvm::append_range(node->memrefLoads, memrefLoads);
701 llvm::append_range(node->memrefStores, memrefStores);
702 llvm::append_range(node->memrefFrees, memrefFrees);
703 }
704
706 Node *node = getNode(id);
707 node->loads.clear();
708 node->stores.clear();
709 }
710
711
712
714 unsigned id, const std::function<void(Edge)> &callback) {
715 if (inEdges.count(id) > 0)
716 forEachMemRefEdge(inEdges[id], callback);
717 }
718
719
720
722 unsigned id, const std::function<void(Edge)> &callback) {
723 if (outEdges.count(id) > 0)
724 forEachMemRefEdge(outEdges[id], callback);
725 }
726
727
728
731 for (const auto &edge : edges) {
732
733 if (!isa(edge.value.getType()))
734 continue;
735 assert(nodes.count(edge.id) > 0);
736
737 if (!isa(getNode(edge.id)->op))
738 continue;
739
740 callback(edge);
741 }
742 }
743
745 os << "\nMemRefDependenceGraph\n";
746 os << "\nNodes:\n";
747 for (const auto &idAndNode : nodes) {
748 os << "Node: " << idAndNode.first << "\n";
749 auto it = inEdges.find(idAndNode.first);
750 if (it != inEdges.end()) {
751 for (const auto &e : it->second)
752 os << " InEdge: " << e.id << " " << e.value << "\n";
753 }
754 it = outEdges.find(idAndNode.first);
755 if (it != outEdges.end()) {
756 for (const auto &e : it->second)
757 os << " OutEdge: " << e.id << " " << e.value << "\n";
758 }
759 }
760 }
761
765 AffineForOp currAffineForOp;
766
767
769 if (auto currAffineForOp = dyn_cast(currOp))
770 loops->push_back(currAffineForOp);
771 currOp = currOp->getParentOp();
772 }
773 std::reverse(loops->begin(), loops->end());
774 }
775
778 ops->clear();
780
781
782
784 if (isa<AffineIfOp, AffineForOp, AffineParallelOp>(currOp))
785 ops->push_back(currOp);
787 }
788 std::reverse(ops->begin(), ops->end());
789 }
790
791
792
795 assert(!ivs.empty() && "Cannot have a slice without its IVs");
797 0, ivs);
798 for (Value iv : ivs) {
800 assert(loop && "Expected affine for");
802 return failure();
803 }
804 return success();
805 }
806
807
808 LogicalResult
810 assert(!lbOperands.empty());
811
812 unsigned numDims = ivs.size();
813
814 unsigned numSymbols = lbOperands[0].size();
815
817
818 values.append(lbOperands[0].begin(), lbOperands[0].end());
820
821
822
823 for (unsigned i = numDims, end = values.size(); i < end; ++i) {
824 Value value = values[i];
825 assert(cst->containsVar(value) && "value expected to be present");
827
829 cst->addBound(BoundType::EQ, value, cOp.value());
832 return failure();
833 }
834 }
835
836
837 LogicalResult ret = cst->addSliceBounds(ivs, lbs, ubs, lbOperands[0]);
838 assert(succeeded(ret) &&
839 "should not fail as we never have semi-affine slice maps");
840 (void)ret;
841 return success();
842 }
843
844
846 lbs.clear();
847 ubs.clear();
848 lbOperands.clear();
849 ubOperands.clear();
850 }
851
853 llvm::errs() << "\tIVs:\n";
854 for (Value iv : ivs)
855 llvm::errs() << "\t\t" << iv << "\n";
856
857 llvm::errs() << "\tLBs:\n";
859 llvm::errs() << "\t\t" << en.value() << "\n";
860 llvm::errs() << "\t\tOperands:\n";
861 for (Value lbOp : lbOperands[en.index()])
862 llvm::errs() << "\t\t\t" << lbOp << "\n";
863 }
864
865 llvm::errs() << "\tUBs:\n";
867 llvm::errs() << "\t\t" << en.value() << "\n";
868 llvm::errs() << "\t\tOperands:\n";
869 for (Value ubOp : ubOperands[en.index()])
870 llvm::errs() << "\t\t\t" << ubOp << "\n";
871 }
872 }
873
874
875
876
877
878
879 std::optional ComputationSliceState::isSliceMaximalFastCheck() const {
880 assert(lbs.size() == ubs.size() && !lbs.empty() && !ivs.empty() &&
881 "Unexpected number of lbs, ubs and ivs in slice");
882
883 for (unsigned i = 0, end = lbs.size(); i < end; ++i) {
886
887
888 if (!lbMap || !ubMap || lbMap.getNumResults() != 1 ||
891
892
893
894
895 isa(lbMap.getResult(0)))
896 return std::nullopt;
897
898
899
901 if (!result)
902 return std::nullopt;
903
904
905 AffineForOp dstLoop =
907 if (!dstLoop)
908 return std::nullopt;
909 AffineMap dstLbMap = dstLoop.getLowerBoundMap();
910 AffineMap dstUbMap = dstLoop.getUpperBoundMap();
911
912
914 assert(srcLoop && "Expected affine for");
915 AffineMap srcLbMap = srcLoop.getLowerBoundMap();
916 AffineMap srcUbMap = srcLoop.getUpperBoundMap();
917
918
919
922 return std::nullopt;
923
928 if (!isa(srcLbResult) ||
929 !isa(srcUbResult) ||
930 !isa(dstLbResult) ||
931 !isa(dstUbResult))
932 return std::nullopt;
933
934
935
936 if (srcLbResult != dstLbResult || srcUbResult != dstUbResult ||
937 srcLoop.getStep() != dstLoop.getStep())
938 return false;
939 }
940
941 return true;
942 }
943
944
945
946
948
949
950
951
952
953
954
955
956
957
958 std::optional isValidFastCheck = isSliceMaximalFastCheck();
959 if (isValidFastCheck && *isValidFastCheck)
960 return true;
961
962
964
965 if (failed(getSourceAsConstraints(srcConstraints))) {
966 LLVM_DEBUG(llvm::dbgs() << "Unable to compute source's domain\n");
967 return std::nullopt;
968 }
969
970
972 LLVM_DEBUG(llvm::dbgs() << "Cannot handle symbols in source domain\n");
973 return std::nullopt;
974 }
975
976
977
979 LLVM_DEBUG(llvm::dbgs() << "Cannot handle locals in source domain\n");
980 return std::nullopt;
981 }
982
983
984
986 if (failed(getAsConstraints(&sliceConstraints))) {
987 LLVM_DEBUG(llvm::dbgs() << "Unable to compute slice's domain\n");
988 return std::nullopt;
989 }
990
991
992
993 sliceConstraints.projectOut(ivs.size(),
994 sliceConstraints.getNumVars() - ivs.size());
995
996 LLVM_DEBUG(llvm::dbgs() << "Domain of the source of the slice:\n");
997 LLVM_DEBUG(srcConstraints.dump());
998 LLVM_DEBUG(llvm::dbgs() << "Domain of the slice if this fusion succeeds "
999 "(expressed in terms of its source's IVs):\n");
1000 LLVM_DEBUG(sliceConstraints.dump());
1001
1002
1006
1008 LLVM_DEBUG(llvm::dbgs() << "Incorrect slice\n");
1009 return false;
1010 }
1011 return true;
1012 }
1013
1014
1015
1016
1018
1019
1020 std::optional isMaximalFastCheck = isSliceMaximalFastCheck();
1021 if (isMaximalFastCheck)
1022 return isMaximalFastCheck;
1023
1024
1026 0,
1027 0, ivs);
1028 for (Value iv : ivs) {
1030 assert(loop && "Expected affine for");
1032 return std::nullopt;
1033 }
1034
1035
1036
1038 for (Value lbOp : lbOperands[0])
1040 consumerIVs.push_back(lbOp);
1041
1042
1043
1044 for (int i = consumerIVs.size(), end = ivs.size(); i < end; ++i)
1045 consumerIVs.push_back(Value());
1046
1048 0,
1049 0, consumerIVs);
1050
1052 return std::nullopt;
1053
1055
1056
1057 return std::nullopt;
1058
1059
1060
1065 }
1066
1068 return cast(memref.getType()).getRank();
1069 }
1070
1073 auto memRefType = cast(memref.getType());
1074 MLIRContext *context = memref.getContext();
1075 unsigned rank = memRefType.getRank();
1076 if (shape)
1077 shape->reserve(rank);
1078
1079 assert(rank == cst.getNumDimVars() && "inconsistent memref region");
1080
1081
1082
1083
1084
1085
1087 for (unsigned r = 0; r < rank; r++) {
1088 cstWithShapeBounds.addBound(BoundType::LB, r, 0);
1089 int64_t dimSize = memRefType.getDimSize(r);
1090 if (ShapedType::isDynamic(dimSize))
1091 continue;
1092 cstWithShapeBounds.addBound(BoundType::UB, r, dimSize - 1);
1093 }
1094
1095
1096
1097 int64_t numElements = 1;
1098 int64_t diffConstant;
1099 for (unsigned d = 0; d < rank; d++) {
1101 std::optional<int64_t> diff =
1103 if (diff.has_value()) {
1104 diffConstant = *diff;
1105 assert(diffConstant >= 0 && "dim size bound cannot be negative");
1106 } else {
1107
1108
1109 auto dimSize = memRefType.getDimSize(d);
1110 if (ShapedType::isDynamic(dimSize))
1111 return std::nullopt;
1112 diffConstant = dimSize;
1113
1116 }
1117 numElements *= diffConstant;
1118
1119 if (lbs)
1120 lbs->push_back(lb);
1121 if (shape)
1122 shape->push_back(diffConstant);
1123 }
1124 return numElements;
1125 }
1126
1129 assert(pos < cst.getNumDimVars() && "invalid position");
1130 auto memRefType = cast(memref.getType());
1131 unsigned rank = memRefType.getRank();
1132
1133 assert(rank == cst.getNumDimVars() && "inconsistent memref region");
1134
1135 auto boundPairs = cst.getLowerAndUpperBound(
1136 pos, 0, rank, cst.getNumDimAndSymbolVars(),
1137 {}, memRefType.getContext());
1138 lbMap = boundPairs.first;
1139 ubMap = boundPairs.second;
1140 assert(lbMap && "lower bound for a region must exist");
1141 assert(ubMap && "upper bound for a region must exist");
1142 assert(lbMap.getNumInputs() == cst.getNumDimAndSymbolVars() - rank);
1143 assert(ubMap.getNumInputs() == cst.getNumDimAndSymbolVars() - rank);
1144 }
1145
1147 assert(memref == other.memref);
1148 return cst.unionBoundingBox(*other.getConstraints());
1149 }
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1170 bool addMemRefDimBounds, bool dropLocalVars,
1171 bool dropOuterIvs) {
1172 assert((isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) &&
1173 "affine read/write op expected");
1174
1176 memref = access.memref;
1177 write = access.isStore();
1178
1179 unsigned rank = access.getRank();
1180
1181 LLVM_DEBUG(llvm::dbgs() << "MemRefRegion::compute: " << *op
1182 << "\ndepth: " << loopDepth << "\n";);
1183
1184
1185 if (rank == 0) {
1188 assert(loopDepth <= ivs.size() && "invalid 'loopDepth'");
1189
1190 ivs.resize(loopDepth);
1191
1193 return success();
1194 }
1195
1196
1200
1201 unsigned numDims = accessMap.getNumDims();
1202 unsigned numSymbols = accessMap.getNumSymbols();
1203 unsigned numOperands = accessValueMap.getNumOperands();
1204
1206 operands.resize(numOperands);
1207 for (unsigned i = 0; i < numOperands; ++i)
1208 operands[i] = accessValueMap.getOperand(i);
1209
1210 if (sliceState != nullptr) {
1211 operands.reserve(operands.size() + sliceState->lbOperands[0].size());
1212
1213 for (auto extraOperand : sliceState->lbOperands[0]) {
1214 if (!llvm::is_contained(operands, extraOperand)) {
1215 operands.push_back(extraOperand);
1216 numSymbols++;
1217 }
1218 }
1219 }
1220
1221
1222
1224
1225
1226
1227 for (unsigned i = 0; i < numDims + numSymbols; ++i) {
1228 auto operand = operands[i];
1230
1231
1232
1233
1234 if (failed(cst.addAffineForOpDomain(affineFor)))
1235 return failure();
1237 if (failed(cst.addAffineParallelOpDomain(parallelOp)))
1238 return failure();
1240
1241 Value symbol = operand;
1243 cst.addBound(BoundType::EQ, symbol, constVal.value());
1244 } else {
1245 LLVM_DEBUG(llvm::dbgs() << "unknown affine dimensional value");
1246 return failure();
1247 }
1248 }
1249
1250
1251 if (sliceState != nullptr) {
1252
1253 for (auto operand : sliceState->lbOperands[0]) {
1254 if (failed(cst.addInductionVarOrTerminalSymbol(operand)))
1255 return failure();
1256 }
1257
1258 LogicalResult ret =
1259 cst.addSliceBounds(sliceState->ivs, sliceState->lbs, sliceState->ubs,
1261 assert(succeeded(ret) &&
1262 "should not fail as we never have semi-affine slice maps");
1263 (void)ret;
1264 }
1265
1266
1267 if (failed(cst.composeMap(&accessValueMap))) {
1268 op->emitError("getMemRefRegion: compose affine map failed");
1270 return failure();
1271 }
1272
1273
1274
1275
1276 cst.setDimSymbolSeparation(cst.getNumDimAndSymbolVars() - rank);
1277
1278
1279
1282 assert(loopDepth <= enclosingIVs.size() && "invalid loop depth");
1283 enclosingIVs.resize(loopDepth);
1285 cst.getValues(cst.getNumDimVars(), cst.getNumDimAndSymbolVars(), &vars);
1288 !llvm::is_contained(enclosingIVs, en.value())) {
1289 if (dropOuterIvs) {
1290 cst.projectOut(en.value());
1291 } else {
1292 unsigned varPosition;
1293 cst.findVar(en.value(), &varPosition);
1294 auto varKind = cst.getVarKindAt(varPosition);
1295 varPosition -= cst.getNumDimVars();
1296 cst.convertToLocal(varKind, varPosition, varPosition + 1);
1297 }
1298 }
1299 }
1300
1301
1302
1303 if (dropLocalVars)
1304 cst.projectOut(cst.getNumDimAndSymbolVars(), cst.getNumLocalVars());
1305
1306
1307 cst.constantFoldVarRange(cst.getNumDimVars(),
1308 cst.getNumSymbolVars());
1309
1310 assert(cst.getNumDimVars() == rank && "unexpected MemRefRegion format");
1311
1312
1313
1314
1315 if (addMemRefDimBounds) {
1316 auto memRefType = cast(memref.getType());
1317 for (unsigned r = 0; r < rank; r++) {
1318 cst.addBound(BoundType::LB, r, 0);
1319 if (memRefType.isDynamicDim(r))
1320 continue;
1321 cst.addBound(BoundType::UB, r, memRefType.getDimSize(r) - 1);
1322 }
1323 }
1324 cst.removeTrivialRedundancy();
1325
1326 LLVM_DEBUG(llvm::dbgs() << "Memory region:\n");
1327 LLVM_DEBUG(cst.dump());
1328 return success();
1329 }
1330
1331 std::optional<int64_t>
1333 auto elementType = memRefType.getElementType();
1334
1335 unsigned sizeInBits;
1336 if (elementType.isIntOrFloat()) {
1337 sizeInBits = elementType.getIntOrFloatBitWidth();
1338 } else if (auto vectorType = dyn_cast(elementType)) {
1339 if (vectorType.getElementType().isIntOrFloat())
1340 sizeInBits =
1341 vectorType.getElementTypeBitWidth() * vectorType.getNumElements();
1342 else
1343 return std::nullopt;
1344 } else {
1345 return std::nullopt;
1346 }
1348 }
1349
1350
1352 auto memRefType = cast(memref.getType());
1353
1354 if (!memRefType.getLayout().isIdentity()) {
1355 LLVM_DEBUG(llvm::dbgs() << "Non-identity layout map not yet supported\n");
1356 return false;
1357 }
1358
1359
1360 std::optional<int64_t> numElements = getConstantBoundingSizeAndShape();
1361 if (!numElements) {
1362 LLVM_DEBUG(llvm::dbgs() << "Dynamic shapes not yet supported\n");
1363 return std::nullopt;
1364 }
1366 if (!eltSize)
1367 return std::nullopt;
1368 return *eltSize * *numElements;
1369 }
1370
1371
1372
1373
1374
1375 std::optional<uint64_t>
1377 if (!memRefType.hasStaticShape())
1378 return std::nullopt;
1379 auto elementType = memRefType.getElementType();
1380 if (!elementType.isIntOrFloat() && !isa(elementType))
1381 return std::nullopt;
1382
1384 if (!sizeInBytes)
1385 return std::nullopt;
1386 for (unsigned i = 0, e = memRefType.getRank(); i < e; i++) {
1387 sizeInBytes = *sizeInBytes * memRefType.getDimSize(i);
1388 }
1389 return sizeInBytes;
1390 }
1391
1392 template
1395 static_assert(llvm::is_one_of<LoadOrStoreOp, AffineReadOpInterface,
1396 AffineWriteOpInterface>::value,
1397 "argument should be either a AffineReadOpInterface or a "
1398 "AffineWriteOpInterface");
1399
1400 Operation *op = loadOrStoreOp.getOperation();
1402 if (failed(region.compute(op, 0, nullptr,
1403 false)))
1404 return success();
1405
1406 LLVM_DEBUG(llvm::dbgs() << "Memory region");
1407 LLVM_DEBUG(region.getConstraints()->dump());
1408
1409 bool outOfBounds = false;
1410 unsigned rank = loadOrStoreOp.getMemRefType().getRank();
1411
1412
1413 for (unsigned r = 0; r < rank; r++) {
1415
1416
1417
1418
1420 int64_t dimSize = loadOrStoreOp.getMemRefType().getDimSize(r);
1421
1422 if (dimSize == -1)
1423 continue;
1424
1425
1426 ucst.addBound(BoundType::LB, r, dimSize);
1427 outOfBounds = !ucst.isEmpty();
1429 loadOrStoreOp.emitOpError()
1430 << "memref out of upper bound access along dimension #" << (r + 1);
1431 }
1432
1433
1435 std::fill(ineq.begin(), ineq.end(), 0);
1436
1437 lcst.addBound(BoundType::UB, r, -1);
1438 outOfBounds = !lcst.isEmpty();
1440 loadOrStoreOp.emitOpError()
1441 << "memref out of lower bound access along dimension #" << (r + 1);
1442 }
1443 }
1444 return failure(outOfBounds);
1445 }
1446
1447
1448 template LogicalResult
1451 template LogicalResult
1454
1455
1456
1460 while (block != limitBlock) {
1461
1462
1463 int instPosInBlock = std::distance(block->begin(), op->getIterator());
1464 positions->push_back(instPosInBlock);
1467 }
1468 std::reverse(positions->begin(), positions->end());
1469 }
1470
1471
1472
1473
1475 unsigned level, Block *block) {
1476 unsigned i = 0;
1477 for (auto &op : *block) {
1478 if (i != positions[level]) {
1479 ++i;
1480 continue;
1481 }
1482 if (level == positions.size() - 1)
1483 return &op;
1484 if (auto childAffineForOp = dyn_cast(op))
1486 childAffineForOp.getBody());
1487
1488 for (auto ®ion : op.getRegions()) {
1489 for (auto &b : region)
1491 return ret;
1492 }
1493 return nullptr;
1494 }
1495 return nullptr;
1496 }
1497
1498
1501 for (unsigned i = 0, e = cst->getNumDimVars(); i < e; ++i) {
1502 auto value = cst->getValue(i);
1503 if (ivs.count(value) == 0) {
1507 return failure();
1508 }
1509 }
1510 return success();
1511 }
1512
1513
1514
1517 unsigned numOps = ops.size();
1518 assert(numOps > 0 && "Expected at least one operation");
1519
1520 std::vector<SmallVector<AffineForOp, 4>> loops(numOps);
1522 for (unsigned i = 0; i < numOps; ++i) {
1524 loopDepthLimit =
1525 std::min(loopDepthLimit, static_cast<unsigned>(loops[i].size()));
1526 }
1527
1528 unsigned loopDepth = 0;
1529 for (unsigned d = 0; d < loopDepthLimit; ++d) {
1530 unsigned i;
1531 for (i = 1; i < numOps; ++i) {
1532 if (loops[i - 1][d] != loops[i][d])
1533 return loopDepth;
1534 }
1535 if (surroundingLoops)
1536 surroundingLoops->push_back(loops[i - 1][d]);
1537 ++loopDepth;
1538 }
1539 return loopDepth;
1540 }
1541
1542
1543
1544
1545
1549 unsigned numCommonLoops, bool isBackwardSlice,
1551
1552
1555 std::vector<std::pair<Operation *, Operation *>> dependentOpPairs;
1561 continue;
1562
1563 if ((!isBackwardSlice && loopDepth > getNestingDepth(i)) ||
1565 LLVM_DEBUG(llvm::dbgs() << "Invalid loop depth\n");
1567 }
1568
1569 bool readReadAccesses = isa(srcAccess.opInst) &&
1570 isa(dstAccess.opInst);
1572
1574 srcAccess, dstAccess, numCommonLoops + 1,
1575 &dependenceConstraints, nullptr,
1576 readReadAccesses);
1578 LLVM_DEBUG(llvm::dbgs() << "Dependence check failed\n");
1580 }
1582 continue;
1583 dependentOpPairs.emplace_back(i, j);
1584
1585
1588 loopDepth, isBackwardSlice,
1589 &tmpSliceState);
1590
1592
1593 if (failed(tmpSliceState.getAsConstraints(&sliceUnionCst))) {
1594 LLVM_DEBUG(llvm::dbgs()
1595 << "Unable to compute slice bound constraints\n");
1597 }
1599 continue;
1600 }
1601
1602
1605 LLVM_DEBUG(llvm::dbgs()
1606 << "Unable to compute slice bound constraints\n");
1608 }
1609
1610
1612
1613
1614
1616 for (unsigned k = 0, l = sliceUnionCst.getNumDimVars(); k < l; ++k)
1617 sliceUnionIVs.insert(sliceUnionCst.getValue(k));
1619 for (unsigned k = 0, l = tmpSliceCst.getNumDimVars(); k < l; ++k)
1620 tmpSliceIVs.insert(tmpSliceCst.getValue(k));
1621
1623
1624
1625
1626
1627
1628
1633 }
1634
1638 LLVM_DEBUG(llvm::dbgs()
1639 << "Unable to compute union bounding box of slice bounds\n");
1641 }
1642 }
1643 }
1644
1645
1647 LLVM_DEBUG(llvm::dbgs() << "empty slice union - unexpected\n");
1649 }
1650
1651
1653 for (auto &dep : dependentOpPairs) {
1654 ops.push_back(isBackwardSlice ? dep.second : dep.first);
1655 }
1657 unsigned innermostCommonLoopDepth =
1659 if (loopDepth > innermostCommonLoopDepth) {
1660 LLVM_DEBUG(llvm::dbgs() << "Exceeds max loop depth\n");
1662 }
1663
1664
1665 unsigned numSliceLoopIVs = sliceUnionCst.getNumDimVars();
1666
1667
1670 sliceUnion->lbs.resize(numSliceLoopIVs, AffineMap());
1671 sliceUnion->ubs.resize(numSliceLoopIVs, AffineMap());
1672
1673
1674 sliceUnionCst.getSliceBounds(0, numSliceLoopIVs,
1676 &sliceUnion->ubs);
1677
1678
1680 sliceUnionCst.getValues(numSliceLoopIVs,
1682 &sliceBoundOperands);
1683
1684
1685 sliceUnion->ivs.clear();
1686 sliceUnionCst.getValues(0, numSliceLoopIVs, &sliceUnion->ivs);
1687
1688
1689
1691 isBackwardSlice
1692 ? surroundingLoops[loopDepth - 1].getBody()->begin()
1693 : std::prev(surroundingLoops[loopDepth - 1].getBody()->end());
1694
1695
1696
1697 sliceUnion->lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1698 sliceUnion->ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1699
1700
1701
1702 std::optional isSliceValid = sliceUnion->isSliceValid();
1703 if (!isSliceValid) {
1704 LLVM_DEBUG(llvm::dbgs() << "Cannot determine if the slice is valid\n");
1706 }
1707 if (!*isSliceValid)
1709
1711 }
1712
1713
1716 assert(lbMap.getNumResults() == 1 && "expected single result bound map");
1717 assert(ubMap.getNumResults() == 1 && "expected single result bound map");
1724 auto cExpr = dyn_cast(loopSpanExpr);
1725 if (!cExpr)
1726 return std::nullopt;
1727 return cExpr.getValue();
1728 }
1729
1730
1731
1732
1733
1736 llvm::SmallDenseMap<Operation *, uint64_t, 8> *tripCountMap) {
1737 unsigned numSrcLoopIVs = slice.ivs.size();
1738
1739 for (unsigned i = 0; i < numSrcLoopIVs; ++i) {
1741 auto *op = forOp.getOperation();
1744
1745
1746
1747 if (!lbMap || lbMap.getNumResults() == 0 || !ubMap ||
1749
1750 if (forOp.hasConstantLowerBound() && forOp.hasConstantUpperBound()) {
1751 (*tripCountMap)[op] =
1752 forOp.getConstantUpperBound() - forOp.getConstantLowerBound();
1753 continue;
1754 }
1756 if (maybeConstTripCount.has_value()) {
1757 (*tripCountMap)[op] = *maybeConstTripCount;
1758 continue;
1759 }
1760 return false;
1761 }
1762 std::optional<uint64_t> tripCount = getConstDifference(lbMap, ubMap);
1763
1764 if (!tripCount.has_value())
1765 return false;
1766 (*tripCountMap)[op] = *tripCount;
1767 }
1768 return true;
1769 }
1770
1771
1773 const llvm::SmallDenseMap<Operation *, uint64_t, 8> &sliceTripCountMap) {
1774 uint64_t iterCount = 1;
1775 for (const auto &count : sliceTripCountMap) {
1776 iterCount *= count.second;
1777 }
1778 return iterCount;
1779 }
1780
1782
1783
1784
1785
1790
1793 unsigned numSrcLoopIVs = srcLoopIVs.size();
1794
1795
1798 unsigned numDstLoopIVs = dstLoopIVs.size();
1799
1800 assert((!isBackwardSlice && loopDepth <= numSrcLoopIVs) ||
1801 (isBackwardSlice && loopDepth <= numDstLoopIVs));
1802
1803
1804 unsigned pos = isBackwardSlice ? numSrcLoopIVs + loopDepth : loopDepth;
1805 unsigned num =
1806 isBackwardSlice ? numDstLoopIVs - loopDepth : numSrcLoopIVs - loopDepth;
1809
1810
1811 unsigned offset = isBackwardSlice ? 0 : loopDepth;
1812 unsigned numSliceLoopIVs = isBackwardSlice ? numSrcLoopIVs : numDstLoopIVs;
1813 sliceCst.getValues(offset, offset + numSliceLoopIVs, &sliceState->ivs);
1814
1815
1816 sliceState->lbs.resize(numSliceLoopIVs, AffineMap());
1817 sliceState->ubs.resize(numSliceLoopIVs, AffineMap());
1818
1819
1821 &sliceState->lbs, &sliceState->ubs);
1822
1823
1826 for (unsigned i = 0; i < numDimsAndSymbols; ++i) {
1827 if (i < offset || i >= offset + numSliceLoopIVs)
1828 sliceBoundOperands.push_back(sliceCst.getValue(i));
1829 }
1830
1831
1832
1833 sliceState->lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1834 sliceState->ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1835
1836
1838 isBackwardSlice ? dstLoopIVs[loopDepth - 1].getBody()->begin()
1839 : std::prev(srcLoopIVs[loopDepth - 1].getBody()->end());
1840
1841 llvm::SmallDenseSet<Value, 8> sequentialLoops;
1842 if (isa(depSourceOp) &&
1843 isa(depSinkOp)) {
1844
1845
1847 &sequentialLoops);
1848 }
1849 auto getSliceLoop = [&](unsigned i) {
1850 return isBackwardSlice ? srcLoopIVs[i] : dstLoopIVs[i];
1851 };
1852 auto isInnermostInsertion = [&]() {
1853 return (isBackwardSlice ? loopDepth >= srcLoopIVs.size()
1854 : loopDepth >= dstLoopIVs.size());
1855 };
1856 llvm::SmallDenseMap<Operation *, uint64_t, 8> sliceTripCountMap;
1857 auto srcIsUnitSlice = [&]() {
1860 };
1861
1862
1863
1864 for (unsigned i = 0; i < numSliceLoopIVs; ++i) {
1865 Value iv = getSliceLoop(i).getInductionVar();
1866 if (sequentialLoops.count(iv) == 0 &&
1868 continue;
1869
1870
1871
1872
1873
1874 std::optional isMaximal = sliceState->isMaximal();
1876 isInnermostInsertion() && srcIsUnitSlice() && isMaximal && *isMaximal)
1877 continue;
1878 for (unsigned j = i; j < numSliceLoopIVs; ++j) {
1881 }
1882 break;
1883 }
1884 }
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1901
1904 unsigned numSrcLoopIVs = srcLoopIVs.size();
1905
1906
1909 unsigned dstLoopIVsSize = dstLoopIVs.size();
1910 if (dstLoopDepth > dstLoopIVsSize) {
1911 dstOpInst->emitError("invalid destination loop depth");
1912 return AffineForOp();
1913 }
1914
1915
1917
1918 findInstPosition(srcOpInst, srcLoopIVs[0]->getBlock(), &positions);
1919
1920
1921
1922 auto dstAffineForOp = dstLoopIVs[dstLoopDepth - 1];
1923 OpBuilder b(dstAffineForOp.getBody(), dstAffineForOp.getBody()->begin());
1924 auto sliceLoopNest =
1925 cast(b.clone(*srcLoopIVs[0].getOperation()));
1926
1928 getInstAtPosition(positions, 0, sliceLoopNest.getBody());
1929
1932
1933
1934 unsigned sliceSurroundingLoopsSize = sliceSurroundingLoops.size();
1935 (void)sliceSurroundingLoopsSize;
1936 assert(dstLoopDepth + numSrcLoopIVs >= sliceSurroundingLoopsSize);
1937 unsigned sliceLoopLimit = dstLoopDepth + numSrcLoopIVs;
1938 (void)sliceLoopLimit;
1939 assert(sliceLoopLimit >= sliceSurroundingLoopsSize);
1940
1941
1942 for (unsigned i = 0; i < numSrcLoopIVs; ++i) {
1943 auto forOp = sliceSurroundingLoops[dstLoopDepth + i];
1945 forOp.setLowerBound(sliceState->lbOperands[i], lbMap);
1947 forOp.setUpperBound(sliceState->ubOperands[i], ubMap);
1948 }
1949 return sliceLoopNest;
1950 }
1951
1952
1953
1955 if (auto loadOp = dyn_cast(loadOrStoreOpInst)) {
1956 memref = loadOp.getMemRef();
1957 opInst = loadOrStoreOpInst;
1958 llvm::append_range(indices, loadOp.getMapOperands());
1959 } else {
1960 assert(isa(loadOrStoreOpInst) &&
1961 "Affine read/write op expected");
1962 auto storeOp = cast(loadOrStoreOpInst);
1963 opInst = loadOrStoreOpInst;
1964 memref = storeOp.getMemRef();
1965 llvm::append_range(indices, storeOp.getMapOperands());
1966 }
1967 }
1968
1970 return cast(memref.getType()).getRank();
1971 }
1972
1974 return isa(opInst);
1975 }
1976
1977
1978
1981 unsigned depth = 0;
1982 while ((currOp = currOp->getParentOp())) {
1983 if (isa(currOp))
1984 depth++;
1985 if (auto parOp = dyn_cast(currOp))
1986 depth += parOp.getNumDims();
1987 }
1988 return depth;
1989 }
1990
1991
1992
1993
1994
1995
1996
1998 if (memref != rhs.memref)
1999 return false;
2000
2002 getAccessMap(&thisMap);
2004 return thisMap == rhsMap;
2005 }
2006
2009 AffineForOp currAffineForOp;
2010
2011
2012 while (currOp) {
2013 if (AffineForOp currAffineForOp = dyn_cast(currOp))
2014 ivs.push_back(currAffineForOp.getInductionVar());
2015 else if (auto parOp = dyn_cast(currOp))
2016 llvm::append_range(ivs, parOp.getIVs());
2017 currOp = currOp->getParentOp();
2018 }
2019 std::reverse(ivs.begin(), ivs.end());
2020 }
2021
2022
2023
2029
2030 unsigned minNumLoops = std::min(loopsA.size(), loopsB.size());
2031 unsigned numCommonLoops = 0;
2032 for (unsigned i = 0; i < minNumLoops; ++i) {
2033 if (loopsA[i] != loopsB[i])
2034 break;
2035 ++numCommonLoops;
2036 }
2037 return numCommonLoops;
2038 }
2039
2043 int memorySpace) {
2044 SmallDenseMap<Value, std::unique_ptr, 4> regions;
2045
2046
2048 if (!isa<AffineReadOpInterface, AffineWriteOpInterface>(opInst)) {
2049
2051 }
2052
2053
2054 auto region = std::make_unique(opInst->getLoc());
2055 if (failed(
2056 region->compute(opInst,
2058 LLVM_DEBUG(opInst->emitError("error obtaining memory region"));
2059 return failure();
2060 }
2061
2062 auto [it, inserted] = regions.try_emplace(region->memref);
2063 if (inserted) {
2064 it->second = std::move(region);
2065 } else if (failed(it->second->unionBoundingBox(*region))) {
2067 "getMemoryFootprintBytes: unable to perform a union on a memory "
2068 "region"));
2069 return failure();
2070 }
2072 });
2073 if (result.wasInterrupted())
2074 return std::nullopt;
2075
2076 int64_t totalSizeInBytes = 0;
2077 for (const auto ®ion : regions) {
2078 std::optional<int64_t> size = region.second->getRegionSize();
2079 if (!size.has_value())
2080 return std::nullopt;
2081 totalSizeInBytes += *size;
2082 }
2083 return totalSizeInBytes;
2084 }
2085
2087 int memorySpace) {
2088 auto *forInst = forOp.getOperation();
2092 }
2093
2094
2098 return false;
2099 return !reductions.empty();
2100 }
2101
2102
2103
2105 AffineForOp forOp, llvm::SmallDenseSet<Value, 8> *sequentialLoops) {
2106 forOp->walk([&](Operation *op) {
2107 if (auto innerFor = dyn_cast(op))
2109 sequentialLoops->insert(innerFor.getInductionVar());
2110 });
2111 }
2112
2119
2121 assert(simplifiedSet && "guaranteed to succeed while roundtripping");
2122 return simplifiedSet;
2123 }
2124
2127 target =
2128 llvm::to_vector<4>(llvm::map_range(source, [](std::optional val) {
2129 return val.has_value() ? *val : Value();
2130 }));
2131 }
2132
2133
2134
2135
2136
2137
2138
2145
2148 for (unsigned i = syms.size(); i < newSyms.size(); ++i)
2150 return constraints.addBound(type, pos, alignedMap);
2151 }
2152
2153
2157 newResults.push_back(r + val);
2160 }
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2201 bool isMin = isa(op);
2202 assert((isMin || isa(op)) && "expect AffineMin/MaxOp");
2206 isMin ? cast(op).getMap() : cast(op).getMap();
2209
2210
2211 unsigned dimOp = constraints.appendDimVar();
2212 unsigned dimOpBound = constraints.appendDimVar();
2213 unsigned resultDimStart = constraints.appendDimVar(numResults);
2214
2215
2216
2217 auto boundType = isMin ? BoundType::UB : BoundType::LB;
2218
2220 if (failed(
2221 alignAndAddBound(constraints, boundType, dimOp, mapLbUb, operands)))
2222 return failure();
2223
2224
2225
2227 constraints.getSliceBounds(dimOp, 1, ctx, &opLb, &opUb);
2228 AffineMap sliceBound = isMin ? opUb[0] : opLb[0];
2229
2230
2231 if (!sliceBound || sliceBound.getNumResults() != 1)
2232 return failure();
2233
2235
2236
2237
2238 AffineMap alignedBoundMap = boundMap.shiftDims(1, dimOp);
2239 if (failed(constraints.addBound(BoundType::EQ, dimOpBound, alignedBoundMap)))
2240 return failure();
2241
2242
2243
2244 if (constraints.isEmpty())
2245 return failure();
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257 for (unsigned i = resultDimStart; i < resultDimStart + numResults; ++i) {
2259
2260
2261
2262
2263
2265 map.getSubMap({i - resultDimStart}), operands)))
2266 return failure();
2267
2268
2269
2270
2271
2273 ineq[dimOpBound] = isMin ? 1 : -1;
2274 ineq[i] = isMin ? -1 : 1;
2275 ineq[newConstr.getNumCols() - 1] = -1;
2277 if (!newConstr.isEmpty())
2278 return failure();
2279 }
2280
2281
2282 AffineMap newMap = alignedBoundMap;
2285
2286
2288
2290 continue;
2291 if (auto bound = constraints.getConstantBound64(BoundType::EQ, i)) {
2298 }
2299 }
2302 }
2303
2308 if (aScope != bScope)
2309 return nullptr;
2310
2311
2312
2313 auto getBlockAncestry = [&](Operation *op,
2316 do {
2317 ancestry.push_back(curOp->getBlock());
2319 break;
2321 } while (curOp);
2322 assert(curOp && "can't reach root op without passing through affine scope");
2323 std::reverse(ancestry.begin(), ancestry.end());
2324 };
2325
2327 getBlockAncestry(a, aAncestors);
2328 getBlockAncestry(b, bAncestors);
2329 assert(!aAncestors.empty() && !bAncestors.empty() &&
2330 "at least one Block ancestor expected");
2331
2332 Block *innermostCommonBlock = nullptr;
2333 for (unsigned a = 0, b = 0, e = aAncestors.size(), f = bAncestors.size();
2334 a < e && b < f; ++a, ++b) {
2335 if (aAncestors[a] != bAncestors[b])
2336 break;
2337 innermostCommonBlock = aAncestors[a];
2338 }
2339 return innermostCommonBlock;
2340 }
static std::optional< uint64_t > getConstDifference(AffineMap lbMap, AffineMap ubMap)
static void findInstPosition(Operation *op, Block *limitBlock, SmallVectorImpl< unsigned > *positions)
static Node * addNodeToMDG(Operation *nodeOp, MemRefDependenceGraph &mdg, DenseMap< Value, SetVector< unsigned >> &memrefAccesses)
Add op to MDG creating a new node and adding its memory accesses (affine or non-affine to memrefAcces...
const char *const kSliceFusionBarrierAttrName
static LogicalResult addMissingLoopIVBounds(SmallPtrSet< Value, 8 > &ivs, FlatAffineValueConstraints *cst)
static void getEffectedValues(Operation *op, SmallVectorImpl< Value > &values)
Returns the values that this op has a memref effect of type EffectTys on, not considering recursive e...
MemRefDependenceGraph::Node Node
static Operation * getInstAtPosition(ArrayRef< unsigned > positions, unsigned level, Block *block)
static std::optional< int64_t > getMemoryFootprintBytes(Block &block, Block::iterator start, Block::iterator end, int memorySpace)
static AffineMap addConstToResults(AffineMap map, int64_t val)
Add val to each result of map.
static LogicalResult alignAndAddBound(FlatAffineValueConstraints &constraints, BoundType type, unsigned pos, AffineMap map, ValueRange operands)
Bound an identifier pos in a given FlatAffineValueConstraints with constraints drawn from an affine m...
static void unpackOptionalValues(ArrayRef< std::optional< Value >> source, SmallVector< Value > &target)
static MLIRContext * getContext(OpFoldResult val)
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
A dimensional identifier appearing in an affine expression.
unsigned getPosition() const
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
MLIRContext * getContext() const
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
AffineMap shiftDims(unsigned shift, unsigned offset=0) const
Replace dims[offset ...
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
unsigned getNumInputs() const
AffineExpr getResult(unsigned idx) const
AffineMap replace(AffineExpr expr, AffineExpr replacement, unsigned numResultDims, unsigned numResultSyms) const
Sparse replace method.
AffineMap getSubMap(ArrayRef< unsigned > resultPos) const
Returns the map consisting of the resultPos subset.
Block represents an ordered list of Operations.
OpListType::iterator iterator
RetT walk(FnT &&callback)
Walk all nested operations, blocks (including this block) or regions, depending on the type of callba...
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
This class is a general helper class for creating context-global objects like types,...
AffineExpr getAffineSymbolExpr(unsigned position)
AffineExpr getAffineConstantExpr(int64_t constant)
AffineExpr getAffineDimExpr(unsigned position)
IntegerSet getAsIntegerSet(MLIRContext *context) const
Returns the constraint system as an integer set.
void getSliceBounds(unsigned offset, unsigned num, MLIRContext *context, SmallVectorImpl< AffineMap > *lbMaps, SmallVectorImpl< AffineMap > *ubMaps, bool closedUB=false)
Computes the lower and upper bounds of the first num dimensional variables (starting at offset) as an...
std::optional< int64_t > getConstantBoundOnDimSize(MLIRContext *context, unsigned pos, AffineMap *lb=nullptr, AffineMap *ub=nullptr, unsigned *minLbPos=nullptr, unsigned *minUbPos=nullptr) const
Returns a non-negative constant bound on the extent (upper bound - lower bound) of the specified vari...
FlatLinearValueConstraints represents an extension of FlatLinearConstraints where each non-local vari...
LogicalResult unionBoundingBox(const FlatLinearValueConstraints &other)
Updates the constraints to be the smallest bounding (enclosing) box that contains the points of this ...
void mergeAndAlignVarsWithOther(unsigned offset, FlatLinearValueConstraints *other)
Merge and align the variables of this and other starting at offset, so that both constraint systems g...
SmallVector< std::optional< Value > > getMaybeValues() const
Value getValue(unsigned pos) const
Returns the Value associated with the pos^th variable.
void projectOut(Value val)
Projects out the variable that is associate with Value.
bool containsVar(Value val) const
Returns true if a variable with the specified Value exists, false otherwise.
void addBound(presburger::BoundType type, Value val, int64_t value)
Adds a constant bound for the variable associated with the given Value.
unsigned appendDimVar(ValueRange vals)
bool areVarsAlignedWithOther(const FlatLinearConstraints &other)
Returns true if this constraint system and other are in the same space, i.e., if they are associated ...
void getValues(unsigned start, unsigned end, SmallVectorImpl< Value > *values) const
Returns the Values associated with variables in range [start, end).
unsigned appendSymbolVar(ValueRange vals)
An integer set representing a conjunction of one or more affine equalities and inequalities.
unsigned getNumDims() const
MLIRContext * getContext() const
static IntegerSet getEmptySet(unsigned numDims, unsigned numSymbols, MLIRContext *context)
unsigned getNumSymbols() const
MLIRContext is the top-level object for a collection of MLIR operations.
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
A trait of region holding operations that defines a new scope for polyhedral optimization purposes.
This trait indicates that the memory effects of an operation includes the effects of operations neste...
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current operation...
InFlightDiagnostic emitWarning(const Twine &message={})
Emit a warning about this operation, reporting up to any diagnostic handlers that may be listening.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
MLIRContext * getContext()
Return the context this operation is associated with.
Location getLoc()
The source location the operation was defined or derived from.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Block * getBlock()
Returns the operation block that contains this operation.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
user_range getUsers()
Returns a range of all users.
Region * getParentRegion()
Returns the region to which the instruction belongs.
result_range getResults()
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
An AffineValueMap is an affine map plus its ML value operands and results for analysis purposes.
Value getOperand(unsigned i) const
unsigned getNumOperands() const
AffineMap getAffineMap() const
FlatAffineValueConstraints is an extension of FlatLinearValueConstraints with helper functions for Af...
LogicalResult addBound(presburger::BoundType type, unsigned pos, AffineMap boundMap, ValueRange operands)
Adds a bound for the variable at the specified position with constraints being drawn from the specifi...
void convertLoopIVSymbolsToDims()
Changes all symbol variables which are loop IVs to dim variables.
LogicalResult addDomainFromSliceMaps(ArrayRef< AffineMap > lbMaps, ArrayRef< AffineMap > ubMaps, ArrayRef< Value > operands)
Adds constraints (lower and upper bounds) for each loop in the loop nest described by the bound maps ...
LogicalResult addAffineForOpDomain(AffineForOp forOp)
Adds constraints (lower and upper bounds) for the specified 'affine.for' operation's Value using IR i...
LogicalResult addSliceBounds(ArrayRef< Value > values, ArrayRef< AffineMap > lbMaps, ArrayRef< AffineMap > ubMaps, ArrayRef< Value > operands)
Adds slice lower bounds represented by lower bounds in lbMaps and upper bounds in ubMaps to each vari...
void removeTrivialRedundancy()
Removes duplicate constraints, trivially true constraints, and constraints that can be detected as re...
unsigned getNumSymbolVars() const
std::optional< int64_t > getConstantBound64(BoundType type, unsigned pos) const
The same, but casts to int64_t.
unsigned getNumVars() const
unsigned getNumLocalVars() const
unsigned getNumDimAndSymbolVars() const
bool isEmpty() const
Checks for emptiness by performing variable elimination on all variables, running the GCD test on eac...
unsigned getNumCols() const
Returns the number of columns in the constraint system.
void addInequality(ArrayRef< DynamicAPInt > inEq)
Adds an inequality (>= 0) from the coefficients specified in inEq.
unsigned getNumDimVars() const
bool isIntegerEmpty() const
Return true if all the sets in the union are known to be integer empty false otherwise.
PresburgerSet subtract(const PresburgerRelation &set) const
std::optional< uint64_t > getConstantTripCount(AffineForOp forOp)
Returns the trip count of the loop if it's a constant, std::nullopt otherwise.
IntegerSet simplifyIntegerSet(IntegerSet set)
Simplify the integer set by simplifying the underlying affine expressions by flattening and some simp...
void getEnclosingAffineOps(Operation &op, SmallVectorImpl< Operation * > *ops)
Populates 'ops' with affine operations enclosing op ordered from outermost to innermost while stoppin...
SliceComputationResult computeSliceUnion(ArrayRef< Operation * > opsA, ArrayRef< Operation * > opsB, unsigned loopDepth, unsigned numCommonLoops, bool isBackwardSlice, ComputationSliceState *sliceUnion)
Computes in 'sliceUnion' the union of all slice bounds computed at 'loopDepth' between all dependent ...
bool isAffineInductionVar(Value val)
Returns true if the provided value is the induction variable of an AffineForOp or AffineParallelOp.
bool isLoopParallelAndContainsReduction(AffineForOp forOp)
Returns whether a loop is a parallel loop and contains a reduction loop.
unsigned getNumCommonSurroundingLoops(Operation &a, Operation &b)
Returns the number of surrounding loops common to both A and B.
AffineForOp getForInductionVarOwner(Value val)
Returns the loop parent of an induction variable.
void getAffineIVs(Operation &op, SmallVectorImpl< Value > &ivs)
Populates 'ivs' with IVs of the surrounding affine.for and affine.parallel ops ordered from the outer...
void getSequentialLoops(AffineForOp forOp, llvm::SmallDenseSet< Value, 8 > *sequentialLoops)
Returns in 'sequentialLoops' all sequential loops in loop nest rooted at 'forOp'.
DependenceResult checkMemrefAccessDependence(const MemRefAccess &srcAccess, const MemRefAccess &dstAccess, unsigned loopDepth, FlatAffineValueConstraints *dependenceConstraints=nullptr, SmallVector< DependenceComponent, 2 > *dependenceComponents=nullptr, bool allowRAR=false)
void canonicalizeMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Modifies both map and operands in-place so as to:
bool isAffineForInductionVar(Value val)
Returns true if the provided value is the induction variable of an AffineForOp.
Region * getAffineAnalysisScope(Operation *op)
Returns the closest region enclosing op that is held by a non-affine operation; nullptr if there is n...
void getAffineForIVs(Operation &op, SmallVectorImpl< AffineForOp > *loops)
Populates 'loops' with IVs of the affine.for ops surrounding 'op' ordered from the outermost 'affine....
std::optional< int64_t > getMemoryFootprintBytes(AffineForOp forOp, int memorySpace=-1)
Gets the memory footprint of all data touched in the specified memory space in bytes; if the memory s...
unsigned getInnermostCommonLoopDepth(ArrayRef< Operation * > ops, SmallVectorImpl< AffineForOp > *surroundingLoops=nullptr)
Returns the innermost common loop depth for the set of operations in 'ops'.
bool isValidSymbol(Value value)
Returns true if the given value can be used as a symbol in the region of the closest surrounding op t...
void getComputationSliceState(Operation *depSourceOp, Operation *depSinkOp, const FlatAffineValueConstraints &dependenceConstraints, unsigned loopDepth, bool isBackwardSlice, ComputationSliceState *sliceState)
Computes the computation slice loop bounds for one loop nest as affine maps of the other loop nest's ...
AffineParallelOp getAffineParallelInductionVarOwner(Value val)
Returns true if the provided value is among the induction variables of an AffineParallelOp.
std::optional< uint64_t > getIntOrFloatMemRefSizeInBytes(MemRefType memRefType)
Returns the size of a memref with element type int or float in bytes if it's statically shaped,...
unsigned getNestingDepth(Operation *op)
Returns the nesting depth of this operation, i.e., the number of loops surrounding this operation.
uint64_t getSliceIterationCount(const llvm::SmallDenseMap< Operation *, uint64_t, 8 > &sliceTripCountMap)
Return the number of iterations for the slicetripCountMap provided.
bool isLoopParallel(AffineForOp forOp, SmallVectorImpl< LoopReduction > *parallelReductions=nullptr)
Returns true if ‘forOp’ is a parallel loop.
LogicalResult boundCheckLoadOrStoreOp(LoadOrStoreOpPointer loadOrStoreOp, bool emitError=true)
Checks a load or store op for an out of bound access; returns failure if the access is out of bounds ...
mlir::Block * findInnermostCommonBlockInScope(mlir::Operation *a, mlir::Operation *b)
Find the innermost common Block of a and b in the affine scope that a and b are part of.
bool buildSliceTripCountMap(const ComputationSliceState &slice, llvm::SmallDenseMap< Operation *, uint64_t, 8 > *tripCountMap)
Builds a map 'tripCountMap' from AffineForOp to constant trip count for loop nest surrounding represe...
AffineForOp insertBackwardComputationSlice(Operation *srcOpInst, Operation *dstOpInst, unsigned dstLoopDepth, ComputationSliceState *sliceState)
Creates a clone of the computation contained in the loop nest surrounding 'srcOpInst',...
FailureOr< AffineValueMap > simplifyConstrainedMinMaxOp(Operation *op, FlatAffineValueConstraints constraints)
Try to simplify the given affine.min or affine.max op to an affine map with a single result and opera...
std::optional< int64_t > getMemRefIntOrFloatEltSizeInBytes(MemRefType memRefType)
Returns the memref's element type's size in bytes where the elemental type is an int or float or a ve...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
llvm::TypeSize divideCeil(llvm::TypeSize numerator, uint64_t denominator)
Divides the known min value of the numerator by the denominator and rounds the result up to the next ...
BoundType
The type of bound: equal, lower bound or upper bound.
Include the generated interface declarations.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
AffineMap alignAffineMapWithValues(AffineMap map, ValueRange operands, ValueRange dims, ValueRange syms, SmallVector< Value > *newSyms=nullptr)
Re-indexes the dimensions and symbols of an affine map with given operands values to align with dims ...
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
AffineExpr simplifyAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols)
Simplify an affine expression by flattening and some amount of simple analysis.
ComputationSliceState aggregates loop IVs, loop bound AffineMaps and their associated operands for a ...
std::optional< bool > isSliceValid() const
Checks the validity of the slice computed.
SmallVector< Value, 4 > ivs
LogicalResult getAsConstraints(FlatAffineValueConstraints *cst) const
LogicalResult getSourceAsConstraints(FlatAffineValueConstraints &cst) const
Adds to 'cst' constraints which represent the original loop bounds on 'ivs' in 'this'.
std::vector< SmallVector< Value, 4 > > ubOperands
SmallVector< AffineMap, 4 > ubs
std::optional< bool > isMaximal() const
Returns true if the computation slice encloses all the iterations of the sliced loop nest.
SmallVector< AffineMap, 4 > lbs
Block::iterator insertPoint
std::vector< SmallVector< Value, 4 > > lbOperands
Checks whether two accesses to the same memref access the same element.
enum mlir::affine::DependenceResult::ResultEnum value
SmallVector< Operation *, 4 > memrefFrees
SmallVector< Operation *, 4 > loadOpInsts
SmallVector< Operation *, 4 > memrefStores
void collect(Operation *opToWalk)
SmallVector< Operation *, 4 > memrefLoads
SmallVector< Operation *, 4 > storeOpInsts
Encapsulates a memref load or store access information.
MemRefAccess(Operation *opInst)
Constructs a MemRefAccess from a load or store operation.
void getAccessMap(AffineValueMap *accessMap) const
Populates 'accessMap' with composition of AffineApplyOps reachable from 'indices'.
bool operator==(const MemRefAccess &rhs) const
Equal if both affine accesses can be proved to be equivalent at compile time (considering the memrefs...
SmallVector< Operation *, 4 > loads
SmallVector< Operation *, 4 > stores
unsigned hasFree(Value memref) const
SmallVector< Operation *, 4 > memrefLoads
SmallVector< Operation *, 4 > memrefStores
unsigned getStoreOpCount(Value memref) const
unsigned hasStore(Value memref) const
Returns true if there exists an operation with a write memory effect to memref in this node.
SmallVector< Operation *, 4 > memrefFrees
unsigned addNode(Operation *op)
bool writesToLiveInOrEscapingMemrefs(unsigned id) const
void removeEdge(unsigned srcId, unsigned dstId, Value value)
void addEdge(unsigned srcId, unsigned dstId, Value value)
DenseMap< unsigned, Node > nodes
void gatherDefiningNodes(unsigned id, DenseSet< unsigned > &definingNodes) const
Return all nodes which define SSA values used in node 'id'.
bool hasDependencePath(unsigned srcId, unsigned dstId) const
void clearNodeLoadAndStores(unsigned id)
const Node * getForOpNode(AffineForOp forOp) const
Operation * getFusedLoopNestInsertionPoint(unsigned srcId, unsigned dstId) const
void updateEdges(unsigned srcId, unsigned dstId, const DenseSet< Value > &privateMemRefs, bool removeSrcId)
void forEachMemRefInputEdge(unsigned id, const std::function< void(Edge)> &callback)
unsigned getOutEdgeCount(unsigned id, Value memref=nullptr) const
const Node * getNode(unsigned id) const
void removeNode(unsigned id)
void forEachMemRefOutputEdge(unsigned id, const std::function< void(Edge)> &callback)
void forEachMemRefEdge(ArrayRef< Edge > edges, const std::function< void(Edge)> &callback)
void addToNode(unsigned id, ArrayRef< Operation * > loads, ArrayRef< Operation * > stores, ArrayRef< Operation * > memrefLoads, ArrayRef< Operation * > memrefStores, ArrayRef< Operation * > memrefFrees)
unsigned getIncomingMemRefAccesses(unsigned id, Value memref) const
bool hasEdge(unsigned srcId, unsigned dstId, Value value=nullptr) const
void print(raw_ostream &os) const
A region of a memref's data space; this is typically constructed by analyzing load/store op's on this...
std::optional< int64_t > getConstantBoundingSizeAndShape(SmallVectorImpl< int64_t > *shape=nullptr, SmallVectorImpl< AffineMap > *lbs=nullptr) const
Returns a constant upper bound on the number of elements in this region if bounded by a known constan...
FlatAffineValueConstraints * getConstraints()
unsigned getRank() const
Returns the rank of the memref that this region corresponds to.
LogicalResult compute(Operation *op, unsigned loopDepth, const ComputationSliceState *sliceState=nullptr, bool addMemRefDimBounds=true, bool dropLocalVars=true, bool dropOuterIVs=true)
Computes the memory region accessed by this memref with the region represented as constraints symboli...
void getLowerAndUpperBound(unsigned pos, AffineMap &lbMap, AffineMap &ubMap) const
Gets the lower and upper bound map for the dimensional variable at pos.
std::optional< int64_t > getRegionSize()
Returns the size of this MemRefRegion in bytes.
LogicalResult unionBoundingBox(const MemRefRegion &other)
Value memref
Memref that this region corresponds to.
Enumerates different result statuses of slice computation by computeSliceUnion
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.