36#include "llvm/ADT/STLExtras.h"
37#include "llvm/ADT/SetVector.h"
38#include "llvm/ADT/SmallBitVector.h"
39#include "llvm/ADT/TypeSwitch.h"
40#include "llvm/Support/InterleavedRange.h"
42#include "llvm/Support/DebugLog.h"
46#define DEBUG_TYPE "parallel-loop-fusion"
49#define GEN_PASS_DEF_SCFPARALLELLOOPFUSION
50#include "mlir/Dialect/SCF/Transforms/Passes.h.inc"
60 return walkResult.wasInterrupted();
65 ParallelOp secondPloop) {
66 if (firstPloop.getNumLoops() != secondPloop.getNumLoops())
76 return std::equal(lhs.begin(), lhs.end(), rhs.begin(),
78 if (lhsValue == rhsValue)
80 std::optional<int64_t> lhsConst =
81 getConstantIntValue(lhsValue);
82 std::optional<int64_t> rhsConst =
83 getConstantIntValue(rhsValue);
84 return lhsConst && rhsConst && *lhsConst == *rhsConst;
87 return matchOperands(firstPloop.getLowerBound(),
88 secondPloop.getLowerBound()) &&
89 matchOperands(firstPloop.getUpperBound(),
90 secondPloop.getUpperBound()) &&
91 matchOperands(firstPloop.getStep(), secondPloop.getStep());
102 if (!isa<memref::StoreOp, vector::TransferWriteOp, vector::StoreOp>(op1))
104 bool opsAreIdentical =
106 .Case([&](memref::StoreOp storeOp1) {
107 auto storeOp2 = cast<memref::StoreOp>(op2);
108 return (storeOp1.getMemRef() == storeOp2.getMemRef()) &&
109 (storeOp1.getIndices() == storeOp2.getIndices());
111 .Case([&](vector::TransferWriteOp writeOp1) {
112 auto writeOp2 = cast<vector::TransferWriteOp>(op2);
113 return (writeOp1.getBase() == writeOp2.getBase()) &&
114 (writeOp1.getIndices() == writeOp2.getIndices()) &&
115 (writeOp1.getMask() == writeOp2.getMask()) &&
116 (writeOp1.getValueToStore().
getType() ==
117 writeOp2.getValueToStore().getType()) &&
118 (writeOp1.getInBounds() == writeOp2.getInBounds());
120 .Case([&](vector::StoreOp vecStoreOp1) {
121 auto vecStoreOp2 = cast<vector::StoreOp>(op2);
122 return (vecStoreOp1.getBase() == vecStoreOp2.getBase()) &&
123 (vecStoreOp1.getIndices() == vecStoreOp2.getIndices()) &&
124 (vecStoreOp1.getValueToStore().
getType() ==
125 vecStoreOp2.getValueToStore().getType()) &&
126 (vecStoreOp1.getAlignment() == vecStoreOp2.getAlignment()) &&
127 (vecStoreOp1.getNontemporal() ==
128 vecStoreOp2.getNontemporal());
130 .Default([](
Operation *) {
return false; });
131 return opsAreIdentical;
144 if (!val1DefOp || !val2DefOp)
149 val1DefOp, val2DefOp,
164 return constOp.value();
167 return constOp.value();
174 return constOp.value();
177 return constOp.value();
181 if (
auto applyOp = expr.
getDefiningOp<affine::AffineApplyOp>()) {
189 auto bin = dyn_cast<AffineBinaryOpExpr>(
result);
192 auto lhsDim = dyn_cast<AffineDimExpr>(bin.getLHS());
193 auto rhsDim = dyn_cast<AffineDimExpr>(bin.getRHS());
194 auto lhsConst = dyn_cast<AffineConstantExpr>(bin.getLHS());
195 auto rhsConst = dyn_cast<AffineConstantExpr>(bin.getRHS());
196 if (lhsConst && rhsDim)
197 return lhsConst.getValue();
198 if (rhsConst && lhsDim)
199 return rhsConst.getValue();
235 auto getConstLoopBoundsForIV =
236 [](
Value index) -> std::optional<std::tuple<int64_t, int64_t, int64_t>> {
237 auto blockArg = dyn_cast<BlockArgument>(
index);
240 auto *parentOp = blockArg.getOwner()->getParentOp();
241 auto loopLike = dyn_cast<LoopLikeOpInterface>(parentOp);
248 auto ivs = loopLike.getLoopInductionVars();
251 auto it = llvm::find(*ivs, blockArg);
252 if (it == ivs->end())
254 unsigned pos = std::distance(ivs->begin(), it);
255 if (pos >= ranges.size())
257 auto [lb,
ub, step] = ranges[pos];
258 return std::make_tuple(lb,
ub, step);
262 std::optional<int64_t> writeConst =
264 if (!writeConst && writeIndex) {
266 if (
auto bounds = getConstLoopBoundsForIV(writeIndex)) {
267 auto [lb,
ub, step] = *bounds;
268 if (step > 0 &&
ub == lb + step)
276 if (rangeExtent <= 0 || step <= 0)
280 int64_t rangeEnd = rangeStart + rangeExtent;
281 return lb >= rangeStart &&
ub <= rangeEnd;
284 if (offsetConst && writeConst) {
286 int64_t start = *offsetConst + *writeConst;
288 return (*loadConst >= start && *loadConst < start + extent);
289 if (
auto bounds = getConstLoopBoundsForIV(loadIndex)) {
290 auto [lb,
ub, step] = *bounds;
291 return loopIVWithinRange(lb,
ub, step, start, extent);
297 if (offsetConst && *offsetConst == 0 &&
300 if (
auto addConst =
getAddConstant(loadIndex, writeIndex, loopsIVsMap)) {
304 return (*addConst >= start && *addConst < start + extent);
310 if (
auto offsetVal = dyn_cast<Value>(offset)) {
323 .Case([&](memref::LoadOp
load) {
return load.getMemRef(); })
324 .Case([&](memref::StoreOp store) {
return store.getMemRef(); })
325 .Case([&](vector::TransferReadOp read) {
return read.getBase(); })
326 .Case([&](vector::TransferWriteOp write) {
return write.getBase(); })
327 .Case([&](vector::LoadOp
load) {
return load.getBase(); })
328 .Case([&](vector::StoreOp store) {
return store.getBase(); })
351 Value base = writeBase;
355 llvm::SmallBitVector droppedDims;
356 bool hasSubview =
false;
357 auto *ctx = loadOp.getContext();
358 if (
auto subView = base.
getDefiningOp<memref::SubViewOp>()) {
359 if (!subView.hasUnitStride())
361 baseMemref = cast<MemrefValue>(subView.getSource());
362 offsets = llvm::to_vector(subView.getMixedOffsets());
363 droppedDims = subView.getDroppedDims();
366 baseMemref = dyn_cast<MemrefValue>(base);
371 auto loadIndices = loadOp.getIndices();
372 unsigned baseRank = baseMemref.getType().getRank();
373 if ((loadOp.getMemref() != baseMemref) || (loadIndices.size() != baseRank))
376 unsigned writeRank = writeIndices.size();
377 if ((!hasSubview && writeRank != baseRank) ||
378 (hasSubview && offsets.size() != baseRank) ||
379 (vectorDimForWriteDim.size() != writeRank))
382 auto zeroAttr = IntegerAttr::get(IndexType::get(ctx), 0);
383 unsigned writeMemrefDim = 0;
384 for (
unsigned baseDim : llvm::seq(baseRank)) {
385 bool wasDropped = (hasSubview && droppedDims.test(baseDim));
386 int64_t vectorDim = !wasDropped ? vectorDimForWriteDim[writeMemrefDim] : -1;
388 if (vectorDim >= 0) {
389 int64_t dimSize = vecTy.getDimSize(vectorDim);
390 if (dimSize == ShapedType::kDynamic)
394 Value writeIndex = !wasDropped ? writeIndices[writeMemrefDim] :
Value();
410 vector::TransferWriteOp writeOp,
412 auto vecTy = dyn_cast<VectorType>(writeOp.getVector().getType());
416 unsigned writeRank = writeOp.getIndices().size();
424 for (
unsigned vecDim = 0; vecDim < permutationMap.
getNumResults(); ++vecDim) {
425 auto dimExpr = dyn_cast<AffineDimExpr>(permutationMap.
getResult(vecDim));
428 unsigned writeDim = dimExpr.getPosition();
429 if (writeDim >= writeRank || vectorDimForWriteDim[writeDim] != -1)
431 vectorDimForWriteDim[writeDim] = vecDim;
435 vecTy, vectorDimForWriteDim, ivsMap);
440 vector::StoreOp storeOp,
442 auto vecTy = dyn_cast<VectorType>(storeOp.getValueToStore().getType());
446 unsigned writeRank = storeOp.getIndices().size();
447 if (vecTy.getRank() > writeRank)
451 unsigned vecRank = vecTy.getRank();
452 for (
unsigned i = 0; i < vecRank; ++i) {
453 unsigned writeDim = writeRank - vecRank + i;
454 vectorDimForWriteDim[writeDim] = i;
458 vecTy, vectorDimForWriteDim, ivsMap);
468template <
typename OpTy1,
typename OpTy2>
470 OpTy1 op1, OpTy2 op2,
const IRMapping &firstToSecondPloopIVsMap,
474 if (!base1 || !base2)
477 auto accessThroughTrivialSubviewIsSame =
478 [&
b](memref::SubViewOp subView,
ValueRange subViewAccess,
481 LogicalResult resolved = resolveSourceIndicesRankReducingSubview(
482 subView.getLoc(),
b, subView, subViewAccess, resolvedSubviewAccess);
483 if (failed(resolved) ||
484 (resolvedSubviewAccess.size() != sourceAccess.size()))
486 for (
auto [dimIdx, resolvedIndex] :
487 llvm::enumerate(resolvedSubviewAccess)) {
496 if (
auto subView = base1.template getDefiningOp<memref::SubViewOp>();
499 base2, cast<MemrefValue>(subView.getSource())) &&
500 accessThroughTrivialSubviewIsSame(subView, op1.getIndices(),
502 firstToSecondPloopIVsMap))
506 if (
auto subView = base2.template getDefiningOp<memref::SubViewOp>();
509 base1, cast<MemrefValue>(subView.getSource())) &&
510 accessThroughTrivialSubviewIsSame(subView, op2.getIndices(),
512 firstToSecondPloopIVsMap))
521template <
typename OpTy1,
typename OpTy2>
524 auto indices1 = op1.getIndices();
525 auto indices2 = op2.getIndices();
526 if (indices1.size() != indices2.size())
528 for (
auto [idx1, idx2] : llvm::zip(indices1, indices2)) {
539 const IRMapping &firstToSecondPloopIVsMap,
541 if (!loadOp || !storeOp)
544 if (!isa<memref::LoadOp, vector::TransferReadOp, vector::LoadOp>(loadOp))
546 bool accessSameMemory =
548 .Case([&](memref::LoadOp memLoadOp) {
549 if (
auto memStoreOp = dyn_cast<memref::StoreOp>(storeOp))
551 firstToSecondPloopIVsMap,
b);
552 if (
auto vecWriteOp = dyn_cast<vector::TransferWriteOp>(storeOp))
554 firstToSecondPloopIVsMap);
555 if (
auto vecStoreOp = dyn_cast<vector::StoreOp>(storeOp))
557 firstToSecondPloopIVsMap);
560 .Case([&](vector::TransferReadOp vecReadOp) {
561 auto vecWriteOp = dyn_cast<vector::TransferWriteOp>(storeOp);
565 firstToSecondPloopIVsMap,
b) &&
566 (vecReadOp.getMask() == vecWriteOp.getMask()) &&
567 (vecReadOp.getInBounds() == vecWriteOp.getInBounds());
569 .Case([&](vector::LoadOp vecLoadOp) {
570 auto vecStoreOp = dyn_cast<vector::StoreOp>(storeOp);
574 firstToSecondPloopIVsMap,
b) &&
575 (vecLoadOp.getAlignment() == vecStoreOp.getAlignment());
577 .Default([](
Operation *) {
return false; });
578 return accessSameMemory;
583 .Case([&](memref::StoreOp storeOp) {
return storeOp.getMemRef(); })
584 .Case([&](vector::TransferWriteOp writeOp) {
return writeOp.getBase(); })
585 .Case([&](vector::StoreOp vecStoreOp) {
return vecStoreOp.getBase(); })
594 if (
auto transfWriteOp = dyn_cast<vector::TransferWriteOp>(storeOp);
595 transfWriteOp && isa<memref::LoadOp>(loadOp))
598 if (
auto vecStoreOp = dyn_cast<vector::StoreOp>(storeOp);
599 vecStoreOp && isa<memref::LoadOp>(loadOp))
609 ParallelOp firstPloop, ParallelOp secondPloop,
610 const IRMapping &firstToSecondPloopIndices,
615 llvm::SmallSetVector<Value, 4> buffersWrittenInFirstPloop;
617 auto collectStoreOpsInWalk = [&](
Operation *op) {
618 auto memOpInterf = dyn_cast_if_present<MemoryEffectOpInterface>(op);
631 MemrefValue storeOpBaseMemref = dyn_cast<MemrefValue>(storeOpBase);
632 if (!storeOpBaseMemref)
636 bufferStoresInFirstPloop[buffer].push_back(op);
637 buffersWrittenInFirstPloop.insert(buffer);
643 if (firstPloop.getBody()->walk(collectStoreOpsInWalk).wasInterrupted())
652 auto checkLoadInWalkHasNoIncompatibleDataDeps = [&](
Operation *loadOp) {
653 auto memOpInterf = dyn_cast_if_present<MemoryEffectOpInterface>(loadOp);
669 if (!isa<memref::LoadOp, vector::TransferReadOp, vector::LoadOp>(loadOp) ||
670 !isa<MemrefValue>(loadOp->getOperand(0)))
673 MemrefValue loadOpBase = cast<MemrefValue>(loadOp->getOperand(0));
676 for (
Value storedMem : buffersWrittenInFirstPloop)
677 if ((storedMem != loadedOrigBuf) &&
mayAlias(storedMem, loadedOrigBuf) &&
678 !llvm::all_of(bufferStoresInFirstPloop[storedMem],
681 firstToSecondPloopIndices);
686 auto writeOpsIt = bufferStoresInFirstPloop.find(loadedOrigBuf);
687 if (writeOpsIt == bufferStoresInFirstPloop.end())
693 if (writeOps.empty())
700 if (!llvm::all_of(writeOps, [&](
Operation *otherWriteOp) {
709 firstToSecondPloopIndices,
b)) {
718 return !secondPloop.getBody()
719 ->walk(checkLoadInWalkHasNoIncompatibleDataDeps)
728 const IRMapping &firstToSecondPloopIndices,
732 firstPloop, secondPloop, firstToSecondPloopIndices,
mayAlias,
b))
736 secondToFirstPloopIndices.
map(secondPloop.getBody()->getArguments(),
737 firstPloop.getBody()->getArguments());
739 secondPloop, firstPloop, secondToFirstPloopIndices,
mayAlias,
b);
746 const IRMapping &firstToSecondPloopIndices,
767static std::optional<ParallelOp>
770 assert(loop.getNumLoops() ==
indices.size());
771 if (loop.getNumLoops() < 2)
782 auto newOp = ParallelOp::create(builder, loop.getLoc(), newLB, newUB, newStep,
783 loop.getInitVals(),
nullptr);
784 auto ivs = loop.getInductionVars();
788 for (
auto [iv, riv] : llvm::zip(ivs, newIvs)) {
789 mapping.
map(iv, riv);
794 for (
auto &o : loop.getNumReductions()
795 ? loop.getBodyRegion().front()
796 : loop.getBodyRegion().front().without_terminator()) {
818 return llvm::hash_combine(
831 ParallelOp &secondPloop,
832 int permBudget = 120) {
834 if (firstPloop.getNumLoops() < 2 ||
835 firstPloop.getNumLoops() != secondPloop.getNumLoops())
840 llvm::SmallSetVector<LoopIV, 6> unique;
841 for (
unsigned index : llvm::seq(firstPloop.getNumLoops())) {
842 firstIVs[
index].lBound = firstPloop.getLowerBound()[
index];
843 firstIVs[
index].uBound = firstPloop.getUpperBound()[
index];
844 firstIVs[
index].step = firstPloop.getStep()[
index];
845 secondIVs[
index].lBound = secondPloop.getLowerBound()[
index];
846 secondIVs[
index].uBound = secondPloop.getUpperBound()[
index];
847 secondIVs[
index].step = secondPloop.getStep()[
index];
848 unique.insert(firstIVs[
index]);
853 llvm::zip(firstIVs, secondIVs), diffIVs.begin(),
854 [](
auto const &pair) { return std::get<0>(pair) != std::get<1>(pair); });
857 for (
auto [idx, val] : enumerate(diffIVs))
867 std::iota(basic.begin(), basic.end(), 0);
869 if (
indices.empty() && unique.size() == firstIVs.size())
879 if (fIdx != sIdx && firstIVs[fIdx] == secondIVs[sIdx] &&
880 remaps.end() == std::find(remaps.begin(), remaps.end(), sIdx)) {
881 remaps.push_back(sIdx);
888 if (
indices.size() != remaps.size())
892 for (
auto [from, to] : zip(
indices, remaps)) {
896 LDBG() <<
"Collected basic permutations: "
897 << llvm::interleaved_array(basic);
900 if (unique.size() == firstIVs.size()) {
907 assert(unique.size() != firstIVs.size() &&
908 "Expected at least two equal axes");
913 for (
auto iv : unique) {
915 for (
unsigned index : llvm::seq(firstIVs.size())) {
916 if (firstIVs[
index] == iv)
917 group.push_back(
index);
919 if (group.size() > 1)
920 groups.push_back(std::move(group));
926 while (repeat && permBudget) {
928 for (
auto const &[group, groupRemaps] : zip(groups, rmpdGroups)) {
929 repeat |= std::next_permutation(groupRemaps.begin(), groupRemaps.end());
936 for (
auto const &[group, groupRemaps] : zip(groups, rmpdGroups)) {
937 for (
auto [from, to] : zip(group, groupRemaps))
938 extra[from] = basic[to];
940 if (basic != extra) {
941 LDBG() <<
"Collected extra permutations: "
942 << llvm::interleaved_array(extra);
944 extraResults.push_back(std::move(extra));
957 Block *block1 = firstPloop.getBody();
958 Block *block2 = secondPloop.getBody();
960 ValueRange inits2 = secondPloop.getInitVals();
963 newInitVars.append(inits2.begin(), inits2.end());
966 b.setInsertionPoint(secondPloop);
967 auto newSecondPloop = ParallelOp::create(
968 b, secondPloop.getLoc(), secondPloop.getLowerBound(),
969 secondPloop.getUpperBound(), secondPloop.getStep(), newInitVars);
971 Block *newBlock = newSecondPloop.getBody();
975 b.inlineBlockBefore(block2, newBlock, newBlock->
begin(),
977 b.inlineBlockBefore(block1, newBlock, newBlock->
begin(),
980 ValueRange results = newSecondPloop.getResults();
981 if (!results.empty()) {
982 b.setInsertionPointToEnd(newBlock);
987 newReduceArgs.append(reduceArgs2.begin(), reduceArgs2.end());
989 auto newReduceOp = scf::ReduceOp::create(
b, term2.getLoc(), newReduceArgs);
991 for (
auto &&[i, reg] : llvm::enumerate(llvm::concat<Region>(
992 term1.getReductions(), term2.getReductions()))) {
994 Block &newRedBlock = newReduceOp.getReductions()[i].
front();
995 b.inlineBlockBefore(&oldRedBlock, &newRedBlock, newRedBlock.
begin(),
999 firstPloop.replaceAllUsesWith(results.take_front(inits1.size()));
1000 secondPloop.replaceAllUsesWith(results.take_back(inits2.size()));
1005 secondPloop.erase();
1006 secondPloop = newSecondPloop;
1010static void fuseIfLegal(ParallelOp firstPloop, ParallelOp &secondPloop,
1013 Block *block1 = firstPloop.getBody();
1014 Block *block2 = secondPloop.getBody();
1018 if (
isFusionLegal(firstPloop, secondPloop, firstToSecondPloopIndices,
1030 LDBG() <<
"Applied permutation: " << llvm::interleaved_array(perms);
1033 firstToSecondPloopIndices.
clear();
1035 newLoop->getBody()->getArguments());
1036 if (!
isFusionLegal(firstPloop, *newLoop, firstToSecondPloopIndices,
1038 LDBG() <<
"Rejected: " << newLoop;
1044 secondPloop.replaceAllUsesWith(newLoop->getResults());
1045 secondPloop->erase();
1046 secondPloop = *newLoop;
1057 for (
auto &block : region) {
1058 ploopChains.clear();
1059 ploopChains.push_back({});
1064 bool noSideEffects =
true;
1065 for (
auto &op : block) {
1066 if (
auto ploop = dyn_cast<ParallelOp>(op)) {
1067 if (noSideEffects) {
1068 ploopChains.back().push_back(ploop);
1070 ploopChains.push_back({ploop});
1071 noSideEffects =
true;
1079 for (
int i = 0, e = ploops.size(); i + 1 < e; ++i)
1086struct ParallelLoopFusion
1087 :
public impl::SCFParallelLoopFusionBase<ParallelLoopFusion> {
1088 void runOnOperation()
override {
1089 auto &aa = getAnalysis<AliasAnalysis>();
1096 auto val2Def = val2.getDefiningOp();
1100 val2Def ? val2Def->getParentOfType<ParallelOp>() :
nullptr;
1101 if (val1Loop != val2Loop)
1104 return !aa.alias(val1, val2).isNo();
1107 getOperation()->walk([&](
Operation *child) {
1116 return std::make_unique<ParallelLoopFusion>();
static bool mayAlias(Value first, Value second)
Returns true if two values may be referencing aliasing memory.
static bool canResolveAlias(Operation *loadOp, Operation *storeOp, const IRMapping &loopsIVsMap)
To be called when mayAlias(val1, val2) is true.
static std::optional< ParallelOp > interchangeLoops(OpBuilder &builder, ParallelOp &loop, const ArrayRef< int64_t > &indices)
static bool equalIterationSpaces(ParallelOp firstPloop, ParallelOp secondPloop)
Verify equal iteration spaces.
static bool isLoadOnWrittenVector(memref::LoadOp loadOp, Value writeBase, ValueRange writeIndices, VectorType vecTy, ArrayRef< int64_t > vectorDimForWriteDim, const IRMapping &ivsMap)
Recognize scalar memref.load of an element produced by a vector write (vector.transfer_write or vecto...
static bool loadMatchesVectorWrite(memref::LoadOp loadOp, vector::TransferWriteOp writeOp, const IRMapping &ivsMap)
Recognize scalar memref.load of an element produced by a vector.transfer_write.
static std::optional< int64_t > getAddConstant(Value expr, Value base, const IRMapping &loopsIVsMap)
If the expr value is the result of an integer addition of base and a constant, return the constant.
static bool opsAccessSameIndices(OpTy1 op1, OpTy2 op2, const IRMapping &loopsIVsMap, OpBuilder &b)
Check if both memory read/write operations access the same indices (considering also the mapping of i...
static Value getStoreOpTargetBuffer(Operation *op)
static void applyLoopFusion(ParallelOp &firstPloop, ParallelOp &secondPloop, OpBuilder &builder)
Prepend operations of firstPloop's body into secondPloop's body.
static bool haveNoDataDependenciesExceptSameIndex(ParallelOp firstPloop, ParallelOp secondPloop, const IRMapping &firstToSecondPloopIndices, llvm::function_ref< bool(Value, Value)> mayAlias, OpBuilder &b)
Check that the parallel loops have no mixed access to the same buffers.
static Value getBaseMemref(Operation *op)
Return the base memref value used by the given memory op.
static bool loadsFromSameMemoryLocationWrittenBy(Operation *loadOp, Operation *storeOp, const IRMapping &firstToSecondPloopIVsMap, OpBuilder &b)
Check if the loadOp reads from the same memory location (same buffer, same indices and same propertie...
static SmallVector< SmallVector< int64_t > > computeCandidateInterchangePermutations(ParallelOp &firstPloop, ParallelOp &secondPloop, int permBudget=120)
static bool loadIndexWithinWriteRange(Value loadIndex, OpFoldResult offset, Value writeIndex, int64_t extent, const IRMapping &loopsIVsMap)
static bool opsWriteSameMemLocation(Operation *op1, Operation *op2)
Check if both operations are the same type of memory write op and write to the same memory location (...
static bool noIncompatibleDataDependencies(ParallelOp firstPloop, ParallelOp secondPloop, const IRMapping &firstToSecondPloopIndices, llvm::function_ref< bool(Value, Value)> mayAlias, OpBuilder &b)
Check that in each loop there are no read ops on the buffers written by the other loop,...
static bool valsAreEquivalent(Value val1, Value val2, const IRMapping &loopsIVsMap)
Check if val1 (from the first parallel loop) and val2 (from the second) are equivalent,...
static bool isFusionLegal(ParallelOp firstPloop, ParallelOp secondPloop, const IRMapping &firstToSecondPloopIndices, llvm::function_ref< bool(Value, Value)> mayAlias, OpBuilder &b)
Check if fusion of the two parallel loops is legal: i.e.
static bool opsAccessSameIndicesViaRankReducingSubview(OpTy1 op1, OpTy2 op2, const IRMapping &firstToSecondPloopIVsMap, OpBuilder &b)
Check if both operations access the same positions of the same buffer, but one of the two does it thr...
static bool loadMatchesVectorStore(memref::LoadOp loadOp, vector::StoreOp storeOp, const IRMapping &ivsMap)
Recognize scalar memref.load of an element produced by a vector.store.
static bool hasNestedParallelOp(ParallelOp ploop)
Verify there are no nested ParallelOps.
static void fuseIfLegal(ParallelOp firstPloop, ParallelOp &secondPloop, OpBuilder builder, llvm::function_ref< bool(Value, Value)> mayAlias)
Check fusion pre-conditions and call fusion if it is possible.
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumSymbols() const
unsigned getNumDims() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
static AffineMap getPermutationMap(ArrayRef< unsigned > permutation, MLIRContext *context)
Returns an AffineMap representing a permutation.
Block represents an ordered list of Operations.
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgListType getArguments()
A class for computing basic dominance information.
bool properlyDominates(Operation *a, Operation *b, bool enclosingOpOk=true) const
Return true if operation A properly dominates operation B, i.e.
This is a utility class for mapping one set of IR entities to another.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void clear()
Clears all mappings held by the mapper.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
static OpBuilder atBlockBegin(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the first operation in the block but still ins...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
This class represents a single result from folding an operation.
This trait indicates that the memory effects of an operation includes the effects of operations neste...
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
OperationName getName()
The name of an operation is the key identifier for it.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
user_range getUsers()
Returns a range of all users.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
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...
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static WalkResult advance()
static WalkResult interrupt()
MemrefValue skipFullyAliasingOperations(MemrefValue source)
Walk up the source chain until an operation that changes/defines the view of memory is found (i....
bool isSameViewOrTrivialAlias(MemrefValue a, MemrefValue b)
Checks if two (memref) values are the same or statically known to alias the same region of memory.
void naivelyFuseParallelOps(Region ®ion, llvm::function_ref< bool(Value, Value)> mayAlias)
Fuses all adjacent scf.parallel operations with identical bounds and step into one scf....
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
SmallVector< T > applyPermutation(ArrayRef< T > input, ArrayRef< int64_t > permutation)
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
llvm::SmallVector< std::tuple< int64_t, int64_t, int64_t > > getConstLoopBounds(mlir::LoopLikeOpInterface loopOp)
Get constant loop bounds and steps for each of the induction variables of the given loop operation,...
detail::constant_int_predicate_matcher m_Zero()
Matches a constant scalar / vector splat / tensor splat integer zero.
TypedValue< BaseMemRefType > MemrefValue
A value with a memref type.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
std::unique_ptr< Pass > createParallelLoopFusionPass()
Creates a loop fusion pass which fuses parallel loops.
SmallVector< int64_t > invertPermutationVector(ArrayRef< int64_t > permutation)
Helper method to apply to inverse a permutation.
bool operator==(LoopIV const &other) const
bool operator!=(LoopIV const &other) const
static bool isEqual(const LoopIV &lhs, const LoopIV &rhs)
static unsigned getHashValue(const LoopIV &val)
The following effect indicates that the operation frees some resource that has been allocated.
The following effect indicates that the operation reads from some resource.
The following effect indicates that the operation writes to some resource.
static bool isEquivalentTo(Operation *lhs, Operation *rhs, function_ref< LogicalResult(Value, Value)> checkEquivalent, function_ref< void(Value, Value)> markEquivalent=nullptr, Flags flags=Flags::None, function_ref< LogicalResult(ValueRange, ValueRange)> checkCommutativeEquivalent=nullptr)
Compare two operations (including their regions) and return if they are equivalent.