24#include "llvm/ADT/SetVector.h"
25#include "llvm/ADT/SmallVectorExtras.h"
26#include "llvm/Support/Debug.h"
27#include "llvm/Support/DebugLog.h"
28#include "llvm/Support/raw_ostream.h"
31#define DEBUG_TYPE "analysis-utils"
37using llvm::SmallDenseMap;
46 if (
auto forOp = dyn_cast<AffineForOp>(op)) {
48 }
else if (isa<AffineReadOpInterface>(op)) {
50 }
else if (isa<AffineWriteOpInterface>(op)) {
53 auto memInterface = dyn_cast<MemoryEffectOpInterface>(op);
60 if (!isa<MemRefType>(v.getType()))
80 unsigned loadOpCount = 0;
83 if (
auto affineLoad = dyn_cast<AffineReadOpInterface>(loadOp)) {
84 if (
memref == affineLoad.getMemRef())
95 unsigned storeOpCount = 0;
98 if (
auto affineStore = dyn_cast<AffineWriteOpInterface>(storeOp)) {
99 if (
memref == affineStore.getMemRef())
114 if (
auto affineStore = dyn_cast<AffineWriteOpInterface>(storeOp)) {
115 if (
memref == affineStore.getMemRef())
134 if (
memref == cast<AffineWriteOpInterface>(storeOp).getMemRef())
135 storeOps->push_back(storeOp);
143 if (
memref == cast<AffineReadOpInterface>(loadOp).getMemRef())
144 loadOps->push_back(loadOp);
152 llvm::SmallDenseSet<Value, 2> loadMemrefs;
154 loadMemrefs.insert(cast<AffineReadOpInterface>(loadOp).getMemRef());
157 auto memref = cast<AffineWriteOpInterface>(storeOp).getMemRef();
158 if (loadMemrefs.count(
memref) > 0)
159 loadAndStoreMemrefSet->insert(
memref);
165template <
typename... EffectTys>
167 auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
174 if (isa<MemRefType>(operand.getType()))
175 values.push_back(operand);
180 memOp.getEffects(effects);
181 for (
auto &effect : effects) {
182 Value effectVal = effect.getValue();
183 if (isa<EffectTys...>(effect.getEffect()) && effectVal &&
184 isa<MemRefType>(effectVal.
getType()))
185 values.push_back(effectVal);
194 auto &nodes = mdg.
nodes;
200 Node &node = nodes.insert({newNodeId,
Node(newNodeId, nodeOp)}).first->second;
202 node.loads.push_back(op);
203 auto memref = cast<AffineReadOpInterface>(op).getMemRef();
204 memrefAccesses[
memref].insert(node.id);
207 node.stores.push_back(op);
208 auto memref = cast<AffineWriteOpInterface>(op).getMemRef();
209 memrefAccesses[
memref].insert(node.id);
214 if (llvm::any_of(((
ValueRange)effectedValues).getTypes(),
215 [](
Type type) {
return !isa<MemRefType>(type); }))
219 memrefAccesses[
memref].insert(node.id);
220 node.memrefLoads.push_back(op);
225 if (llvm::any_of((
ValueRange(effectedValues)).getTypes(),
226 [](
Type type) {
return !isa<MemRefType>(type); }))
229 memrefAccesses[
memref].insert(node.id);
230 node.memrefStores.push_back(op);
235 if (llvm::any_of((
ValueRange(effectedValues)).getTypes(),
236 [](
Type type) {
return !isa<MemRefType>(type); }))
239 memrefAccesses[
memref].insert(node.id);
240 node.memrefFrees.push_back(op);
251 if (llvm::is_contained(effectedValues,
memref))
254 auto memoryEffectOp = dyn_cast<MemoryEffectOpInterface>(op);
259 memoryEffectOp.getEffects(effects);
260 return llvm::any_of(effects, [](
const auto &effect) {
261 if (!isa<MemoryEffects::Read, MemoryEffects::Write>(effect.getEffect()))
266 return !effect.getValue();
278 assert(srcNode.op->getBlock() == dstNode.op->getBlock());
279 if (!isa<AffineForOp>(srcNode.op) || !isa<AffineForOp>(dstNode.op))
289 return llvm::any_of(srcMemOps,
293 llvm::any_of(dstMemOps, [&](
Operation *dstOp) {
300 llvm::append_range(dstOps, llvm::concat<Operation *const>(
301 dstNode.loads, dstNode.stores,
302 dstNode.memrefLoads, dstNode.memrefStores));
303 if (hasNonAffineDep(srcNode.memrefStores, dstOps))
307 llvm::append_range(dstOps, llvm::concat<Operation *const>(
308 dstNode.stores, dstNode.memrefStores));
309 if (hasNonAffineDep(srcNode.memrefLoads, dstOps))
313 llvm::append_range(dstOps, llvm::concat<Operation *const>(
314 dstNode.memrefLoads, dstNode.memrefStores));
315 if (hasNonAffineDep(srcNode.stores, dstOps))
319 llvm::append_range(dstOps, dstNode.memrefStores);
320 if (hasNonAffineDep(srcNode.loads, dstOps))
328 for (
auto *srcMemOp :
329 llvm::concat<Operation *const>(srcNode.stores, srcNode.loads)) {
333 for (
auto *destMemOp :
334 llvm::concat<Operation *const>(dstNode.stores, dstNode.loads)) {
348 LDBG() <<
"--- Initializing MDG ---";
356 if (
auto forOp = dyn_cast<AffineForOp>(op)) {
360 forToNodeMap[&op] = node->
id;
361 }
else if (isa<AffineReadOpInterface>(op)) {
364 node.
loads.push_back(&op);
365 auto memref = cast<AffineReadOpInterface>(op).getMemRef();
366 memrefAccesses[
memref].insert(node.
id);
368 }
else if (isa<AffineWriteOpInterface>(op)) {
371 node.
stores.push_back(&op);
372 auto memref = cast<AffineWriteOpInterface>(op).getMemRef();
373 memrefAccesses[
memref].insert(node.
id);
375 }
else if (op.getNumResults() > 0 && !op.use_empty()) {
382 (op.getNumRegions() == 0 || isa<RegionBranchOpInterface>(op))) {
391 }
else if (op.getNumRegions() != 0 && !isa<RegionBranchOpInterface>(op)) {
395 LDBG() <<
"MDG init failed; unknown region-holding op found!";
403 LDBG() <<
"Created " <<
nodes.size() <<
" nodes";
407 for (
auto &idAndNode :
nodes) {
408 const Node &node = idAndNode.second;
416 if (
block.getParent()->findAncestorOpInRegion(*user)->getBlock() !=
423 auto *it = llvm::find_if(loops, [&](AffineForOp loop) {
424 return loop->getBlock() == &
block;
426 if (it == loops.end())
428 assert(forToNodeMap.count(*it) > 0 &&
"missing mapping");
429 unsigned userLoopNestId = forToNodeMap[*it];
436 for (
auto &memrefAndList : memrefAccesses) {
437 unsigned n = memrefAndList.second.size();
438 Value srcMemRef = memrefAndList.first;
440 for (
unsigned i = 0; i < n; ++i) {
441 unsigned srcId = memrefAndList.second[i];
443 bool srcHasStoreOrFree =
445 for (
unsigned j = i + 1;
j < n; ++
j) {
446 unsigned dstId = memrefAndList.second[
j];
448 bool dstHasStoreOrFree =
450 if ((srcHasStoreOrFree || dstHasStoreOrFree)) {
452 if (!fullAffineDependences ||
454 addEdge(srcId, dstId, srcMemRef);
464 auto it =
nodes.find(
id);
465 assert(it !=
nodes.end());
471 for (
auto &idAndNode :
nodes)
472 if (idAndNode.second.op == forOp)
473 return &idAndNode.second;
489 for (
auto &inEdge : oldInEdges) {
496 for (
auto &outEdge : oldOutEdges) {
510 for (
auto *storeOpInst : node->
stores) {
511 auto memref = cast<AffineWriteOpInterface>(storeOpInst).getMemRef();
512 auto *op =
memref.getDefiningOp();
518 for (
auto *user :
memref.getUsers())
519 if (!isa<AffineMapAccessInterface>(*user))
533 bool hasOutEdge = llvm::any_of(
outEdges.lookup(srcId), [=](
const Edge &edge) {
534 return edge.id == dstId && (!value || edge.value == value);
536 bool hasInEdge = llvm::any_of(
inEdges.lookup(dstId), [=](
const Edge &edge) {
537 return edge.id == srcId && (!value || edge.value == value);
539 return hasOutEdge && hasInEdge;
545 if (!
hasEdge(srcId, dstId, value)) {
546 outEdges[srcId].push_back({dstId, value});
547 inEdges[dstId].push_back({srcId, value});
548 if (isa<MemRefType>(value.
getType()))
556 assert(
inEdges.count(dstId) > 0);
558 if (isa<MemRefType>(value.
getType())) {
563 for (
auto *it =
inEdges[dstId].begin(); it !=
inEdges[dstId].end(); ++it) {
564 if ((*it).id == srcId && (*it).value == value) {
570 for (
auto *it =
outEdges[srcId].begin(); it !=
outEdges[srcId].end(); ++it) {
571 if ((*it).id == dstId && (*it).value == value) {
582 unsigned dstId)
const {
585 worklist.push_back({srcId, 0});
588 while (!worklist.empty()) {
589 auto &idAndIndex = worklist.back();
591 if (idAndIndex.first == dstId)
595 if (!
outEdges.contains(idAndIndex.first) ||
596 idAndIndex.second ==
outEdges.lookup(idAndIndex.first).size()) {
601 const Edge edge =
outEdges.lookup(idAndIndex.first)[idAndIndex.second];
608 if (!afterDst && edge.
id != idAndIndex.first)
609 worklist.push_back({edge.
id, 0});
618 unsigned inEdgeCount = 0;
620 if (inEdge.value ==
memref) {
634 unsigned outEdgeCount = 0;
635 for (
const auto &outEdge :
outEdges.lookup(
id))
648 if (!isa<MemRefType>(edge.value.getType()))
649 definingNodes.insert(edge.id);
657 unsigned dstId)
const {
664 if (llvm::any_of(definingNodes,
666 LDBG() <<
"Can't fuse: a defining op with a user in the dst "
667 <<
"loop has dependence from the src loop";
673 for (
auto &outEdge :
outEdges.lookup(srcId))
674 if (outEdge.id != dstId)
675 srcDepInsts.insert(
getNode(outEdge.id)->
op);
679 for (
auto &inEdge :
inEdges.lookup(dstId))
680 if (inEdge.id != srcId)
681 dstDepInsts.insert(
getNode(inEdge.id)->
op);
697 std::optional<unsigned> firstSrcDepPos;
698 std::optional<unsigned> lastDstDepPos;
703 if (srcDepInsts.count(op) > 0 && firstSrcDepPos == std::nullopt)
704 firstSrcDepPos = pos;
705 if (dstDepInsts.count(op) > 0)
707 depInsts.push_back(op);
711 if (firstSrcDepPos.has_value()) {
712 if (lastDstDepPos.has_value()) {
713 if (*firstSrcDepPos <= *lastDstDepPos) {
719 return depInsts[*firstSrcDepPos];
735 if (
inEdges.count(srcId) > 0) {
737 for (
auto &inEdge : oldInEdges) {
739 if (!privateMemRefs.contains(inEdge.value))
740 addEdge(inEdge.id, dstId, inEdge.value);
747 for (
auto &outEdge : oldOutEdges) {
749 if (outEdge.id == dstId)
751 else if (removeSrcId) {
752 addEdge(dstId, outEdge.id, outEdge.value);
760 if (
inEdges.count(dstId) > 0 && !privateMemRefs.empty()) {
762 for (
auto &inEdge : oldInEdges)
763 if (privateMemRefs.count(inEdge.value) > 0)
774 if (
inEdges.count(sibId) > 0) {
776 for (
auto &inEdge : oldInEdges) {
777 addEdge(inEdge.id, dstId, inEdge.value);
787 for (
auto &outEdge : oldOutEdges) {
788 addEdge(dstId, outEdge.id, outEdge.value);
801 llvm::append_range(node->
loads, loads);
802 llvm::append_range(node->
stores, stores);
803 llvm::append_range(node->
memrefLoads, memrefLoads);
805 llvm::append_range(node->
memrefFrees, memrefFrees);
817 unsigned id,
const std::function<
void(
Edge)> &callback) {
825 unsigned id,
const std::function<
void(
Edge)> &callback) {
834 for (
const auto &edge : edges) {
836 if (!isa<MemRefType>(edge.value.getType()))
838 assert(
nodes.count(edge.id) > 0);
845 os <<
"\nMemRefDependenceGraph\n";
847 for (
const auto &idAndNode :
nodes) {
848 os <<
"Node: " << idAndNode.first <<
"\n";
849 auto it =
inEdges.find(idAndNode.first);
851 for (
const auto &e : it->second)
852 os <<
" InEdge: " << e.id <<
" " << e.value <<
"\n";
854 it =
outEdges.find(idAndNode.first);
856 for (
const auto &e : it->second)
857 os <<
" OutEdge: " << e.id <<
" " << e.value <<
"\n";
862#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
869 AffineForOp currAffineForOp;
873 if (
auto currAffineForOp = dyn_cast<AffineForOp>(currOp))
874 loops->push_back(currAffineForOp);
875 currOp = currOp->getParentOp();
877 std::reverse(loops->begin(), loops->end());
888 if (isa<AffineIfOp, AffineForOp, AffineParallelOp>(currOp))
889 ops->push_back(currOp);
892 std::reverse(ops->begin(), ops->end());
899 assert(!
ivs.empty() &&
"Cannot have a slice without its IVs");
904 assert(loop &&
"Expected affine for");
916 unsigned numDims =
ivs.size();
927 for (
unsigned i = numDims, end = values.size(); i < end; ++i) {
928 Value value = values[i];
929 assert(cst->
containsVar(value) &&
"value expected to be present");
933 cst->
addBound(BoundType::EQ, value, cOp.value());
942 assert(succeeded(ret) &&
943 "should not fail as we never have semi-affine slice maps");
956#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
958 llvm::errs() <<
"\tIVs:\n";
960 llvm::errs() <<
"\t\t" << iv <<
"\n";
962 llvm::errs() <<
"\tLBs:\n";
963 for (
auto en : llvm::enumerate(
lbs)) {
964 llvm::errs() <<
"\t\t" << en.value() <<
"\n";
965 llvm::errs() <<
"\t\tOperands:\n";
967 llvm::errs() <<
"\t\t\t" << lbOp <<
"\n";
970 llvm::errs() <<
"\tUBs:\n";
971 for (
auto en : llvm::enumerate(
ubs)) {
972 llvm::errs() <<
"\t\t" << en.value() <<
"\n";
973 llvm::errs() <<
"\t\tOperands:\n";
975 llvm::errs() <<
"\t\t\t" << ubOp <<
"\n";
985std::optional<bool> ComputationSliceState::isSliceMaximalFastCheck()
const {
986 assert(
lbs.size() ==
ubs.size() && !
lbs.empty() && !
ivs.empty() &&
987 "Unexpected number of lbs, ubs and ivs in slice");
989 for (
unsigned i = 0, end =
lbs.size(); i < end; ++i) {
1001 isa<AffineConstantExpr>(lbMap.
getResult(0)))
1002 return std::nullopt;
1008 return std::nullopt;
1011 AffineForOp dstLoop =
1014 return std::nullopt;
1015 AffineMap dstLbMap = dstLoop.getLowerBoundMap();
1016 AffineMap dstUbMap = dstLoop.getUpperBoundMap();
1020 assert(srcLoop &&
"Expected affine for");
1021 AffineMap srcLbMap = srcLoop.getLowerBoundMap();
1022 AffineMap srcUbMap = srcLoop.getUpperBoundMap();
1028 return std::nullopt;
1034 if (!isa<AffineConstantExpr>(srcLbResult) ||
1035 !isa<AffineConstantExpr>(srcUbResult) ||
1036 !isa<AffineConstantExpr>(dstLbResult) ||
1037 !isa<AffineConstantExpr>(dstUbResult))
1038 return std::nullopt;
1042 if (srcLbResult != dstLbResult || srcUbResult != dstUbResult ||
1043 srcLoop.getStep() != dstLoop.getStep())
1064 std::optional<bool> isValidFastCheck = isSliceMaximalFastCheck();
1065 if (isValidFastCheck && *isValidFastCheck)
1072 LDBG() <<
"Unable to compute source's domain";
1073 return std::nullopt;
1079 LDBG() <<
"Cannot handle locals in source domain";
1080 return std::nullopt;
1087 LDBG() <<
"Unable to compute slice's domain";
1088 return std::nullopt;
1098 LDBG() <<
"Domain of the source of the slice:\n"
1099 <<
"Source constraints:" << srcConstraints
1100 <<
"\nDomain of the slice if this fusion succeeds "
1101 <<
"(expressed in terms of its source's IVs):\n"
1102 <<
"Slice constraints:" << sliceConstraints;
1110 LDBG() <<
"Incorrect slice";
1122 std::optional<bool> isMaximalFastCheck = isSliceMaximalFastCheck();
1123 if (isMaximalFastCheck)
1124 return isMaximalFastCheck;
1132 assert(loop &&
"Expected affine for");
1134 return std::nullopt;
1142 consumerIVs.push_back(lbOp);
1146 for (
int i = consumerIVs.size(), end =
ivs.size(); i < end; ++i)
1147 consumerIVs.push_back(
Value());
1154 return std::nullopt;
1159 return std::nullopt;
1170 return cast<MemRefType>(
memref.getType()).getRank();
1175 auto memRefType = cast<MemRefType>(
memref.getType());
1177 unsigned rank = memRefType.getRank();
1179 shape->reserve(rank);
1181 assert(rank ==
cst.getNumDimVars() &&
"inconsistent memref region");
1189 for (
unsigned r = 0; r < rank; r++) {
1190 cstWithShapeBounds.
addBound(BoundType::LB, r, 0);
1191 int64_t dimSize = memRefType.getDimSize(r);
1192 if (ShapedType::isDynamic(dimSize))
1194 cstWithShapeBounds.
addBound(BoundType::UB, r, dimSize - 1);
1201 for (
unsigned d = 0; d < rank; d++) {
1203 std::optional<int64_t> diff =
1205 if (diff.has_value()) {
1206 diffConstant = *diff;
1207 assert(diffConstant >= 0 &&
"dim size bound cannot be negative");
1211 auto dimSize = memRefType.getDimSize(d);
1212 if (ShapedType::isDynamic(dimSize))
1213 return std::nullopt;
1214 diffConstant = dimSize;
1219 numElements *= diffConstant;
1224 shape->push_back(diffConstant);
1231 assert(pos <
cst.getNumDimVars() &&
"invalid position");
1232 auto memRefType = cast<MemRefType>(
memref.getType());
1233 unsigned rank = memRefType.getRank();
1235 assert(rank ==
cst.getNumDimVars() &&
"inconsistent memref region");
1237 auto boundPairs =
cst.getLowerAndUpperBound(
1238 pos, 0, rank,
cst.getNumDimAndSymbolVars(),
1239 {}, memRefType.getContext());
1240 lbMap = boundPairs.first;
1241 ubMap = boundPairs.second;
1242 assert(lbMap &&
"lower bound for a region must exist");
1243 assert(ubMap &&
"upper bound for a region must exist");
1272 bool addMemRefDimBounds,
bool dropLocalVars,
1273 bool dropOuterIvs) {
1274 assert((isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) &&
1275 "affine read/write op expected");
1281 unsigned rank = access.
getRank();
1283 LDBG() <<
"MemRefRegion::compute: " << *op <<
" depth: " << loopDepth;
1289 assert(loopDepth <= ivs.size() &&
"invalid 'loopDepth'");
1291 ivs.resize(loopDepth);
1307 operands.resize(numOperands);
1308 for (
unsigned i = 0; i < numOperands; ++i)
1311 if (sliceState !=
nullptr) {
1312 operands.reserve(operands.size() + sliceState->
lbOperands[0].size());
1314 for (
auto extraOperand : sliceState->
lbOperands[0]) {
1315 if (!llvm::is_contained(operands, extraOperand)) {
1316 operands.push_back(extraOperand);
1328 for (
unsigned i = 0; i < numDims + numSymbols; ++i) {
1329 auto operand = operands[i];
1335 if (failed(
cst.addAffineForOpDomain(affineFor)))
1338 if (failed(
cst.addAffineParallelOpDomain(parallelOp)))
1342 Value symbol = operand;
1344 cst.addBound(BoundType::EQ, symbol, constVal.value());
1346 LDBG() <<
"unknown affine dimensional value";
1352 if (sliceState !=
nullptr) {
1354 for (
auto operand : sliceState->
lbOperands[0]) {
1355 if (failed(
cst.addInductionVarOrTerminalSymbol(operand)))
1360 cst.addSliceBounds(sliceState->
ivs, sliceState->
lbs, sliceState->
ubs,
1362 assert(succeeded(ret) &&
1363 "should not fail as we never have semi-affine slice maps");
1368 if (failed(
cst.composeMap(&accessValueMap))) {
1369 op->
emitError(
"getMemRefRegion: compose affine map failed");
1370 LDBG() <<
"Access map: " << accessValueMap.
getAffineMap();
1377 cst.setDimSymbolSeparation(
cst.getNumDimAndSymbolVars() - rank);
1383 assert(loopDepth <= enclosingIVs.size() &&
"invalid loop depth");
1384 enclosingIVs.resize(loopDepth);
1386 cst.getValues(
cst.getNumDimVars(),
cst.getNumDimAndSymbolVars(), &vars);
1387 for (
auto en : llvm::enumerate(vars)) {
1389 !llvm::is_contained(enclosingIVs, en.value())) {
1391 cst.projectOut(en.value());
1393 unsigned varPosition;
1394 cst.findVar(en.value(), &varPosition);
1395 auto varKind =
cst.getVarKindAt(varPosition);
1396 varPosition -=
cst.getNumDimVars();
1397 cst.convertToLocal(varKind, varPosition, varPosition + 1);
1405 cst.projectOut(
cst.getNumDimAndSymbolVars(),
cst.getNumLocalVars());
1408 cst.constantFoldVarRange(
cst.getNumDimVars(),
1409 cst.getNumSymbolVars());
1411 assert(
cst.getNumDimVars() == rank &&
"unexpected MemRefRegion format");
1416 if (addMemRefDimBounds) {
1417 auto memRefType = cast<MemRefType>(
memref.getType());
1418 for (
unsigned r = 0; r < rank; r++) {
1419 cst.addBound(BoundType::LB, r, 0);
1420 if (memRefType.isDynamicDim(r))
1422 cst.addBound(BoundType::UB, r, memRefType.getDimSize(r) - 1);
1425 cst.removeTrivialRedundancy();
1427 LDBG() <<
"Memory region: " <<
cst;
1431std::optional<int64_t>
1433 auto elementType = memRefType.getElementType();
1435 unsigned sizeInBits;
1436 if (elementType.isIntOrFloat()) {
1437 sizeInBits = elementType.getIntOrFloatBitWidth();
1438 }
else if (
auto vectorType = dyn_cast<VectorType>(elementType)) {
1439 if (vectorType.getElementType().isIntOrFloat())
1441 vectorType.getElementTypeBitWidth() * vectorType.getNumElements();
1443 return std::nullopt;
1445 return std::nullopt;
1447 return llvm::divideCeil(sizeInBits, 8);
1452 auto memRefType = cast<MemRefType>(
memref.getType());
1454 if (!memRefType.getLayout().isIdentity()) {
1455 LDBG() <<
"Non-identity layout map not yet supported";
1462 LDBG() <<
"Dynamic shapes not yet supported";
1463 return std::nullopt;
1467 return std::nullopt;
1468 return *eltSize * *numElements;
1475std::optional<uint64_t>
1477 if (!memRefType.hasStaticShape())
1478 return std::nullopt;
1479 auto elementType = memRefType.getElementType();
1480 if (!elementType.isIntOrFloat() && !isa<VectorType>(elementType))
1481 return std::nullopt;
1485 return std::nullopt;
1486 for (
unsigned i = 0, e = memRefType.getRank(); i < e; i++) {
1487 sizeInBytes = *sizeInBytes * memRefType.getDimSize(i);
1492template <
typename LoadOrStoreOp>
1495 static_assert(llvm::is_one_of<LoadOrStoreOp, AffineReadOpInterface,
1496 AffineWriteOpInterface>::value,
1497 "argument should be either a AffineReadOpInterface or a "
1498 "AffineWriteOpInterface");
1500 Operation *op = loadOrStoreOp.getOperation();
1502 if (failed(region.compute(op, 0,
nullptr,
1506 LDBG() <<
"Memory region: " << region.getConstraints();
1508 bool outOfBounds =
false;
1509 unsigned rank = loadOrStoreOp.getMemRefType().getRank();
1512 for (
unsigned r = 0; r < rank; r++) {
1519 int64_t dimSize = loadOrStoreOp.getMemRefType().getDimSize(r);
1525 ucst.addBound(BoundType::LB, r, dimSize);
1526 outOfBounds = !ucst.isEmpty();
1528 loadOrStoreOp.emitOpError()
1529 <<
"memref out of upper bound access along dimension #" << (r + 1);
1534 llvm::fill(ineq, 0);
1536 lcst.addBound(BoundType::UB, r, -1);
1537 outOfBounds = !lcst.isEmpty();
1539 loadOrStoreOp.emitOpError()
1540 <<
"memref out of lower bound access along dimension #" << (r + 1);
1543 return failure(outOfBounds);
1547template LogicalResult
1550template LogicalResult
1559 while (block != limitBlock) {
1562 int instPosInBlock = std::distance(block->
begin(), op->getIterator());
1563 positions->push_back(instPosInBlock);
1567 std::reverse(positions->begin(), positions->end());
1574 unsigned level,
Block *block) {
1576 for (
auto &op : *block) {
1577 if (i != positions[level]) {
1581 if (level == positions.size() - 1)
1583 if (
auto childAffineForOp = dyn_cast<AffineForOp>(op))
1585 childAffineForOp.getBody());
1588 for (
auto &
b : region)
1600 for (
unsigned i = 0, e = cst->
getNumDimVars(); i < e; ++i) {
1602 if (ivs.count(value) == 0) {
1616 unsigned numOps = ops.size();
1617 assert(numOps > 0 &&
"Expected at least one operation");
1619 std::vector<SmallVector<AffineForOp, 4>> loops(numOps);
1620 unsigned loopDepthLimit = std::numeric_limits<unsigned>::max();
1621 for (
unsigned i = 0; i < numOps; ++i) {
1623 loopDepthLimit = std::min(loopDepthLimit, (
unsigned)loops[i].size());
1626 unsigned loopDepth = 0;
1627 for (
unsigned d = 0; d < loopDepthLimit; ++d) {
1629 for (i = 1; i < numOps; ++i) {
1630 if (loops[i - 1][d] != loops[i][d])
1633 if (surroundingLoops)
1634 surroundingLoops->push_back(loops[i - 1][d]);
1647 unsigned numCommonLoops,
bool isBackwardSlice,
1653 std::vector<std::pair<Operation *, Operation *>> dependentOpPairs;
1665 LDBG() <<
"Invalid loop depth";
1669 bool readReadAccesses = isa<AffineReadOpInterface>(srcAccess.
opInst) &&
1670 isa<AffineReadOpInterface>(dstAccess.
opInst);
1674 srcAccess, dstAccess, numCommonLoops + 1,
1675 &dependenceConstraints,
nullptr,
1678 LDBG() <<
"Dependence check failed";
1683 dependentOpPairs.emplace_back(a,
b);
1688 isBackwardSlice, &tmpSliceState);
1693 LDBG() <<
"Unable to compute slice bound constraints";
1703 LDBG() <<
"Unable to compute slice bound constraints";
1713 for (
unsigned k = 0, l = sliceUnionCst.
getNumDimVars(); k < l; ++k)
1714 sliceUnionIVs.insert(sliceUnionCst.
getValue(k));
1716 for (
unsigned k = 0, l = tmpSliceCst.
getNumDimVars(); k < l; ++k)
1717 tmpSliceIVs.insert(tmpSliceCst.
getValue(k));
1735 LDBG() <<
"Unable to compute union bounding box of slice bounds";
1743 LDBG() <<
"empty slice union - unexpected";
1749 for (
auto &dep : dependentOpPairs) {
1750 ops.push_back(isBackwardSlice ? dep.second : dep.first);
1753 unsigned innermostCommonLoopDepth =
1755 if (loopDepth > innermostCommonLoopDepth) {
1756 LDBG() <<
"Exceeds max loop depth";
1772 &sliceUnion->
ubs,
false,
1777 sliceUnionCst.
getValues(numSliceLoopIVs,
1779 &sliceBoundOperands);
1782 sliceUnion->
ivs.clear();
1783 sliceUnionCst.
getValues(0, numSliceLoopIVs, &sliceUnion->
ivs);
1789 ? surroundingLoops[loopDepth - 1].getBody()->begin()
1790 : std::prev(surroundingLoops[loopDepth - 1].getBody()->end());
1794 sliceUnion->
lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1795 sliceUnion->
ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1799 std::optional<bool> isSliceValid = sliceUnion->
isSliceValid();
1800 if (!isSliceValid) {
1801 LDBG() <<
"Cannot determine if the slice is valid";
1821 assert(lbMap.
getNumResults() == 1 &&
"expected single result lower bound");
1822 assert(ubMap.
getNumResults() >= 1 &&
"expected at least one upper bound");
1826 std::optional<uint64_t> tripCount;
1830 auto cExpr = dyn_cast<AffineConstantExpr>(loopSpanExpr);
1833 if (cExpr.getValue() < 0)
1836 std::min(tripCount.value_or(std::numeric_limits<uint64_t>::max()),
1837 (uint64_t)cExpr.getValue());
1848 llvm::SmallDenseMap<Operation *, uint64_t, 8> *tripCountMap) {
1849 unsigned numSrcLoopIVs =
slice.ivs.size();
1851 for (
unsigned i = 0; i < numSrcLoopIVs; ++i) {
1853 auto *op = forOp.getOperation();
1862 if (forOp.hasConstantLowerBound() && forOp.hasConstantUpperBound()) {
1863 (*tripCountMap)[op] =
1864 forOp.getConstantUpperBound() - forOp.getConstantLowerBound();
1867 std::optional<APInt> maybeConstTripCount = forOp.getStaticTripCount();
1868 if (maybeConstTripCount.has_value()) {
1869 (*tripCountMap)[op] = maybeConstTripCount->getZExtValue();
1876 if (!tripCount.has_value())
1878 (*tripCountMap)[op] = *tripCount;
1885 const llvm::SmallDenseMap<Operation *, uint64_t, 8> &sliceTripCountMap) {
1886 uint64_t iterCount = 1;
1887 for (
const auto &count : sliceTripCountMap) {
1888 iterCount *= count.second;
1905 unsigned numSrcLoopIVs = srcLoopIVs.size();
1910 unsigned numDstLoopIVs = dstLoopIVs.size();
1912 assert((!isBackwardSlice && loopDepth <= numSrcLoopIVs) ||
1913 (isBackwardSlice && loopDepth <= numDstLoopIVs));
1916 unsigned pos = isBackwardSlice ? numSrcLoopIVs + loopDepth : loopDepth;
1918 isBackwardSlice ? numDstLoopIVs - loopDepth : numSrcLoopIVs - loopDepth;
1923 unsigned offset = isBackwardSlice ? 0 : loopDepth;
1924 unsigned numSliceLoopIVs = isBackwardSlice ? numSrcLoopIVs : numDstLoopIVs;
1925 sliceCst.
getValues(offset, offset + numSliceLoopIVs, &sliceState->
ivs);
1933 &sliceState->
lbs, &sliceState->
ubs,
1939 for (
unsigned i = 0; i < numDimsAndSymbols; ++i) {
1940 if (i < offset || i >= offset + numSliceLoopIVs)
1941 sliceBoundOperands.push_back(sliceCst.
getValue(i));
1946 sliceState->
lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1947 sliceState->
ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1951 isBackwardSlice ? dstLoopIVs[loopDepth - 1].getBody()->begin()
1952 : std::prev(srcLoopIVs[loopDepth - 1].getBody()->end());
1954 llvm::SmallDenseSet<Value, 8> sequentialLoops;
1955 if (isa<AffineReadOpInterface>(depSourceOp) &&
1956 isa<AffineReadOpInterface>(depSinkOp)) {
1962 auto getSliceLoop = [&](
unsigned i) {
1963 return isBackwardSlice ? srcLoopIVs[i] : dstLoopIVs[i];
1965 auto isInnermostInsertion = [&]() {
1966 return (isBackwardSlice ? loopDepth >= srcLoopIVs.size()
1967 : loopDepth >= dstLoopIVs.size());
1969 llvm::SmallDenseMap<Operation *, uint64_t, 8> sliceTripCountMap;
1970 auto srcIsUnitSlice = [&]() {
1977 for (
unsigned i = 0; i < numSliceLoopIVs; ++i) {
1978 Value iv = getSliceLoop(i).getInductionVar();
1979 if (sequentialLoops.count(iv) == 0 &&
1988 std::optional<bool> isMaximal = sliceState->
isMaximal();
1990 isInnermostInsertion() && srcIsUnitSlice() && isMaximal && *isMaximal)
1992 for (
unsigned j = i;
j < numSliceLoopIVs; ++
j) {
2018 unsigned numSrcLoopIVs = srcLoopIVs.size();
2023 unsigned dstLoopIVsSize = dstLoopIVs.size();
2024 if (dstLoopDepth > dstLoopIVsSize) {
2025 dstOpInst->
emitError(
"invalid destination loop depth");
2026 return AffineForOp();
2036 auto dstAffineForOp = dstLoopIVs[dstLoopDepth - 1];
2037 OpBuilder b(dstAffineForOp.getBody(), dstAffineForOp.getBody()->begin());
2038 auto sliceLoopNest =
2039 cast<AffineForOp>(
b.clone(*srcLoopIVs[0].getOperation()));
2048 unsigned sliceSurroundingLoopsSize = sliceSurroundingLoops.size();
2049 (
void)sliceSurroundingLoopsSize;
2050 assert(dstLoopDepth + numSrcLoopIVs >= sliceSurroundingLoopsSize);
2051 unsigned sliceLoopLimit = dstLoopDepth + numSrcLoopIVs;
2052 (
void)sliceLoopLimit;
2053 assert(sliceLoopLimit >= sliceSurroundingLoopsSize);
2056 for (
unsigned i = 0; i < numSrcLoopIVs; ++i) {
2057 auto forOp = sliceSurroundingLoops[dstLoopDepth + i];
2059 forOp.setLowerBound(sliceState->
lbOperands[i], lbMap);
2061 forOp.setUpperBound(sliceState->
ubOperands[i], ubMap);
2063 return sliceLoopNest;
2069 if (
auto loadOp = dyn_cast<AffineReadOpInterface>(memOp)) {
2070 memref = loadOp.getMemRef();
2072 llvm::append_range(
indices, loadOp.getMapOperands());
2074 assert(isa<AffineWriteOpInterface>(memOp) &&
2075 "Affine read/write op expected");
2076 auto storeOp = cast<AffineWriteOpInterface>(memOp);
2078 memref = storeOp.getMemRef();
2079 llvm::append_range(
indices, storeOp.getMapOperands());
2084 return cast<MemRefType>(
memref.getType()).getRank();
2088 return isa<AffineWriteOpInterface>(
opInst);
2097 if (isa<AffineForOp>(currOp))
2099 if (
auto parOp = dyn_cast<AffineParallelOp>(currOp))
2100 depth += parOp.getNumDims();
2112 if (
memref != rhs.memref)
2117 rhs.getAccessMap(&rhsMap);
2118 return thisMap == rhsMap;
2123 AffineForOp currAffineForOp;
2127 if (AffineForOp currAffineForOp = dyn_cast<AffineForOp>(currOp))
2128 ivs.push_back(currAffineForOp.getInductionVar());
2129 else if (
auto parOp = dyn_cast<AffineParallelOp>(currOp))
2130 llvm::append_range(ivs, parOp.getIVs());
2131 currOp = currOp->getParentOp();
2133 std::reverse(ivs.begin(), ivs.end());
2144 unsigned minNumLoops = std::min(loopsA.size(), loopsB.size());
2145 unsigned numCommonLoops = 0;
2146 for (
unsigned i = 0; i < minNumLoops; ++i) {
2147 if (loopsA[i] != loopsB[i])
2151 return numCommonLoops;
2162 if (!isa<AffineReadOpInterface, AffineWriteOpInterface>(opInst)) {
2168 auto region = std::make_unique<MemRefRegion>(opInst->
getLoc());
2170 region->compute(opInst,
2172 LDBG() <<
"Error obtaining memory region";
2173 opInst->
emitError(
"error obtaining memory region");
2177 auto [it,
inserted] = regions.try_emplace(region->memref);
2179 it->second = std::move(region);
2180 }
else if (failed(it->second->unionBoundingBox(*region))) {
2181 LDBG() <<
"getMemoryFootprintBytes: unable to perform a union on a "
2184 "getMemoryFootprintBytes: unable to perform a union on a memory "
2190 if (
result.wasInterrupted())
2191 return std::nullopt;
2194 for (
const auto ®ion : regions) {
2195 std::optional<int64_t> size = region.second->getRegionSize();
2196 if (!size.has_value())
2197 return std::nullopt;
2198 totalSizeInBytes += *size;
2200 return totalSizeInBytes;
2205 auto *forInst = forOp.getOperation();
2206 return ::getMemoryFootprintBytes(
2214 if (!isLoopParallel(forOp, &reductions))
2216 return !reductions.empty();
2222 AffineForOp forOp, llvm::SmallDenseSet<Value, 8> *sequentialLoops) {
2224 if (
auto innerFor = dyn_cast<AffineForOp>(op))
2225 if (!isLoopParallel(innerFor))
2226 sequentialLoops->insert(innerFor.getInductionVar());
2231 FailureOr<FlatAffineValueConstraints> fac =
2239 fac->removeTrivialRedundancy();
2241 auto simplifiedSet = fac->getAsIntegerSet(set.
getContext());
2242 assert(simplifiedSet &&
"guaranteed to succeed while roundtripping");
2243 return simplifiedSet;
2248 target = llvm::map_to_vector<4>(source, [](std::optional<Value> val) {
2249 return val.has_value() ? *val :
Value();
2268 for (
unsigned i = syms.size(); i < newSyms.size(); ++i)
2270 return constraints.
addBound(type, pos, alignedMap);
2277 newResults.push_back(r + val);
2321 bool isMin = isa<AffineMinOp>(op);
2322 assert((isMin || isa<AffineMaxOp>(op)) &&
"expect AffineMin/MaxOp");
2326 isMin ? cast<AffineMinOp>(op).getMap() : cast<AffineMaxOp>(op).getMap();
2333 unsigned resultDimStart = constraints.
appendDimVar(numResults);
2337 auto boundType = isMin ? BoundType::UB : BoundType::LB;
2348 AffineMap sliceBound = isMin ? opUb[0] : opLb[0];
2359 if (failed(constraints.
addBound(BoundType::EQ, dimOpBound, alignedBoundMap)))
2377 for (
unsigned i = resultDimStart; i < resultDimStart + numResults; ++i) {
2385 map.
getSubMap({i - resultDimStart}), operands)))
2393 ineq[dimOpBound] = isMin ? 1 : -1;
2394 ineq[i] = isMin ? -1 : 1;
2433 filteredOperands.reserve(newOperands.size());
2434 unsigned newDim = 0;
2435 for (
unsigned i = 0; i < numDims; ++i) {
2436 if (newOperands[i]) {
2438 filteredOperands.push_back(newOperands[i]);
2441 "null-valued dim operand referenced in bound map");
2445 unsigned newSym = 0;
2446 for (
unsigned i = 0; i < numSyms; ++i) {
2447 if (newOperands[numDims + i]) {
2449 filteredOperands.push_back(newOperands[numDims + i]);
2452 "null-valued symbol operand referenced in bound map");
2458 newOperands = std::move(filteredOperands);
2469 if (aScope != bScope)
2474 auto getBlockAncestry = [&](
Operation *op,
2478 ancestry.push_back(curOp->
getBlock());
2483 assert(curOp &&
"can't reach root op without passing through affine scope");
2484 std::reverse(ancestry.begin(), ancestry.end());
2488 getBlockAncestry(a, aAncestors);
2489 getBlockAncestry(
b, bAncestors);
2490 assert(!aAncestors.empty() && !bAncestors.empty() &&
2491 "at least one Block ancestor expected");
2493 Block *innermostCommonBlock =
nullptr;
2494 for (
unsigned a = 0,
b = 0, e = aAncestors.size(), f = bAncestors.size();
2495 a < e &&
b < f; ++a, ++
b) {
2496 if (aAncestors[a] != bAncestors[
b])
2498 innermostCommonBlock = aAncestors[a];
2500 return innermostCommonBlock;
static std::optional< uint64_t > getConstDifference(AffineMap lbMap, AffineMap ubMap)
Returns the number of iterations the slice bounded below by lbMap and above by ubMap runs for,...
static bool mayAccessMemRef(Operation *op, Value memref)
Returns true if op may read from or write to memref.
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...
static bool mayDependence(const Node &srcNode, const Node &dstNode, Value memref)
Returns true if there may be a dependence on memref from srcNode's memory ops to dstNode's memory ops...
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 void unpackOptionalValues(ArrayRef< std::optional< Value > > source, SmallVector< Value > &target)
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 Operation * getInstAtPosition(ArrayRef< unsigned > positions, unsigned level, Block *block)
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
template bool mlir::hasEffect< MemoryEffects::Free >(Operation *)
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
A dimensional identifier appearing in an affine expression.
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
bool isFunctionOfDim(unsigned position) const
Return true if any affine expression involves AffineDimExpr position.
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 ... numDims) by dims[offset + shift ... shift + numDims).
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
bool isFunctionOfSymbol(unsigned position) const
Return true if any affine expression involves AffineSymbolExpr position.
unsigned getNumResults() const
AffineMap replaceDimsAndSymbols(ArrayRef< AffineExpr > dimReplacements, ArrayRef< AffineExpr > symReplacements, unsigned numResultDims, unsigned numResultSyms) const
This method substitutes any uses of dimensions and symbols (e.g.
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)
void getSliceBounds(unsigned offset, unsigned num, MLIRContext *context, SmallVectorImpl< AffineMap > *lbMaps, SmallVectorImpl< AffineMap > *ubMaps, bool closedUB=false, bool allowMultiResultUB=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...
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).
SmallVector< std::optional< Value > > getMaybeValues() const
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.
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.
Block * getBlock()
Returns the operation block that contains this operation.
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...
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
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),...
user_range getUsers()
Returns a range of all users.
result_range getResults()
Region * getParentRegion()
Returns the region to which the instruction belongs.
MLIRContext * getContext()
Return the context this operation is associated with.
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...
static FailureOr< FlatAffineValueConstraints > create(IntegerSet set, ValueRange operands={})
Creates an affine constraint system from an IntegerSet.
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...
unsigned getNumSymbolVars() const
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.
std::optional< int64_t > getConstantBound64(BoundType type, unsigned pos) const
The same, but casts to int64_t.
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
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.
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 noDependence(DependenceResult result)
Returns true if the provided DependenceResult corresponds to the absence of a dependence.
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...
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.
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
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 ...
llvm::SetVector< T, Vector, Set, N > SetVector
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
bool hasEffect(Operation *op)
Returns "true" if op has an effect of type EffectTy.
AffineExpr simplifyAffineExpr(AffineExpr expr, unsigned numDims, unsigned numSymbols)
Simplify an affine expression by flattening and some amount of simple analysis.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
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
LLVM_DUMP_METHOD void dump() const
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.
SmallVector< Operation *, 4 > memrefFrees
SmallVector< AffineForOp, 4 > forOps
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.
SmallVector< Value, 4 > indices
void getAccessMap(AffineValueMap *accessMap) const
Populates 'accessMap' with composition of AffineApplyOps reachable from 'indices'.
MemRefAccess(Operation *memOp)
Constructs a MemRefAccess from an affine read/write operation.
bool operator==(const MemRefAccess &rhs) const
Equal if both affine accesses can be proved to be equivalent at compile time (considering the memrefs...
void getStoreOpsForMemref(Value memref, SmallVectorImpl< Operation * > *storeOps) const
SmallVector< Operation *, 4 > loads
SmallVector< Operation *, 4 > stores
void getLoadAndStoreMemrefSet(DenseSet< Value > *loadAndStoreMemrefSet) const
unsigned hasFree(Value memref) const
SmallVector< Operation *, 4 > memrefLoads
SmallVector< Operation *, 4 > memrefStores
unsigned getLoadOpCount(Value memref) const
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.
void getLoadOpsForMemref(Value memref, SmallVectorImpl< Operation * > *loadOps) const
SmallVector< Operation *, 4 > memrefFrees
DenseMap< unsigned, SmallVector< Edge, 2 > > outEdges
Block & block
The block for which this graph is created to perform fusion.
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)
DenseMap< unsigned, SmallVector< Edge, 2 > > inEdges
void forEachMemRefInputEdge(unsigned id, const std::function< void(Edge)> &callback)
bool init(bool fullAffineDependences=true)
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
LLVM_DUMP_METHOD void dump() const
DenseMap< Value, unsigned > memrefEdgeCount
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...
unsigned getRank() const
Returns the rank of the memref that this region corresponds to.
FlatAffineValueConstraints cst
Region (data space) of the memref accessed.
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)
FlatAffineValueConstraints * getConstraints()
Value memref
Memref that this region corresponds to.
MemRefRegion(Location loc)
Enumerates different result statuses of slice computation by computeSliceUnion
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.