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) {
1607 loopDepthLimit = std::min(loopDepthLimit, (
unsigned)loops[i].size());
1610 unsigned loopDepth = 0;
1611 for (
unsigned d = 0; d < loopDepthLimit; ++d) {
1613 for (i = 1; i < numOps; ++i) {
1614 if (loops[i - 1][d] != loops[i][d])
1617 if (surroundingLoops)
1618 surroundingLoops->push_back(loops[i - 1][d]);
1631 unsigned numCommonLoops,
bool isBackwardSlice,
1637 std::vector<std::pair<Operation *, Operation *>> dependentOpPairs;
1649 LDBG() <<
"Invalid loop depth";
1653 bool readReadAccesses = isa<AffineReadOpInterface>(srcAccess.
opInst) &&
1654 isa<AffineReadOpInterface>(dstAccess.
opInst);
1658 srcAccess, dstAccess, numCommonLoops + 1,
1659 &dependenceConstraints,
nullptr,
1662 LDBG() <<
"Dependence check failed";
1667 dependentOpPairs.emplace_back(a,
b);
1672 isBackwardSlice, &tmpSliceState);
1677 LDBG() <<
"Unable to compute slice bound constraints";
1687 LDBG() <<
"Unable to compute slice bound constraints";
1697 for (
unsigned k = 0, l = sliceUnionCst.
getNumDimVars(); k < l; ++k)
1698 sliceUnionIVs.insert(sliceUnionCst.
getValue(k));
1700 for (
unsigned k = 0, l = tmpSliceCst.
getNumDimVars(); k < l; ++k)
1701 tmpSliceIVs.insert(tmpSliceCst.
getValue(k));
1719 LDBG() <<
"Unable to compute union bounding box of slice bounds";
1727 LDBG() <<
"empty slice union - unexpected";
1733 for (
auto &dep : dependentOpPairs) {
1734 ops.push_back(isBackwardSlice ? dep.second : dep.first);
1737 unsigned innermostCommonLoopDepth =
1739 if (loopDepth > innermostCommonLoopDepth) {
1740 LDBG() <<
"Exceeds max loop depth";
1756 &sliceUnion->
ubs,
false,
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";
1805 assert(lbMap.
getNumResults() == 1 &&
"expected single result lower bound");
1806 assert(ubMap.
getNumResults() >= 1 &&
"expected at least one upper bound");
1810 std::optional<uint64_t> tripCount;
1814 auto cExpr = dyn_cast<AffineConstantExpr>(loopSpanExpr);
1817 if (cExpr.getValue() < 0)
1820 std::min(tripCount.value_or(std::numeric_limits<uint64_t>::max()),
1821 (uint64_t)cExpr.getValue());
1832 llvm::SmallDenseMap<Operation *, uint64_t, 8> *tripCountMap) {
1833 unsigned numSrcLoopIVs = slice.
ivs.size();
1835 for (
unsigned i = 0; i < numSrcLoopIVs; ++i) {
1837 auto *op = forOp.getOperation();
1846 if (forOp.hasConstantLowerBound() && forOp.hasConstantUpperBound()) {
1847 (*tripCountMap)[op] =
1848 forOp.getConstantUpperBound() - forOp.getConstantLowerBound();
1851 std::optional<APInt> maybeConstTripCount = forOp.getStaticTripCount();
1852 if (maybeConstTripCount.has_value()) {
1853 (*tripCountMap)[op] = maybeConstTripCount->getZExtValue();
1860 if (!tripCount.has_value())
1862 (*tripCountMap)[op] = *tripCount;
1869 const llvm::SmallDenseMap<Operation *, uint64_t, 8> &sliceTripCountMap) {
1870 uint64_t iterCount = 1;
1871 for (
const auto &count : sliceTripCountMap) {
1872 iterCount *= count.second;
1889 unsigned numSrcLoopIVs = srcLoopIVs.size();
1894 unsigned numDstLoopIVs = dstLoopIVs.size();
1896 assert((!isBackwardSlice && loopDepth <= numSrcLoopIVs) ||
1897 (isBackwardSlice && loopDepth <= numDstLoopIVs));
1900 unsigned pos = isBackwardSlice ? numSrcLoopIVs + loopDepth : loopDepth;
1902 isBackwardSlice ? numDstLoopIVs - loopDepth : numSrcLoopIVs - loopDepth;
1907 unsigned offset = isBackwardSlice ? 0 : loopDepth;
1908 unsigned numSliceLoopIVs = isBackwardSlice ? numSrcLoopIVs : numDstLoopIVs;
1909 sliceCst.
getValues(offset, offset + numSliceLoopIVs, &sliceState->
ivs);
1917 &sliceState->
lbs, &sliceState->
ubs,
1923 for (
unsigned i = 0; i < numDimsAndSymbols; ++i) {
1924 if (i < offset || i >= offset + numSliceLoopIVs)
1925 sliceBoundOperands.push_back(sliceCst.
getValue(i));
1930 sliceState->
lbOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1931 sliceState->
ubOperands.resize(numSliceLoopIVs, sliceBoundOperands);
1935 isBackwardSlice ? dstLoopIVs[loopDepth - 1].getBody()->begin()
1936 : std::prev(srcLoopIVs[loopDepth - 1].getBody()->end());
1938 llvm::SmallDenseSet<Value, 8> sequentialLoops;
1939 if (isa<AffineReadOpInterface>(depSourceOp) &&
1940 isa<AffineReadOpInterface>(depSinkOp)) {
1946 auto getSliceLoop = [&](
unsigned i) {
1947 return isBackwardSlice ? srcLoopIVs[i] : dstLoopIVs[i];
1949 auto isInnermostInsertion = [&]() {
1950 return (isBackwardSlice ? loopDepth >= srcLoopIVs.size()
1951 : loopDepth >= dstLoopIVs.size());
1953 llvm::SmallDenseMap<Operation *, uint64_t, 8> sliceTripCountMap;
1954 auto srcIsUnitSlice = [&]() {
1961 for (
unsigned i = 0; i < numSliceLoopIVs; ++i) {
1962 Value iv = getSliceLoop(i).getInductionVar();
1963 if (sequentialLoops.count(iv) == 0 &&
1972 std::optional<bool> isMaximal = sliceState->
isMaximal();
1974 isInnermostInsertion() && srcIsUnitSlice() && isMaximal && *isMaximal)
1976 for (
unsigned j = i;
j < numSliceLoopIVs; ++
j) {
2002 unsigned numSrcLoopIVs = srcLoopIVs.size();
2007 unsigned dstLoopIVsSize = dstLoopIVs.size();
2008 if (dstLoopDepth > dstLoopIVsSize) {
2009 dstOpInst->
emitError(
"invalid destination loop depth");
2010 return AffineForOp();
2020 auto dstAffineForOp = dstLoopIVs[dstLoopDepth - 1];
2021 OpBuilder b(dstAffineForOp.getBody(), dstAffineForOp.getBody()->begin());
2022 auto sliceLoopNest =
2023 cast<AffineForOp>(
b.clone(*srcLoopIVs[0].getOperation()));
2032 unsigned sliceSurroundingLoopsSize = sliceSurroundingLoops.size();
2033 (
void)sliceSurroundingLoopsSize;
2034 assert(dstLoopDepth + numSrcLoopIVs >= sliceSurroundingLoopsSize);
2035 unsigned sliceLoopLimit = dstLoopDepth + numSrcLoopIVs;
2036 (
void)sliceLoopLimit;
2037 assert(sliceLoopLimit >= sliceSurroundingLoopsSize);
2040 for (
unsigned i = 0; i < numSrcLoopIVs; ++i) {
2041 auto forOp = sliceSurroundingLoops[dstLoopDepth + i];
2043 forOp.setLowerBound(sliceState->
lbOperands[i], lbMap);
2045 forOp.setUpperBound(sliceState->
ubOperands[i], ubMap);
2047 return sliceLoopNest;
2053 if (
auto loadOp = dyn_cast<AffineReadOpInterface>(memOp)) {
2054 memref = loadOp.getMemRef();
2056 llvm::append_range(
indices, loadOp.getMapOperands());
2058 assert(isa<AffineWriteOpInterface>(memOp) &&
2059 "Affine read/write op expected");
2060 auto storeOp = cast<AffineWriteOpInterface>(memOp);
2062 memref = storeOp.getMemRef();
2063 llvm::append_range(
indices, storeOp.getMapOperands());
2068 return cast<MemRefType>(
memref.getType()).getRank();
2072 return isa<AffineWriteOpInterface>(
opInst);
2081 if (isa<AffineForOp>(currOp))
2083 if (
auto parOp = dyn_cast<AffineParallelOp>(currOp))
2084 depth += parOp.getNumDims();
2096 if (
memref != rhs.memref)
2101 rhs.getAccessMap(&rhsMap);
2102 return thisMap == rhsMap;
2107 AffineForOp currAffineForOp;
2111 if (AffineForOp currAffineForOp = dyn_cast<AffineForOp>(currOp))
2112 ivs.push_back(currAffineForOp.getInductionVar());
2113 else if (
auto parOp = dyn_cast<AffineParallelOp>(currOp))
2114 llvm::append_range(ivs, parOp.getIVs());
2115 currOp = currOp->getParentOp();
2117 std::reverse(ivs.begin(), ivs.end());
2128 unsigned minNumLoops = std::min(loopsA.size(), loopsB.size());
2129 unsigned numCommonLoops = 0;
2130 for (
unsigned i = 0; i < minNumLoops; ++i) {
2131 if (loopsA[i] != loopsB[i])
2135 return numCommonLoops;
2146 if (!isa<AffineReadOpInterface, AffineWriteOpInterface>(opInst)) {
2152 auto region = std::make_unique<MemRefRegion>(opInst->
getLoc());
2154 region->compute(opInst,
2156 LDBG() <<
"Error obtaining memory region";
2157 opInst->
emitError(
"error obtaining memory region");
2161 auto [it,
inserted] = regions.try_emplace(region->memref);
2163 it->second = std::move(region);
2164 }
else if (failed(it->second->unionBoundingBox(*region))) {
2165 LDBG() <<
"getMemoryFootprintBytes: unable to perform a union on a "
2168 "getMemoryFootprintBytes: unable to perform a union on a memory "
2174 if (
result.wasInterrupted())
2175 return std::nullopt;
2178 for (
const auto ®ion : regions) {
2179 std::optional<int64_t> size = region.second->getRegionSize();
2180 if (!size.has_value())
2181 return std::nullopt;
2182 totalSizeInBytes += *size;
2184 return totalSizeInBytes;
2189 auto *forInst = forOp.getOperation();
2190 return ::getMemoryFootprintBytes(
2198 if (!isLoopParallel(forOp, &reductions))
2200 return !reductions.empty();
2206 AffineForOp forOp, llvm::SmallDenseSet<Value, 8> *sequentialLoops) {
2208 if (
auto innerFor = dyn_cast<AffineForOp>(op))
2209 if (!isLoopParallel(innerFor))
2210 sequentialLoops->insert(innerFor.getInductionVar());
2215 FailureOr<FlatAffineValueConstraints> fac =
2223 fac->removeTrivialRedundancy();
2225 auto simplifiedSet = fac->getAsIntegerSet(set.
getContext());
2226 assert(simplifiedSet &&
"guaranteed to succeed while roundtripping");
2227 return simplifiedSet;
2232 target = llvm::map_to_vector<4>(source, [](std::optional<Value> val) {
2233 return val.has_value() ? *val :
Value();
2252 for (
unsigned i = syms.size(); i < newSyms.size(); ++i)
2254 return constraints.
addBound(type, pos, alignedMap);
2261 newResults.push_back(r + val);
2305 bool isMin = isa<AffineMinOp>(op);
2306 assert((isMin || isa<AffineMaxOp>(op)) &&
"expect AffineMin/MaxOp");
2310 isMin ? cast<AffineMinOp>(op).getMap() : cast<AffineMaxOp>(op).getMap();
2317 unsigned resultDimStart = constraints.
appendDimVar(numResults);
2321 auto boundType = isMin ? BoundType::UB : BoundType::LB;
2332 AffineMap sliceBound = isMin ? opUb[0] : opLb[0];
2343 if (failed(constraints.
addBound(BoundType::EQ, dimOpBound, alignedBoundMap)))
2361 for (
unsigned i = resultDimStart; i < resultDimStart + numResults; ++i) {
2369 map.
getSubMap({i - resultDimStart}), operands)))
2377 ineq[dimOpBound] = isMin ? 1 : -1;
2378 ineq[i] = isMin ? -1 : 1;
2417 filteredOperands.reserve(newOperands.size());
2418 unsigned newDim = 0;
2419 for (
unsigned i = 0; i < numDims; ++i) {
2420 if (newOperands[i]) {
2422 filteredOperands.push_back(newOperands[i]);
2425 "null-valued dim operand referenced in bound map");
2429 unsigned newSym = 0;
2430 for (
unsigned i = 0; i < numSyms; ++i) {
2431 if (newOperands[numDims + i]) {
2433 filteredOperands.push_back(newOperands[numDims + i]);
2436 "null-valued symbol operand referenced in bound map");
2442 newOperands = std::move(filteredOperands);
2453 if (aScope != bScope)
2458 auto getBlockAncestry = [&](
Operation *op,
2462 ancestry.push_back(curOp->
getBlock());
2467 assert(curOp &&
"can't reach root op without passing through affine scope");
2468 std::reverse(ancestry.begin(), ancestry.end());
2472 getBlockAncestry(a, aAncestors);
2473 getBlockAncestry(
b, bAncestors);
2474 assert(!aAncestors.empty() && !bAncestors.empty() &&
2475 "at least one Block ancestor expected");
2477 Block *innermostCommonBlock =
nullptr;
2478 for (
unsigned a = 0,
b = 0, e = aAncestors.size(), f = bAncestors.size();
2479 a < e &&
b < f; ++a, ++
b) {
2480 if (aAncestors[a] != bAncestors[
b])
2482 innermostCommonBlock = aAncestors[a];
2484 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)
Returns the number of iterations the slice bounded below by lbMap and above by ubMap runs for,...
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
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
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.