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())
135 storeOps->push_back(storeOp);
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);
248 if (
auto memrefLoad = dyn_cast<memref::LoadOp>(memOp))
249 return memrefLoad.getMemRef();
250 if (
auto affineLoad = dyn_cast<AffineReadOpInterface>(memOp))
251 return affineLoad.getMemRef();
252 if (
auto memrefStore = dyn_cast<memref::StoreOp>(memOp))
253 return memrefStore.getMemRef();
254 if (
auto affineStore = dyn_cast<AffineWriteOpInterface>(memOp))
255 return affineStore.getMemRef();
256 llvm_unreachable(
"unexpected op");
267 assert(srcNode.op->getBlock() == dstNode.op->getBlock());
268 if (!isa<AffineForOp>(srcNode.op) || !isa<AffineForOp>(dstNode.op))
278 return llvm::any_of(srcMemOps, [&](
Operation *srcOp) {
282 return llvm::find_if(dstMemOps, [&](
Operation *dstOp) {
284 }) != dstMemOps.end();
290 llvm::append_range(dstOps, llvm::concat<Operation *const>(
291 dstNode.loads, dstNode.stores,
292 dstNode.memrefLoads, dstNode.memrefStores));
293 if (hasNonAffineDep(srcNode.memrefStores, dstOps))
297 llvm::append_range(dstOps, llvm::concat<Operation *const>(
298 dstNode.stores, dstNode.memrefStores));
299 if (hasNonAffineDep(srcNode.memrefLoads, dstOps))
303 llvm::append_range(dstOps, llvm::concat<Operation *const>(
304 dstNode.memrefLoads, dstNode.memrefStores));
305 if (hasNonAffineDep(srcNode.stores, dstOps))
309 llvm::append_range(dstOps, dstNode.memrefStores);
310 if (hasNonAffineDep(srcNode.loads, dstOps))
318 for (
auto *srcMemOp :
319 llvm::concat<Operation *const>(srcNode.stores, srcNode.loads)) {
323 for (
auto *destMemOp :
324 llvm::concat<Operation *const>(dstNode.stores, dstNode.loads)) {
338 LDBG() <<
"--- Initializing MDG ---";
346 if (
auto forOp = dyn_cast<AffineForOp>(op)) {
350 forToNodeMap[&op] = node->
id;
351 }
else if (isa<AffineReadOpInterface>(op)) {
354 node.
loads.push_back(&op);
355 auto memref = cast<AffineReadOpInterface>(op).getMemRef();
356 memrefAccesses[
memref].insert(node.
id);
358 }
else if (isa<AffineWriteOpInterface>(op)) {
361 node.
stores.push_back(&op);
362 auto memref = cast<AffineWriteOpInterface>(op).getMemRef();
363 memrefAccesses[
memref].insert(node.
id);
365 }
else if (op.getNumResults() > 0 && !op.use_empty()) {
372 (op.getNumRegions() == 0 || isa<RegionBranchOpInterface>(op))) {
381 }
else if (op.getNumRegions() != 0 && !isa<RegionBranchOpInterface>(op)) {
385 LDBG() <<
"MDG init failed; unknown region-holding op found!";
393 LDBG() <<
"Created " <<
nodes.size() <<
" nodes";
397 for (
auto &idAndNode :
nodes) {
398 const Node &node = idAndNode.second;
406 if (
block.getParent()->findAncestorOpInRegion(*user)->getBlock() !=
413 auto *it = llvm::find_if(loops, [&](AffineForOp loop) {
414 return loop->getBlock() == &
block;
416 if (it == loops.end())
418 assert(forToNodeMap.count(*it) > 0 &&
"missing mapping");
419 unsigned userLoopNestId = forToNodeMap[*it];
426 for (
auto &memrefAndList : memrefAccesses) {
427 unsigned n = memrefAndList.second.size();
428 Value srcMemRef = memrefAndList.first;
430 for (
unsigned i = 0; i < n; ++i) {
431 unsigned srcId = memrefAndList.second[i];
433 bool srcHasStoreOrFree =
435 for (
unsigned j = i + 1;
j < n; ++
j) {
436 unsigned dstId = memrefAndList.second[
j];
438 bool dstHasStoreOrFree =
440 if ((srcHasStoreOrFree || dstHasStoreOrFree)) {
442 if (!fullAffineDependences ||
444 addEdge(srcId, dstId, srcMemRef);
454 auto it =
nodes.find(
id);
455 assert(it !=
nodes.end());
461 for (
auto &idAndNode :
nodes)
462 if (idAndNode.second.op == forOp)
463 return &idAndNode.second;
479 for (
auto &inEdge : oldInEdges) {
486 for (
auto &outEdge : oldOutEdges) {
500 for (
auto *storeOpInst : node->
stores) {
501 auto memref = cast<AffineWriteOpInterface>(storeOpInst).getMemRef();
502 auto *op =
memref.getDefiningOp();
508 for (
auto *user :
memref.getUsers())
509 if (!isa<AffineMapAccessInterface>(*user))
523 bool hasOutEdge = llvm::any_of(
outEdges.lookup(srcId), [=](
const Edge &edge) {
524 return edge.id == dstId && (!value || edge.value == value);
526 bool hasInEdge = llvm::any_of(
inEdges.lookup(dstId), [=](
const Edge &edge) {
527 return edge.id == srcId && (!value || edge.value == value);
529 return hasOutEdge && hasInEdge;
535 if (!
hasEdge(srcId, dstId, value)) {
536 outEdges[srcId].push_back({dstId, value});
537 inEdges[dstId].push_back({srcId, value});
538 if (isa<MemRefType>(value.
getType()))
546 assert(
inEdges.count(dstId) > 0);
548 if (isa<MemRefType>(value.
getType())) {
553 for (
auto *it =
inEdges[dstId].begin(); it !=
inEdges[dstId].end(); ++it) {
554 if ((*it).id == srcId && (*it).value == value) {
560 for (
auto *it =
outEdges[srcId].begin(); it !=
outEdges[srcId].end(); ++it) {
561 if ((*it).id == dstId && (*it).value == value) {
572 unsigned dstId)
const {
575 worklist.push_back({srcId, 0});
578 while (!worklist.empty()) {
579 auto &idAndIndex = worklist.back();
581 if (idAndIndex.first == dstId)
585 if (!
outEdges.contains(idAndIndex.first) ||
586 idAndIndex.second ==
outEdges.lookup(idAndIndex.first).size()) {
591 const Edge edge =
outEdges.lookup(idAndIndex.first)[idAndIndex.second];
598 if (!afterDst && edge.
id != idAndIndex.first)
599 worklist.push_back({edge.
id, 0});
608 unsigned inEdgeCount = 0;
610 if (inEdge.value ==
memref) {
624 unsigned outEdgeCount = 0;
625 for (
const auto &outEdge :
outEdges.lookup(
id))
638 if (!isa<MemRefType>(edge.value.getType()))
639 definingNodes.insert(edge.id);
647 unsigned dstId)
const {
654 if (llvm::any_of(definingNodes,
656 LDBG() <<
"Can't fuse: a defining op with a user in the dst "
657 <<
"loop has dependence from the src loop";
663 for (
auto &outEdge :
outEdges.lookup(srcId))
664 if (outEdge.id != dstId)
665 srcDepInsts.insert(
getNode(outEdge.id)->
op);
669 for (
auto &inEdge :
inEdges.lookup(dstId))
670 if (inEdge.id != srcId)
671 dstDepInsts.insert(
getNode(inEdge.id)->
op);
687 std::optional<unsigned> firstSrcDepPos;
688 std::optional<unsigned> lastDstDepPos;
693 if (srcDepInsts.count(op) > 0 && firstSrcDepPos == std::nullopt)
694 firstSrcDepPos = pos;
695 if (dstDepInsts.count(op) > 0)
697 depInsts.push_back(op);
701 if (firstSrcDepPos.has_value()) {
702 if (lastDstDepPos.has_value()) {
703 if (*firstSrcDepPos <= *lastDstDepPos) {
709 return depInsts[*firstSrcDepPos];
725 if (
inEdges.count(srcId) > 0) {
727 for (
auto &inEdge : oldInEdges) {
729 if (!privateMemRefs.contains(inEdge.value))
730 addEdge(inEdge.id, dstId, inEdge.value);
737 for (
auto &outEdge : oldOutEdges) {
739 if (outEdge.id == dstId)
741 else if (removeSrcId) {
742 addEdge(dstId, outEdge.id, outEdge.value);
750 if (
inEdges.count(dstId) > 0 && !privateMemRefs.empty()) {
752 for (
auto &inEdge : oldInEdges)
753 if (privateMemRefs.count(inEdge.value) > 0)
764 if (
inEdges.count(sibId) > 0) {
766 for (
auto &inEdge : oldInEdges) {
767 addEdge(inEdge.id, dstId, inEdge.value);
777 for (
auto &outEdge : oldOutEdges) {
778 addEdge(dstId, outEdge.id, outEdge.value);
791 llvm::append_range(node->
loads, loads);
792 llvm::append_range(node->
stores, stores);
793 llvm::append_range(node->
memrefLoads, memrefLoads);
795 llvm::append_range(node->
memrefFrees, memrefFrees);
807 unsigned id,
const std::function<
void(
Edge)> &callback) {
815 unsigned id,
const std::function<
void(
Edge)> &callback) {
824 for (
const auto &edge : edges) {
826 if (!isa<MemRefType>(edge.value.getType()))
828 assert(
nodes.count(edge.id) > 0);
835 os <<
"\nMemRefDependenceGraph\n";
837 for (
const auto &idAndNode :
nodes) {
838 os <<
"Node: " << idAndNode.first <<
"\n";
839 auto it =
inEdges.find(idAndNode.first);
841 for (
const auto &e : it->second)
842 os <<
" InEdge: " << e.id <<
" " << e.value <<
"\n";
844 it =
outEdges.find(idAndNode.first);
846 for (
const auto &e : it->second)
847 os <<
" OutEdge: " << e.id <<
" " << e.value <<
"\n";
855 AffineForOp currAffineForOp;
859 if (
auto currAffineForOp = dyn_cast<AffineForOp>(currOp))
860 loops->push_back(currAffineForOp);
861 currOp = currOp->getParentOp();
863 std::reverse(loops->begin(), loops->end());
874 if (isa<AffineIfOp, AffineForOp, AffineParallelOp>(currOp))
875 ops->push_back(currOp);
878 std::reverse(ops->begin(), ops->end());
885 assert(!
ivs.empty() &&
"Cannot have a slice without its IVs");
890 assert(loop &&
"Expected affine for");
902 unsigned numDims =
ivs.size();
913 for (
unsigned i = numDims, end = values.size(); i < end; ++i) {
914 Value value = values[i];
915 assert(cst->
containsVar(value) &&
"value expected to be present");
919 cst->
addBound(BoundType::EQ, value, cOp.value());
928 assert(succeeded(ret) &&
929 "should not fail as we never have semi-affine slice maps");
943 llvm::errs() <<
"\tIVs:\n";
945 llvm::errs() <<
"\t\t" << iv <<
"\n";
947 llvm::errs() <<
"\tLBs:\n";
948 for (
auto en : llvm::enumerate(
lbs)) {
949 llvm::errs() <<
"\t\t" << en.value() <<
"\n";
950 llvm::errs() <<
"\t\tOperands:\n";
952 llvm::errs() <<
"\t\t\t" << lbOp <<
"\n";
955 llvm::errs() <<
"\tUBs:\n";
956 for (
auto en : llvm::enumerate(
ubs)) {
957 llvm::errs() <<
"\t\t" << en.value() <<
"\n";
958 llvm::errs() <<
"\t\tOperands:\n";
960 llvm::errs() <<
"\t\t\t" << ubOp <<
"\n";
969std::optional<bool> ComputationSliceState::isSliceMaximalFastCheck()
const {
970 assert(
lbs.size() ==
ubs.size() && !
lbs.empty() && !
ivs.empty() &&
971 "Unexpected number of lbs, ubs and ivs in slice");
973 for (
unsigned i = 0, end =
lbs.size(); i < end; ++i) {
985 isa<AffineConstantExpr>(lbMap.
getResult(0)))
995 AffineForOp dstLoop =
999 AffineMap dstLbMap = dstLoop.getLowerBoundMap();
1000 AffineMap dstUbMap = dstLoop.getUpperBoundMap();
1004 assert(srcLoop &&
"Expected affine for");
1005 AffineMap srcLbMap = srcLoop.getLowerBoundMap();
1006 AffineMap srcUbMap = srcLoop.getUpperBoundMap();
1012 return std::nullopt;
1018 if (!isa<AffineConstantExpr>(srcLbResult) ||
1019 !isa<AffineConstantExpr>(srcUbResult) ||
1020 !isa<AffineConstantExpr>(dstLbResult) ||
1021 !isa<AffineConstantExpr>(dstUbResult))
1022 return std::nullopt;
1026 if (srcLbResult != dstLbResult || srcUbResult != dstUbResult ||
1027 srcLoop.getStep() != dstLoop.getStep())
1048 std::optional<bool> isValidFastCheck = isSliceMaximalFastCheck();
1049 if (isValidFastCheck && *isValidFastCheck)
1056 LDBG() <<
"Unable to compute source's domain";
1057 return std::nullopt;
1063 LDBG() <<
"Cannot handle locals in source domain";
1064 return std::nullopt;
1071 LDBG() <<
"Unable to compute slice's domain";
1072 return std::nullopt;
1082 LDBG() <<
"Domain of the source of the slice:\n"
1083 <<
"Source constraints:" << srcConstraints
1084 <<
"\nDomain of the slice if this fusion succeeds "
1085 <<
"(expressed in terms of its source's IVs):\n"
1086 <<
"Slice constraints:" << sliceConstraints;
1094 LDBG() <<
"Incorrect slice";
1106 std::optional<bool> isMaximalFastCheck = isSliceMaximalFastCheck();
1107 if (isMaximalFastCheck)
1108 return isMaximalFastCheck;
1116 assert(loop &&
"Expected affine for");
1118 return std::nullopt;
1126 consumerIVs.push_back(lbOp);
1130 for (
int i = consumerIVs.size(), end =
ivs.size(); i < end; ++i)
1131 consumerIVs.push_back(
Value());
1138 return std::nullopt;
1143 return std::nullopt;
1154 return cast<MemRefType>(
memref.getType()).getRank();
1159 auto memRefType = cast<MemRefType>(
memref.getType());
1161 unsigned rank = memRefType.getRank();
1163 shape->reserve(rank);
1165 assert(rank ==
cst.getNumDimVars() &&
"inconsistent memref region");
1173 for (
unsigned r = 0; r < rank; r++) {
1174 cstWithShapeBounds.
addBound(BoundType::LB, r, 0);
1175 int64_t dimSize = memRefType.getDimSize(r);
1176 if (ShapedType::isDynamic(dimSize))
1178 cstWithShapeBounds.
addBound(BoundType::UB, r, dimSize - 1);
1185 for (
unsigned d = 0; d < rank; d++) {
1187 std::optional<int64_t> diff =
1189 if (diff.has_value()) {
1190 diffConstant = *diff;
1191 assert(diffConstant >= 0 &&
"dim size bound cannot be negative");
1195 auto dimSize = memRefType.getDimSize(d);
1196 if (ShapedType::isDynamic(dimSize))
1197 return std::nullopt;
1198 diffConstant = dimSize;
1203 numElements *= diffConstant;
1208 shape->push_back(diffConstant);
1215 assert(pos <
cst.getNumDimVars() &&
"invalid position");
1216 auto memRefType = cast<MemRefType>(
memref.getType());
1217 unsigned rank = memRefType.getRank();
1219 assert(rank ==
cst.getNumDimVars() &&
"inconsistent memref region");
1221 auto boundPairs =
cst.getLowerAndUpperBound(
1222 pos, 0, rank,
cst.getNumDimAndSymbolVars(),
1223 {}, memRefType.getContext());
1224 lbMap = boundPairs.first;
1225 ubMap = boundPairs.second;
1226 assert(lbMap &&
"lower bound for a region must exist");
1227 assert(ubMap &&
"upper bound for a region must exist");
1256 bool addMemRefDimBounds,
bool dropLocalVars,
1257 bool dropOuterIvs) {
1258 assert((isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) &&
1259 "affine read/write op expected");
1265 unsigned rank = access.
getRank();
1267 LDBG() <<
"MemRefRegion::compute: " << *op <<
" depth: " << loopDepth;
1273 assert(loopDepth <= ivs.size() &&
"invalid 'loopDepth'");
1275 ivs.resize(loopDepth);
1291 operands.resize(numOperands);
1292 for (
unsigned i = 0; i < numOperands; ++i)
1295 if (sliceState !=
nullptr) {
1296 operands.reserve(operands.size() + sliceState->
lbOperands[0].size());
1298 for (
auto extraOperand : sliceState->
lbOperands[0]) {
1299 if (!llvm::is_contained(operands, extraOperand)) {
1300 operands.push_back(extraOperand);
1312 for (
unsigned i = 0; i < numDims + numSymbols; ++i) {
1313 auto operand = operands[i];
1319 if (failed(
cst.addAffineForOpDomain(affineFor)))
1322 if (failed(
cst.addAffineParallelOpDomain(parallelOp)))
1326 Value symbol = operand;
1328 cst.addBound(BoundType::EQ, symbol, constVal.value());
1330 LDBG() <<
"unknown affine dimensional value";
1336 if (sliceState !=
nullptr) {
1338 for (
auto operand : sliceState->
lbOperands[0]) {
1339 if (failed(
cst.addInductionVarOrTerminalSymbol(operand)))
1344 cst.addSliceBounds(sliceState->
ivs, sliceState->
lbs, sliceState->
ubs,
1346 assert(succeeded(ret) &&
1347 "should not fail as we never have semi-affine slice maps");
1352 if (failed(
cst.composeMap(&accessValueMap))) {
1353 op->
emitError(
"getMemRefRegion: compose affine map failed");
1354 LDBG() <<
"Access map: " << accessValueMap.
getAffineMap();
1361 cst.setDimSymbolSeparation(
cst.getNumDimAndSymbolVars() - rank);
1367 assert(loopDepth <= enclosingIVs.size() &&
"invalid loop depth");
1368 enclosingIVs.resize(loopDepth);
1370 cst.getValues(
cst.getNumDimVars(),
cst.getNumDimAndSymbolVars(), &vars);
1371 for (
auto en : llvm::enumerate(vars)) {
1373 !llvm::is_contained(enclosingIVs, en.value())) {
1375 cst.projectOut(en.value());
1377 unsigned varPosition;
1378 cst.findVar(en.value(), &varPosition);
1379 auto varKind =
cst.getVarKindAt(varPosition);
1380 varPosition -=
cst.getNumDimVars();
1381 cst.convertToLocal(varKind, varPosition, varPosition + 1);
1389 cst.projectOut(
cst.getNumDimAndSymbolVars(),
cst.getNumLocalVars());
1392 cst.constantFoldVarRange(
cst.getNumDimVars(),
1393 cst.getNumSymbolVars());
1395 assert(
cst.getNumDimVars() == rank &&
"unexpected MemRefRegion format");
1400 if (addMemRefDimBounds) {
1401 auto memRefType = cast<MemRefType>(
memref.getType());
1402 for (
unsigned r = 0; r < rank; r++) {
1403 cst.addBound(BoundType::LB, r, 0);
1404 if (memRefType.isDynamicDim(r))
1406 cst.addBound(BoundType::UB, r, memRefType.getDimSize(r) - 1);
1409 cst.removeTrivialRedundancy();
1411 LDBG() <<
"Memory region: " <<
cst;
1415std::optional<int64_t>
1417 auto elementType = memRefType.getElementType();
1419 unsigned sizeInBits;
1420 if (elementType.isIntOrFloat()) {
1421 sizeInBits = elementType.getIntOrFloatBitWidth();
1422 }
else if (
auto vectorType = dyn_cast<VectorType>(elementType)) {
1423 if (vectorType.getElementType().isIntOrFloat())
1425 vectorType.getElementTypeBitWidth() * vectorType.getNumElements();
1427 return std::nullopt;
1429 return std::nullopt;
1431 return llvm::divideCeil(sizeInBits, 8);
1436 auto memRefType = cast<MemRefType>(
memref.getType());
1438 if (!memRefType.getLayout().isIdentity()) {
1439 LDBG() <<
"Non-identity layout map not yet supported";
1446 LDBG() <<
"Dynamic shapes not yet supported";
1447 return std::nullopt;
1451 return std::nullopt;
1452 return *eltSize * *numElements;
1459std::optional<uint64_t>
1461 if (!memRefType.hasStaticShape())
1462 return std::nullopt;
1463 auto elementType = memRefType.getElementType();
1464 if (!elementType.isIntOrFloat() && !isa<VectorType>(elementType))
1465 return std::nullopt;
1469 return std::nullopt;
1470 for (
unsigned i = 0, e = memRefType.getRank(); i < e; i++) {
1471 sizeInBytes = *sizeInBytes * memRefType.getDimSize(i);
1476template <
typename LoadOrStoreOp>
1479 static_assert(llvm::is_one_of<LoadOrStoreOp, AffineReadOpInterface,
1480 AffineWriteOpInterface>::value,
1481 "argument should be either a AffineReadOpInterface or a "
1482 "AffineWriteOpInterface");
1484 Operation *op = loadOrStoreOp.getOperation();
1486 if (failed(region.compute(op, 0,
nullptr,
1490 LDBG() <<
"Memory region: " << region.getConstraints();
1492 bool outOfBounds =
false;
1493 unsigned rank = loadOrStoreOp.getMemRefType().getRank();
1496 for (
unsigned r = 0; r < rank; r++) {
1503 int64_t dimSize = loadOrStoreOp.getMemRefType().getDimSize(r);
1509 ucst.addBound(BoundType::LB, r, dimSize);
1510 outOfBounds = !ucst.isEmpty();
1512 loadOrStoreOp.emitOpError()
1513 <<
"memref out of upper bound access along dimension #" << (r + 1);
1518 llvm::fill(ineq, 0);
1520 lcst.addBound(BoundType::UB, r, -1);
1521 outOfBounds = !lcst.isEmpty();
1523 loadOrStoreOp.emitOpError()
1524 <<
"memref out of lower bound access along dimension #" << (r + 1);
1527 return failure(outOfBounds);
1531template LogicalResult
1534template LogicalResult
1543 while (block != limitBlock) {
1546 int instPosInBlock = std::distance(block->
begin(), op->getIterator());
1547 positions->push_back(instPosInBlock);
1551 std::reverse(positions->begin(), positions->end());
1558 unsigned level,
Block *block) {
1560 for (
auto &op : *block) {
1561 if (i != positions[level]) {
1565 if (level == positions.size() - 1)
1567 if (
auto childAffineForOp = dyn_cast<AffineForOp>(op))
1569 childAffineForOp.getBody());
1572 for (
auto &
b : region)
1584 for (
unsigned i = 0, e = cst->
getNumDimVars(); i < e; ++i) {
1586 if (ivs.count(value) == 0) {
1600 unsigned numOps = ops.size();
1601 assert(numOps > 0 &&
"Expected at least one operation");
1603 std::vector<SmallVector<AffineForOp, 4>> loops(numOps);
1604 unsigned loopDepthLimit = std::numeric_limits<unsigned>::max();
1605 for (
unsigned i = 0; i < numOps; ++i) {
1608 std::min(loopDepthLimit,
static_cast<unsigned>(loops[i].size()));
1611 unsigned loopDepth = 0;
1612 for (
unsigned d = 0; d < loopDepthLimit; ++d) {
1614 for (i = 1; i < numOps; ++i) {
1615 if (loops[i - 1][d] != loops[i][d])
1618 if (surroundingLoops)
1619 surroundingLoops->push_back(loops[i - 1][d]);
1632 unsigned numCommonLoops,
bool isBackwardSlice,
1638 std::vector<std::pair<Operation *, Operation *>> dependentOpPairs;
1650 LDBG() <<
"Invalid loop depth";
1654 bool readReadAccesses = isa<AffineReadOpInterface>(srcAccess.
opInst) &&
1655 isa<AffineReadOpInterface>(dstAccess.
opInst);
1659 srcAccess, dstAccess, numCommonLoops + 1,
1660 &dependenceConstraints,
nullptr,
1663 LDBG() <<
"Dependence check failed";
1668 dependentOpPairs.emplace_back(a,
b);
1673 isBackwardSlice, &tmpSliceState);
1678 LDBG() <<
"Unable to compute slice bound constraints";
1688 LDBG() <<
"Unable to compute slice bound constraints";
1698 for (
unsigned k = 0, l = sliceUnionCst.
getNumDimVars(); k < l; ++k)
1699 sliceUnionIVs.insert(sliceUnionCst.
getValue(k));
1701 for (
unsigned k = 0, l = tmpSliceCst.
getNumDimVars(); k < l; ++k)
1702 tmpSliceIVs.insert(tmpSliceCst.
getValue(k));
1720 LDBG() <<
"Unable to compute union bounding box of slice bounds";
1728 LDBG() <<
"empty slice union - unexpected";
1734 for (
auto &dep : dependentOpPairs) {
1735 ops.push_back(isBackwardSlice ? dep.second : dep.first);
1738 unsigned innermostCommonLoopDepth =
1740 if (loopDepth > innermostCommonLoopDepth) {
1741 LDBG() <<
"Exceeds max loop depth";
1761 sliceUnionCst.
getValues(numSliceLoopIVs,
1763 &sliceBoundOperands);
1766 sliceUnion->
ivs.clear();
1767 sliceUnionCst.
getValues(0, numSliceLoopIVs, &sliceUnion->
ivs);
1773 ? surroundingLoops[loopDepth - 1].getBody()->begin()
1774 : std::prev(surroundingLoops[loopDepth - 1].getBody()->end());
1778 sliceUnion->
lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1779 sliceUnion->
ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1783 std::optional<bool> isSliceValid = sliceUnion->
isSliceValid();
1784 if (!isSliceValid) {
1785 LDBG() <<
"Cannot determine if the slice is valid";
1797 assert(lbMap.
getNumResults() == 1 &&
"expected single result bound map");
1798 assert(ubMap.
getNumResults() == 1 &&
"expected single result bound map");
1805 auto cExpr = dyn_cast<AffineConstantExpr>(loopSpanExpr);
1807 return std::nullopt;
1808 return cExpr.getValue();
1817 llvm::SmallDenseMap<Operation *, uint64_t, 8> *tripCountMap) {
1818 unsigned numSrcLoopIVs = slice.
ivs.size();
1820 for (
unsigned i = 0; i < numSrcLoopIVs; ++i) {
1822 auto *op = forOp.getOperation();
1831 if (forOp.hasConstantLowerBound() && forOp.hasConstantUpperBound()) {
1832 (*tripCountMap)[op] =
1833 forOp.getConstantUpperBound() - forOp.getConstantLowerBound();
1837 if (maybeConstTripCount.has_value()) {
1838 (*tripCountMap)[op] = *maybeConstTripCount;
1845 if (!tripCount.has_value())
1847 (*tripCountMap)[op] = *tripCount;
1854 const llvm::SmallDenseMap<Operation *, uint64_t, 8> &sliceTripCountMap) {
1855 uint64_t iterCount = 1;
1856 for (
const auto &count : sliceTripCountMap) {
1857 iterCount *= count.second;
1874 unsigned numSrcLoopIVs = srcLoopIVs.size();
1879 unsigned numDstLoopIVs = dstLoopIVs.size();
1881 assert((!isBackwardSlice && loopDepth <= numSrcLoopIVs) ||
1882 (isBackwardSlice && loopDepth <= numDstLoopIVs));
1885 unsigned pos = isBackwardSlice ? numSrcLoopIVs + loopDepth : loopDepth;
1887 isBackwardSlice ? numDstLoopIVs - loopDepth : numSrcLoopIVs - loopDepth;
1892 unsigned offset = isBackwardSlice ? 0 : loopDepth;
1893 unsigned numSliceLoopIVs = isBackwardSlice ? numSrcLoopIVs : numDstLoopIVs;
1894 sliceCst.
getValues(offset, offset + numSliceLoopIVs, &sliceState->
ivs);
1902 &sliceState->
lbs, &sliceState->
ubs);
1907 for (
unsigned i = 0; i < numDimsAndSymbols; ++i) {
1908 if (i < offset || i >= offset + numSliceLoopIVs)
1909 sliceBoundOperands.push_back(sliceCst.
getValue(i));
1914 sliceState->
lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1915 sliceState->
ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1919 isBackwardSlice ? dstLoopIVs[loopDepth - 1].getBody()->begin()
1920 : std::prev(srcLoopIVs[loopDepth - 1].getBody()->end());
1922 llvm::SmallDenseSet<Value, 8> sequentialLoops;
1923 if (isa<AffineReadOpInterface>(depSourceOp) &&
1924 isa<AffineReadOpInterface>(depSinkOp)) {
1930 auto getSliceLoop = [&](
unsigned i) {
1931 return isBackwardSlice ? srcLoopIVs[i] : dstLoopIVs[i];
1933 auto isInnermostInsertion = [&]() {
1934 return (isBackwardSlice ? loopDepth >= srcLoopIVs.size()
1935 : loopDepth >= dstLoopIVs.size());
1937 llvm::SmallDenseMap<Operation *, uint64_t, 8> sliceTripCountMap;
1938 auto srcIsUnitSlice = [&]() {
1945 for (
unsigned i = 0; i < numSliceLoopIVs; ++i) {
1946 Value iv = getSliceLoop(i).getInductionVar();
1947 if (sequentialLoops.count(iv) == 0 &&
1955 std::optional<bool> isMaximal = sliceState->
isMaximal();
1957 isInnermostInsertion() && srcIsUnitSlice() && isMaximal && *isMaximal)
1959 for (
unsigned j = i;
j < numSliceLoopIVs; ++
j) {
1985 unsigned numSrcLoopIVs = srcLoopIVs.size();
1990 unsigned dstLoopIVsSize = dstLoopIVs.size();
1991 if (dstLoopDepth > dstLoopIVsSize) {
1992 dstOpInst->
emitError(
"invalid destination loop depth");
1993 return AffineForOp();
2003 auto dstAffineForOp = dstLoopIVs[dstLoopDepth - 1];
2004 OpBuilder b(dstAffineForOp.getBody(), dstAffineForOp.getBody()->begin());
2005 auto sliceLoopNest =
2006 cast<AffineForOp>(
b.clone(*srcLoopIVs[0].getOperation()));
2015 unsigned sliceSurroundingLoopsSize = sliceSurroundingLoops.size();
2016 (
void)sliceSurroundingLoopsSize;
2017 assert(dstLoopDepth + numSrcLoopIVs >= sliceSurroundingLoopsSize);
2018 unsigned sliceLoopLimit = dstLoopDepth + numSrcLoopIVs;
2019 (
void)sliceLoopLimit;
2020 assert(sliceLoopLimit >= sliceSurroundingLoopsSize);
2023 for (
unsigned i = 0; i < numSrcLoopIVs; ++i) {
2024 auto forOp = sliceSurroundingLoops[dstLoopDepth + i];
2026 forOp.setLowerBound(sliceState->
lbOperands[i], lbMap);
2028 forOp.setUpperBound(sliceState->
ubOperands[i], ubMap);
2030 return sliceLoopNest;
2036 if (
auto loadOp = dyn_cast<AffineReadOpInterface>(memOp)) {
2037 memref = loadOp.getMemRef();
2039 llvm::append_range(
indices, loadOp.getMapOperands());
2041 assert(isa<AffineWriteOpInterface>(memOp) &&
2042 "Affine read/write op expected");
2043 auto storeOp = cast<AffineWriteOpInterface>(memOp);
2045 memref = storeOp.getMemRef();
2046 llvm::append_range(
indices, storeOp.getMapOperands());
2051 return cast<MemRefType>(
memref.getType()).getRank();
2055 return isa<AffineWriteOpInterface>(
opInst);
2064 if (isa<AffineForOp>(currOp))
2066 if (
auto parOp = dyn_cast<AffineParallelOp>(currOp))
2067 depth += parOp.getNumDims();
2084 rhs.getAccessMap(&rhsMap);
2085 return thisMap == rhsMap;
2090 AffineForOp currAffineForOp;
2094 if (AffineForOp currAffineForOp = dyn_cast<AffineForOp>(currOp))
2095 ivs.push_back(currAffineForOp.getInductionVar());
2096 else if (
auto parOp = dyn_cast<AffineParallelOp>(currOp))
2097 llvm::append_range(ivs, parOp.getIVs());
2098 currOp = currOp->getParentOp();
2100 std::reverse(ivs.begin(), ivs.end());
2111 unsigned minNumLoops = std::min(loopsA.size(), loopsB.size());
2112 unsigned numCommonLoops = 0;
2113 for (
unsigned i = 0; i < minNumLoops; ++i) {
2114 if (loopsA[i] != loopsB[i])
2118 return numCommonLoops;
2129 if (!isa<AffineReadOpInterface, AffineWriteOpInterface>(opInst)) {
2135 auto region = std::make_unique<MemRefRegion>(opInst->
getLoc());
2137 region->compute(opInst,
2139 LDBG() <<
"Error obtaining memory region";
2140 opInst->
emitError(
"error obtaining memory region");
2144 auto [it,
inserted] = regions.try_emplace(region->memref);
2146 it->second = std::move(region);
2147 }
else if (failed(it->second->unionBoundingBox(*region))) {
2148 LDBG() <<
"getMemoryFootprintBytes: unable to perform a union on a "
2151 "getMemoryFootprintBytes: unable to perform a union on a memory "
2157 if (
result.wasInterrupted())
2158 return std::nullopt;
2161 for (
const auto ®ion : regions) {
2162 std::optional<int64_t> size = region.second->getRegionSize();
2163 if (!size.has_value())
2164 return std::nullopt;
2165 totalSizeInBytes += *size;
2167 return totalSizeInBytes;
2172 auto *forInst = forOp.getOperation();
2173 return ::getMemoryFootprintBytes(
2181 if (!isLoopParallel(forOp, &reductions))
2183 return !reductions.empty();
2189 AffineForOp forOp, llvm::SmallDenseSet<Value, 8> *sequentialLoops) {
2191 if (
auto innerFor = dyn_cast<AffineForOp>(op))
2192 if (!isLoopParallel(innerFor))
2193 sequentialLoops->insert(innerFor.getInductionVar());
2205 assert(simplifiedSet &&
"guaranteed to succeed while roundtripping");
2206 return simplifiedSet;
2211 target = llvm::map_to_vector<4>(source, [](std::optional<Value> val) {
2212 return val.has_value() ? *val :
Value();
2231 for (
unsigned i = syms.size(); i < newSyms.size(); ++i)
2233 return constraints.
addBound(type, pos, alignedMap);
2240 newResults.push_back(r + val);
2284 bool isMin = isa<AffineMinOp>(op);
2285 assert((isMin || isa<AffineMaxOp>(op)) &&
"expect AffineMin/MaxOp");
2289 isMin ? cast<AffineMinOp>(op).getMap() : cast<AffineMaxOp>(op).getMap();
2296 unsigned resultDimStart = constraints.
appendDimVar(numResults);
2300 auto boundType = isMin ? BoundType::UB : BoundType::LB;
2311 AffineMap sliceBound = isMin ? opUb[0] : opLb[0];
2322 if (failed(constraints.
addBound(BoundType::EQ, dimOpBound, alignedBoundMap)))
2340 for (
unsigned i = resultDimStart; i < resultDimStart + numResults; ++i) {
2348 map.
getSubMap({i - resultDimStart}), operands)))
2356 ineq[dimOpBound] = isMin ? 1 : -1;
2357 ineq[i] = isMin ? -1 : 1;
2391 if (aScope != bScope)
2396 auto getBlockAncestry = [&](
Operation *op,
2400 ancestry.push_back(curOp->
getBlock());
2405 assert(curOp &&
"can't reach root op without passing through affine scope");
2406 std::reverse(ancestry.begin(), ancestry.end());
2410 getBlockAncestry(a, aAncestors);
2411 getBlockAncestry(
b, bAncestors);
2412 assert(!aAncestors.empty() && !bAncestors.empty() &&
2413 "at least one Block ancestor expected");
2415 Block *innermostCommonBlock =
nullptr;
2416 for (
unsigned a = 0,
b = 0, e = aAncestors.size(), f = bAncestors.size();
2417 a < e &&
b < f; ++a, ++
b) {
2418 if (aAncestors[a] != bAncestors[
b])
2420 innermostCommonBlock = aAncestors[a];
2422 return innermostCommonBlock;
static Value getMemRef(Operation *memOp)
Returns the memref being read/written by a memref/affine load/store op.
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...
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 *)
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
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
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...
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...
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
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
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.
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.
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.
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
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.