22#include "llvm/ADT/STLExtras.h"
23#include "llvm/ADT/SetOperations.h"
24#include "llvm/ADT/SmallBitVector.h"
25#include "llvm/ADT/SmallVector.h"
26#include "llvm/Support/Casting.h"
27#include "llvm/Support/raw_ostream.h"
34#include "mlir/Dialect/Linalg/IR/LinalgInterfaces.cpp.inc"
43 for (
auto &opOperand : linalgOp->getOpOperands()) {
44 if (llvm::is_contained(droppedOperands, &opOperand))
46 indexingMaps.push_back(linalgOp.getMatchingIndexingMap(&opOperand));
48 if (indexingMaps.empty()) {
51 return linalgOp.getNumLoops() == 0;
54 indexingMaps, linalgOp.getContext())) !=
AffineMap();
63 if (!op.isAllParallelLoops() || !op.isSingleInputOutput())
66 auto mapRange = op.getIndexingMapsArray();
67 if (mapRange.size() != 2 || !mapRange.front().isIdentity() ||
68 !mapRange.back().isIdentity()) {
72 Block *body = op.getBlock();
75 auto yieldOp = dyn_cast<linalg::YieldOp>(body->
back());
76 if (!yieldOp || yieldOp.getNumOperands() != 1)
78 return yieldOp->getOperand(0) == body->
getArgument(0);
88 if (!op.isAllParallelLoops() || op.getNumDpsInits() != 1 ||
89 op.getNumDpsInputs() != 0)
93 if (op.payloadUsesValueFromOperand(op.getDpsInitOperand(0)))
96 Block *body = op.getBody();
100 auto yieldOp = dyn_cast<linalg::YieldOp>(body->
back());
101 if (!yieldOp || yieldOp.getNumOperands() != 1)
104 Value yieldOperand = yieldOp->getOperand(0);
116 if (!op.isAllParallelLoops() || !op.isSingleInputOutput() ||
117 !op.isSingleYieldOp())
121 if (!op.payloadUsesValueFromOperand(op.getDpsInputOperand(0)) ||
122 op.payloadUsesValueFromOperand(op.getDpsInitOperand(0)))
125 OpOperand *value = op.getDpsInputOperand(0);
126 if (!op.isScalar(value))
140std::optional<SmallVector<int64_t>>
142 if (
auto broadcastOp = dyn_cast<BroadcastOp>(linalgOp.getOperation()))
144 broadcastOp.getDimensions().end());
146 auto op = dyn_cast<GenericOp>(linalgOp.getOperation());
151 if (!op.isAllParallelLoops() || !op.isSingleInputOutput() ||
152 !op.isSingleYieldOp())
155 auto srcTy = op.getDpsInputOperand(0)->get().getType();
156 auto dstTy = op.getDpsInitOperand(0)->get().getType();
157 if (!isa<MemRefType, RankedTensorType>(srcTy) ||
158 !isa<MemRefType, RankedTensorType>(dstTy))
164 auto dstMap = op.getIndexingMapsArray()[1];
165 if (!dstMap.isIdentity())
169 auto srcMap = op.getIndexingMapsArray()[0];
171 if (srcMap.getResults().size() >= dstMap.getResults().size())
175 for (
unsigned i = 0; i < srcMap.getNumResults(); ++i) {
176 auto expr = llvm::dyn_cast<AffineDimExpr>(srcMap.getResults()[i]);
179 int64_t pos = expr.getPosition();
180 if (i > 0 && pos <= position[i - 1])
182 position.push_back(expr.getPosition());
186 auto numDims = srcMap.getNumDims();
188 for (
auto dim : llvm::seq<int64_t>(0, numDims)) {
189 if (!llvm::is_contained(position, dim))
190 broadcastedDims.push_back(dim);
192 return broadcastedDims;
198std::optional<SmallVector<int64_t>>
203 if (!op.isAllParallelLoops() || !op.isSingleInputOutput() ||
204 !op.isSingleYieldOp())
207 auto mapRange = op.getIndexingMapsArray();
208 if (mapRange.size() != 2)
211 auto mapOfInput = mapRange.front();
212 auto mapOfResult = mapRange.back();
216 if (!mapOfResult.isIdentity() || !mapOfInput.isPermutation())
220 for (
unsigned i = 0; i < mapOfInput.getNumDims(); ++i) {
221 auto expr = llvm::cast<AffineDimExpr>(mapOfInput.getResults()[i]);
222 permutation[expr.getPosition()] = i;
230std::optional<SmallVector<int64_t>>
233 if (op.getNumReductionLoops() == 0)
237 unsigned numInputs = op.getNumDpsInputs();
238 if (numInputs == 0 || numInputs != op.getNumDpsInits())
243 for (
auto [idx, iter] : llvm::enumerate(op.getIteratorTypesArray()))
244 if (iter == utils::IteratorType::reduction)
245 reductionDims.push_back(idx);
253 for (
OpOperand *input : op.getDpsInputOperands())
254 if (!op.getMatchingIndexingMap(input).isIdentity())
256 for (
OpOperand &init : op.getDpsInitsMutable())
257 if (op.getMatchingIndexingMap(&init) != expectedOutputMap)
264 dyn_cast<ShapedType>(op.getDpsInputOperand(0)->get().getType());
265 if (!inputType || !inputType.hasRank())
267 for (
OpOperand *input : op.getDpsInputOperands()) {
268 auto type = dyn_cast<ShapedType>(input->get().getType());
269 if (!type || !type.hasRank() || type.getShape() != inputType.getShape())
274 for (
auto [idx, size] : llvm::enumerate(inputType.getShape()))
275 if (!llvm::is_contained(reductionDims, idx))
276 expectedOutputShape.push_back(size);
277 for (
OpOperand &init : op.getDpsInitsMutable()) {
278 auto type = dyn_cast<ShapedType>(init.get().getType());
279 if (!type || !type.hasRank() ||
284 return reductionDims;
293 if (!op.isAllParallelLoops() || op.getNumLoops() < 1)
298 if (op.getNumDpsInputs() != arity || op.getNumDpsInits() != 1)
302 if (op.payloadUsesValueFromOperand(op.getDpsInitOperand(0)))
309 Block *body = op.getBody();
321 auto yieldOp = dyn_cast<linalg::YieldOp>(body->
back());
322 return !(!yieldOp || yieldOp.getNumOperands() != 1 ||
323 yieldOp->getOperand(0).getDefiningOp() != oper);
332 if (!op.payloadUsesValueFromOperand(op.getDpsInputOperand(0)))
343 OpOperand *inputOpOperand0 = op.getDpsInputOperand(0);
344 OpOperand *inputOpOperand1 = op.getDpsInputOperand(1);
345 return !(!op.payloadUsesValueFromOperand(inputOpOperand0) ||
346 !op.payloadUsesValueFromOperand(inputOpOperand1));
363 OpOperand *inputOpOperand0 = op.getDpsInputOperand(0);
364 OpOperand *inputOpOperand1 = op.getDpsInputOperand(1);
365 OpOperand *inputOpOperand2 = op.getDpsInputOperand(2);
366 return !(!op.payloadUsesValueFromOperand(inputOpOperand0) ||
367 !op.payloadUsesValueFromOperand(inputOpOperand1) ||
368 !op.payloadUsesValueFromOperand(inputOpOperand2));
382 auto iface = dyn_cast<MemoryEffectOpInterface>(op);
383 if (!iface || !iface.hasNoEffect())
393 llvm::raw_ostream &errs) {
395 errs <<
"no terminator in the block";
400 errs <<
"expected block with 3 arguments";
406 errs <<
"expected terminator with 1 operand";
414 errs <<
"expected reduction op to be binary";
423 errs <<
"expected reduction to take block argument #2 as one of the "
424 "operands (modulo unary casts)";
429 isa<BlockArgument>(reductionLHS) ? reductionRHS : reductionLHS);
433 errs <<
"expected elementwise op to be binary";
437 if (!isaPair(elementwiseOp, reductionOp)) {
438 errs <<
"expected reduction/elementwise op kind not satisfied";
451 errs <<
"expected elementwise op to apply to block arguments (modulo unary "
458template <
typename AddOpTy,
typename MulOpTy,
typename... Args>
460 static_assert(
sizeof...(Args) % 2 == 0,
461 "expected an even number of template arguments");
462 if (isa<AddOpTy>(
add) && isa<MulOpTy>(
mul))
465 if constexpr (
sizeof...(Args) > 0)
473template <
typename... Args>
485static llvm::SmallDenseSet<int64_t>
488 utils::IteratorType iter) {
489 assert(iterators.size() == indexingMap.
getNumDims());
490 llvm::SmallDenseSet<int64_t> res;
492 if (
auto d = dyn_cast<AffineDimExpr>(e)) {
493 if (iterators[d.getPosition()] == iter &&
495 return e.isFunctionOfDim(d.getPosition());
497 res.insert(d.getPosition());
504auto par = utils::IteratorType::parallel;
505auto red = utils::IteratorType::reduction;
512static FailureOr<SmallVector<utils::IteratorType>>
518 if (
auto dim = dyn_cast<AffineDimExpr>(expr))
519 iterators[dim.getPosition()] = par;
534static FailureOr<ContractionDimensions>
537 llvm::SmallDenseSet<int64_t> a =
539 llvm::SmallDenseSet<int64_t>
b =
541 llvm::SmallDenseSet<int64_t> c =
545 llvm::SmallDenseSet<int64_t> ac = a;
546 llvm::set_intersect(ac, c);
547 llvm::set_subtract(ac,
b);
549 llvm::SmallDenseSet<int64_t> bc =
b;
550 llvm::set_intersect(bc, c);
551 llvm::set_subtract(bc, a);
553 llvm::SmallDenseSet<int64_t> batches = a;
554 llvm::set_intersect(batches,
b);
555 llvm::set_intersect(batches, c);
558 llvm::SmallDenseSet<int64_t> ra =
560 llvm::SmallDenseSet<int64_t> rb =
562 llvm::set_intersect(ra, rb);
570 llvm::sort(dimensions.
batch);
571 llvm::sort(dimensions.
m);
572 llvm::sort(dimensions.
n);
573 llvm::sort(dimensions.
k);
577FailureOr<ContractionDimensions>
579 if (linalgOp.getNumDpsInits() != 1 || linalgOp.getNumDpsInputs() != 2)
582 linalgOp.getIteratorTypesArray());
585FailureOr<ContractionDimensions>
587 if (indexingMaps.size() != 3)
590 if (failed(iterators))
609 auto linalgOp = dyn_cast<linalg::LinalgOp>(op);
612 if (linalgOp.getNumDpsInputs() != 2 || linalgOp.getNumDpsInits() != 1)
614 auto mapRange = linalgOp.getIndexingMapsArray();
615 if (linalgOp.getNumReductionLoops() == 0)
617 if (llvm::any_of(mapRange,
626 Block &body = *linalgOp.getBlock();
627 bool isContraction =
false;
630 else if (isa<IntegerType>(accType))
632 else if (isa<FloatType>(accType))
634 else if (isa<ComplexType>(accType))
641 assert(succeeded(res) &&
"unexpected failure to infer contraction dims");
651 return "expected a LinalgOp";
653 return "expected op with 2 inputs and 1 output";
655 return "expected at least 1 reduction";
657 return "expected indexing maps to be projected permutations";
659 return "expected add/mul op in the body";
663 llvm_unreachable(
"unhandled MatchContractionResult case");
670 return isa<ContractionOpInterface>(op) ||
703 return isa<T>(lhs) ? cast<T>(lhs) : (isa<T>(rhs) ? cast<T>(rhs) :
nullptr);
715struct ConvAccessExprWalker
718 llvm::SmallDenseSet<int64_t> convolvedDims;
720 llvm::SmallDenseMap<int64_t, int64_t> convolvedDimMapping;
722 llvm::SmallDenseSet<int64_t> unConvolvedDims;
724 llvm::SmallDenseMap<int64_t, AffineExpr> strideAndDilationMapping;
728 void clearMultiUseDims(AffineMap map) {
729 for (
int dimPos = 0, e = map.
getNumDims(); dimPos < e; ++dimPos) {
730 if (llvm::count_if(map.
getResults(), [dimPos](AffineExpr e) {
731 return e.isFunctionOfDim(dimPos);
733 convolvedDims.erase(dimPos);
734 unConvolvedDims.erase(dimPos);
737 auto it = convolvedDimMapping.find(dimPos);
738 if (it != convolvedDimMapping.end()) {
739 int64_t pairedDim = it->second;
740 convolvedDims.erase(pairedDim);
741 unConvolvedDims.erase(pairedDim);
742 strideAndDilationMapping.erase(pairedDim);
743 convolvedDimMapping.erase(dimPos);
744 convolvedDimMapping.erase(pairedDim);
750 LogicalResult visitDimExpr(AffineDimExpr dimExpr) {
752 if (unConvolvedDims.count(position) || convolvedDims.count(position)) {
755 unConvolvedDims.insert(position);
759 LogicalResult visitSymbolExpr(AffineSymbolExpr expr) {
return failure(); }
761 LogicalResult visitConstantExpr(AffineConstantExpr expr) {
return failure(); }
763 LogicalResult visitAffineBinaryOpExpr(AffineBinaryOpExpr binaryExpr) {
765 if (binaryExpr.
getKind() != AffineExprKind::Add)
767 auto lhsDimPos = getDimExprOrMulExprDimPos(binaryExpr.
getLHS());
768 auto rhsDimPos = getDimExprOrMulExprDimPos(binaryExpr.
getRHS());
771 convolvedDimMapping[*lhsDimPos] = *rhsDimPos;
772 convolvedDimMapping[*rhsDimPos] = *lhsDimPos;
776 FailureOr<int64_t> getDimExprOrMulExprDimPos(AffineExpr expr) {
777 if (
auto dimExpr = dyn_cast<AffineDimExpr>(expr)) {
779 if (convolvedDims.count(dim) || unConvolvedDims.count(dim))
782 strideAndDilationMapping[dim] =
784 convolvedDims.insert(dim);
787 if (
auto symbolMulExpr = dyn_cast<AffineBinaryOpExpr>(expr)) {
788 if (symbolMulExpr.getKind() != AffineExprKind::Mul)
790 auto lhsExpr = symbolMulExpr.getLHS();
791 auto rhsExpr = symbolMulExpr.getRHS();
800 if (!mulExpr || !dimExpr)
803 if (convolvedDims.count(dim) || unConvolvedDims.count(dim))
805 strideAndDilationMapping[dim] = mulExpr;
806 convolvedDims.insert(dim);
816 "expected map to have projected permutations");
817 llvm::SmallDenseSet<int64_t> preservedDims;
819 preservedDims.insert(cast<AffineDimExpr>(expr).getPosition());
820 return preservedDims;
826 for (
auto e : exprs) {
827 auto constantExpr = dyn_cast<AffineConstantExpr>(e);
828 assert(constantExpr &&
"Found non-constant stride/dilation");
829 vals.push_back(constantExpr.getValue());
854 ConvAccessExprWalker &inputExprWalker,
bool allowEmptyConvolvedDims,
857 AffineMap outputMap = indexingMaps.back();
858 llvm::SmallDenseSet<int64_t> filterDims =
860 llvm::SmallDenseSet<int64_t> outputDims =
864 llvm::SmallDenseSet<int64_t> batch = inputExprWalker.unConvolvedDims;
865 llvm::set_intersect(batch, outputDims);
866 llvm::set_subtract(batch, filterDims);
869 llvm::SmallDenseSet<int64_t> oi = inputExprWalker.convolvedDims;
870 llvm::set_intersect(oi, outputDims);
873 llvm::SmallDenseSet<int64_t> oc = filterDims;
874 llvm::set_intersect(oc, outputDims);
875 llvm::set_subtract(oc, inputExprWalker.unConvolvedDims);
878 llvm::SmallDenseSet<int64_t> depth = filterDims;
879 llvm::set_intersect(depth, outputDims);
880 llvm::set_intersect(depth, inputExprWalker.unConvolvedDims);
882 llvm::SmallDenseSet<int64_t> filterReducedDims =
886 llvm::SmallDenseSet<int64_t> fl = inputExprWalker.convolvedDims;
887 llvm::set_intersect(fl, filterReducedDims);
890 llvm::SmallDenseSet<int64_t> ic = inputExprWalker.unConvolvedDims;
891 llvm::set_intersect(ic, filterReducedDims);
893 if (oi.empty() && !allowEmptyConvolvedDims)
907 llvm::sort(dimensions.
batch);
911 llvm::sort(dimensions.
depth);
917 dimensions.
filterLoop.push_back(inputExprWalker.convolvedDimMapping[oiDim]);
920 if (!nativeStrides) {
923 strideExprs.push_back(inputExprWalker.strideAndDilationMapping[oiDim]);
926 dimensions.
strides = llvm::to_vector<2>(nativeStrides.getValues<
int64_t>());
928 if (!nativeDilations) {
931 dilationExprs.push_back(inputExprWalker.strideAndDilationMapping[flDim]);
935 llvm::to_vector<2>(nativeDilations.getValues<
int64_t>());
942 return dyn_cast_or_null<DenseIntElementsAttr>(
943 linalgOp->getInherentAttr(name).value_or(
Attribute{}));
974FailureOr<ConvolutionDimensions>
976 if (linalgOp.getNumDpsInits() != 1 || linalgOp.getNumDpsInputs() != 2)
979 auto indexingMaps = linalgOp.getIndexingMapsArray();
982 ConvAccessExprWalker inputExprWalker;
983 for (
AffineExpr expr : indexingMaps[0].getResults())
984 (
void)inputExprWalker.visit(expr);
985 inputExprWalker.clearMultiUseDims(indexingMaps[0]);
988 indexingMaps, linalgOp.getIteratorTypesArray(), inputExprWalker,
994FailureOr<ConvolutionDimensions>
996 if (indexingMaps.size() != 3)
1000 FailureOr<SmallVector<utils::IteratorType>> iterators =
1002 if (failed(iterators))
1006 ConvAccessExprWalker inputExprWalker;
1007 for (
AffineExpr expr : indexingMaps[0].getResults())
1008 (
void)inputExprWalker.visit(expr);
1009 inputExprWalker.clearMultiUseDims(indexingMaps[0]);
1035 bool allowEmptyConvolvedDims) {
1036 auto linalgOp = dyn_cast<linalg::LinalgOp>(op);
1039 if (linalgOp.getNumDpsInputs() < 2 || linalgOp.getNumDpsInits() != 1)
1042 auto indexingMaps = linalgOp.getIndexingMapsArray();
1045 ConvAccessExprWalker inputExprWalker;
1046 if (llvm::any_of(indexingMaps[0].getResults(),
1048 return failed(inputExprWalker.visit(expr));
1054 if (!indexingMaps[1].isProjectedPermutation() ||
1055 !indexingMaps.back().isProjectedPermutation())
1058 auto iteratorTypes = linalgOp.getIteratorTypesArray();
1060 llvm::SmallDenseSet<int64_t> outputDims =
1062 llvm::SmallDenseSet<int64_t> filterDims =
getPreservedDims(indexingMaps[1]);
1076 llvm::SmallDenseSet<int64_t> allLoopDims;
1077 for (
auto outputExpr : indexingMaps.back().getResults()) {
1078 int64_t outputDim = cast<AffineDimExpr>(outputExpr).getPosition();
1079 if (inputExprWalker.unConvolvedDims.count(outputDim) &&
1080 !filterDims.count(outputDim)) {
1082 if (iteratorTypes[outputDim] != utils::IteratorType::parallel)
1084 allLoopDims.insert(outputDim);
1087 if (inputExprWalker.convolvedDims.count(outputDim) &&
1088 !filterDims.count(outputDim)) {
1090 if (iteratorTypes[outputDim] != utils::IteratorType::parallel)
1092 allLoopDims.insert(outputDim);
1095 if (!inputExprWalker.convolvedDims.count(outputDim) &&
1096 !inputExprWalker.unConvolvedDims.count(outputDim) &&
1097 filterDims.count(outputDim)) {
1099 if (iteratorTypes[outputDim] != utils::IteratorType::parallel)
1101 allLoopDims.insert(outputDim);
1104 if (inputExprWalker.unConvolvedDims.count(outputDim) &&
1105 filterDims.count(outputDim)) {
1107 if (iteratorTypes[outputDim] != utils::IteratorType::parallel)
1109 allLoopDims.insert(outputDim);
1114 for (
auto filterExpr : indexingMaps[1].getResults()) {
1115 int64_t filterDim = cast<AffineDimExpr>(filterExpr).getPosition();
1116 if (outputDims.count(filterDim) &&
1117 !inputExprWalker.unConvolvedDims.count(filterDim) &&
1118 !inputExprWalker.convolvedDims.count(filterDim)) {
1122 if (inputExprWalker.convolvedDims.count(filterDim) &&
1123 !outputDims.count(filterDim)) {
1125 if (iteratorTypes[filterDim] != utils::IteratorType::reduction)
1127 if (allLoopDims.count(filterDim))
1129 allLoopDims.insert(filterDim);
1132 if (inputExprWalker.unConvolvedDims.count(filterDim) &&
1133 !outputDims.count(filterDim)) {
1135 if (iteratorTypes[filterDim] != utils::IteratorType::reduction)
1137 if (allLoopDims.count(filterDim))
1139 allLoopDims.insert(filterDim);
1142 if (inputExprWalker.unConvolvedDims.count(filterDim) &&
1143 outputDims.count(filterDim)) {
1150 if (allLoopDims.size() != linalgOp.getNumLoops())
1153 if (!allowEmptyConvolvedDims && inputExprWalker.convolvedDims.empty())
1158 indexingMaps, iteratorTypes, inputExprWalker, allowEmptyConvolvedDims,
1161 assert(succeeded(res) &&
"unexpected failure to infer convolution dims");
1172 return "expected a LinalgOp";
1174 return "expected op with 2 inputs and 1 output";
1176 return "unexpected input index map for convolutions";
1178 return "expected output/filter indexing maps to be projected permutations";
1180 return "unexpected loop dimension for convolution op";
1182 return "expected all iterators used to access outputs to be parallel";
1184 return "expected all iterators not used to access outputs to be reduction";
1186 return "expected convolved dim to be non-empty";
1190 llvm_unreachable(
"unhandled MatchConvolutionResult case");
1194 bool allowEmptyConvolvedDims) {
1196 linalgOp.getOperation(),
nullptr, allowEmptyConvolvedDims) ==
1212enum class MatchFillResult {
1222 auto linalgOp = dyn_cast<linalg::LinalgOp>(op);
1224 return MatchFillResult::NotLinalgOp;
1225 if (linalgOp.getNumDpsInputs() != 1 || linalgOp.getNumDpsInits() != 1)
1226 return MatchFillResult::WrongNumOperands;
1228 OpOperand *value = linalgOp.getDpsInputOperand(0);
1229 if (!linalgOp.isScalar(value))
1230 return MatchFillResult::NotScalarInput;
1233 OpOperand *output = linalgOp.getDpsInitOperand(0);
1236 if (scalarType != outputElementType)
1237 return MatchFillResult::TypeMismatch;
1239 return MatchFillResult::Success;
1244 if (res == MatchFillResult::NotLinalgOp)
1245 return op->
emitError(
"expected a LinalgOp");
1246 if (res == MatchFillResult::WrongNumOperands)
1247 return op->
emitError(
"expected op with 1 input and 1 output");
1248 if (res == MatchFillResult::NotScalarInput)
1249 return op->
emitError(
"expected op with scalar input");
1250 if (res == MatchFillResult::TypeMismatch) {
1251 auto linalgOp = cast<linalg::LinalgOp>(op);
1252 Type scalarType = linalgOp.getDpsInputOperand(0)->get().getType();
1253 Type outputElementType =
1255 return op->
emitOpError(
"expected fill value type (")
1256 << scalarType <<
") to match output element type ("
1257 << outputElementType <<
")";
1270 for (
OpOperand &opOperand : getOperation()->getOpOperands()) {
1271 for (
int64_t i = 0, e = getRank(&opOperand); i < e; ++i)
1279 assert(!hasDynamicShape() &&
"expected operands to have static shapes");
1280 for (
OpOperand &opOperand : getOperation()->getOpOperands())
1281 llvm::append_range(res,
getShape(&opOperand));
1286 AffineMap map = getLoopsToShapesMap();
1288 auto viewSizes = createFlatListOfOperandDims(
b, loc);
1289 SmallVector<Range, 4> res(numDims);
1290 for (
unsigned idx = 0; idx < numRes; ++idx) {
1292 if (
auto d = dyn_cast<AffineDimExpr>(
result)) {
1293 if (res[d.getPosition()].offset)
1295 res[d.getPosition()] =
1296 Range{
b.getIndexAttr(0), viewSizes[idx],
b.getIndexAttr(1)};
1307 : positions(std::move(positions)) {}
1322 llvm::SmallBitVector positions;
1325static std::pair<int64_t, int64_t>
1329 for (
OpOperand *input : op.getDpsInputOperands())
1330 inputRankSum += op.getRank(input);
1331 for (
OpOperand &output : op.getDpsInitsMutable())
1332 outputRankSum += op.getRank(&output);
1333 return {inputRankSum, inputRankSum + outputRankSum};
1348 AffineMap loopsToShapesMap = getLoopsToShapesMap();
1356 AffineMap loopToResultsShapeMap = loopsToShapesMap.
getSliceMap(
1357 resultShapesSubMapPos.first,
1358 resultShapesSubMapPos.second - resultShapesSubMapPos.first);
1359 AffineMap resultShapesFromInputShapesMap =
1360 loopToResultsShapeMap.
compose(getShapesToLoopsMap());
1364 llvm::SmallBitVector outputDims(resultShapesFromInputShapesMap.
getNumDims());
1365 outputDims.set(resultShapesSubMapPos.first, resultShapesSubMapPos.second);
1366 HasAffineDimExprVisitor checkDimExpr(std::move(outputDims));
1367 Location loc = getOperation()->getLoc();
1368 IRRewriter rewriter(
b);
1369 SmallVector<OpFoldResult> allResultDimValues =
1370 affine::makeComposedFoldedMultiResultAffineApply(
1371 rewriter, loc, resultShapesFromInputShapesMap,
1372 createFlatListOfOperandDims(
b, loc));
1374 ArrayRef<AffineExpr> shapeExprs = resultShapesFromInputShapesMap.
getResults();
1375 for (OpOperand &opOperand : getDpsInitsMutable()) {
1376 SmallVector<OpFoldResult> shapes;
1377 for (int64_t dim : llvm::seq<int64_t>(0, getRank(&opOperand))) {
1378 auto shapedType = llvm::cast<ShapedType>(opOperand.get().getType());
1379 if (!shapedType.isDynamicDim(dim)) {
1381 shapes.push_back(
b.getIndexAttr(shapedType.getDimSize(dim)));
1384 OpFoldResult ofr = checkDimExpr.visit(shapeExprs[pos])
1386 : allResultDimValues[pos];
1391 reifiedReturnShapes.emplace_back(std::move(shapes));
1400 auto dpsIface = cast<DestinationStyleOpInterface>(*this->getOperation());
1401 if (!dpsIface.isDpsInput(opOperand))
1402 return operandNumber;
1403 unsigned start = dpsIface.getDpsInits().getBeginOperandIndex();
1404 assert(!dpsIface.isDpsInit(opOperand));
1407 return cast<DestinationStyleOpInterface>(*this->getOperation())
1408 .getNumDpsInputs() +
1409 operandNumber - start;
1413 LinalgOp linalgOp = cast<LinalgOp>(op);
1415 if (!linalgOp.hasPureTensorSemantics() &&
1417 return op->
emitOpError(
"expected to have pure tensor or buffer semantics");
1421 if (linalgOp.hasDynamicIndexingMaps())
1422 if (failed(linalgOp.verifyIndexingMapRequiredAttributes()))
1426 if (failed(cast<IndexingMapOpInterface>(op).verifyImpl()))
1431 for (
OpOperand &opOperand : linalgOp->getOpOperands()) {
1432 AffineMap indexingMap = linalgOp.getMatchingIndexingMap(&opOperand);
1434 unsigned numLoops = linalgOp.getNumLoops();
1438 <<
" dim(s) to match the number of loops";
1441 linalgOp.getReductionDims(redDims);
1443 if (!linalgOp.getShapesToLoopsMap())
1444 return op->
emitOpError(
"expected the shape-to-loops map to be non-null");
1447 if (linalgOp->getNumRegions() != 1 || !linalgOp->getRegion(0).hasOneBlock())
1448 return op->
emitOpError(
"expects to have 1 region with 1 block");
1456 Block &block = linalgOp->getRegion(0).front();
1458 if (linalgOp.getOpOperandsMatchingBBargs().size() != block.
getNumArguments())
1459 return op->
emitOpError(
"expected as many non-induction variable region "
1460 "arguments as the number of input/output operands");
1462 for (
OpOperand *opOperand : linalgOp.getOpOperandsMatchingBBargs()) {
1464 if (isa<MemRefType, RankedTensorType>(elementType))
1467 if (elementType != argType)
1468 return op->
emitOpError(
"expected type of bb argument #")
1470 <<
" to match element or self type of the corresponding operand ("
1471 << elementType <<
")";
static FailureOr< ContractionDimensions > inferContractionDimsImpl(ArrayRef< AffineMap > indexingMaps, ArrayRef< utils::IteratorType > iterators)
Find 2 parallel (m and n) and 1 reduction (k) dimension candidates that form a matmul subcomputation ...
static Value getSourceSkipUnary(Value value)
If the value is defined by a chain of unary side effect-free, go up the use-def chain until the first...
static llvm::SmallDenseSet< int64_t > getPreservedDims(AffineMap map)
static T getAffineExprOfType(AffineExpr lhs, AffineExpr rhs)
Of the given two expressions returns one that is of type T (lhs gets preference over rhs)
static FailureOr< ConvolutionDimensions > inferConvolutionDimsImpl(ArrayRef< AffineMap > indexingMaps, ArrayRef< utils::IteratorType > iterators, ConvAccessExprWalker &inputExprWalker, bool allowEmptyConvolvedDims, DenseIntElementsAttr nativeStrides, DenseIntElementsAttr nativeDilations)
Classifies dimensions in the indexingMaps used by a convolution subcomputation, as captured by inputE...
static std::pair< int64_t, int64_t > getResultsPositionInLoopsToShapeMap(LinalgOp &op)
static bool isPairTemplateImpl(Operation *add, Operation *mul)
Returns true if the two operations are of the kinds specified by a pair of consecutive template argum...
static bool isaElemwiseSingleOpInterface(linalg::GenericOp op, unsigned arity)
static MatchFillResult isFillInterfaceImpl(Operation *op)
static bool isContractionBody(Block &block)
Returns true if the block is a body of a contraction with the kinds of operations given pairwise by t...
static std::optional< Value > isaExternalFillOp(GenericOp op)
Detects if a linalg.generic operation represents an external scalar input.
static FailureOr< SmallVector< utils::IteratorType > > inferIteratorsFromOutMap(AffineMap map)
Infer the iterator types from the init affine map.
static DenseIntElementsAttr getInherentConvolutionAttr(LinalgOp linalgOp, StringRef name)
static llvm::SmallDenseSet< int64_t > findPermutationsIndexingOperand(AffineMap indexingMap, ArrayRef< utils::IteratorType > iterators, utils::IteratorType iter)
Given an indexingMap and its corresponding iterators, returns the positions of the iterators of type ...
static SmallVector< int64_t, 2 > getConstantsFromExprList(const SmallVector< AffineExpr, 2 > &exprs)
static std::optional< Value > isaInlinedFillOp(GenericOp op)
Detects if a linalg.generic operation represents a fill with an inlined constant.
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Affine binary operation expression.
AffineExpr getLHS() const
AffineExpr getRHS() const
An integer constant appearing in affine expression.
A dimensional identifier appearing in an affine expression.
unsigned getPosition() const
bool visit(AffineExpr expr)
See documentation for AffineExprVisitorBase.
Base type for affine expression.
AffineExprKind getKind() const
Return the classification for this type.
MLIRContext * getContext() const
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
AffineMap dropResults(ArrayRef< int64_t > positions) const
AffineMap getSliceMap(unsigned start, unsigned length) const
Returns the map consisting of length expressions starting from start.
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
A symbolic identifier appearing in an affine expression.
Attributes are known-constant values of operations.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
OpListType & getOperations()
Operation * getTerminator()
Get the terminator operation of this block.
An attribute that represents a reference to a dense integer vector or tensor object.
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
This class provides the API for ops that are known to be terminators.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
bool mightHaveTrait()
Returns true if the operation might have the provided trait.
unsigned getNumOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
unsigned getNumResults()
Return the number of results held by this operation.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isInteger() const
Return true if this is an integer type (with the specified width).
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.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
MatchConvolutionResult isConvolutionInterfaceImpl(Operation *op, ConvolutionDimensions *dimensions=nullptr, bool allowEmptyConvolvedDims=false)
Checks whether op conforms to ConvolutionOpInterface and populates dimensions with indexes of the dif...
@ NotProjectedPermutations
bool isContractionBody(Block &block, function_ref< bool(Operation *, Operation *)> isaPair, llvm::raw_ostream &errs=mlir::thread_safe_nulls())
Returns true if the block contains a contraction of the following form:
StringRef getMatchConvolutionMessage(MatchConvolutionResult res)
Returns the error message corresponding to the convolution checking return code.
bool canOpOperandsBeDroppedImpl(linalg::LinalgOp linalgOp, ArrayRef< OpOperand * > droppedOperands)
Implementation of the method that check if given operands can be dropped, i.e.
MatchContractionResult isContractionInterfaceImpl(Operation *op, ContractionDimensions *dimensions=nullptr)
Checks whether op conforms to ContractionOpInterface and populates dimensions with indexes of the dif...
LogicalResult verifyContractionInterface(Operation *op)
Verify that op conforms to ContractionOpInterface.
@ NotProjectedPermutations
@ NonOutputDimNotReduction
LogicalResult verifyFillInterface(Operation *op)
Verify that op conforms to the FillOpInterface.
StringRef getMatchContractionMessage(MatchContractionResult res)
Returns the error message corresponding to the contraction checking return code.
LogicalResult verifyStructuredOpInterface(Operation *op)
Verify that op conforms to the invariants of StructuredOpInterface.
LogicalResult verifyConvolutionInterface(Operation *op)
Verify that op conforms to the ConvolutionOpInterface.
std::optional< SmallVector< int64_t > > isaTransposeOpInterface(GenericOp genericOp)
Checks whether genericOp is semantically equivalent to a linalg.transpose.
bool isaElemwiseSingleUnaryOpInterface(GenericOp genericOp)
Checks whether a given genericOp is semantically equivalent to a single linalg elementwise unary op,...
bool isaCopyOpInterface(LinalgOp linalgOp)
Checks whether linalgOp is semantically equivalent to a linalg.copyOp.
std::optional< SmallVector< int64_t > > isaReductionOpInterface(GenericOp genericOp)
Checks whether the genericOp is semantically equivalent to a linalg.reduce.
FailureOr< ConvolutionDimensions > inferConvolutionDims(LinalgOp linalgOp)
Find at least 1 parallel (output_image) and reduction (filter_loop) dimension candidates that form a ...
OpFoldResult createFoldedDimOp(OpBuilder &b, Location loc, Value val, int64_t dim)
Create one memref::DimOp or tensor::DimOp depending on the type of val.
bool isaConvolutionOpInterface(LinalgOp linalgOp, bool allowEmptyConvolvedDims=false)
Checks whether linalgOp conforms to ConvolutionOpInterface.
bool isaElemwiseSingleTernaryOpInterface(GenericOp genericOp)
Checks whether genericOp is semantically equivalent to a single linalg elementwise ternary op e....
std::optional< SmallVector< int64_t > > isaBroadcastOpInterface(LinalgOp linalgOp)
Checks whether linalgOp is semantically equivalent to a broadcast operation.
FailureOr< ContractionDimensions > inferContractionDims(LinalgOp linalgOp)
Find at least 2 parallel (m and n) and 1 reduction (k) dimension candidates that form a matmul subcom...
Value createOrFoldDimOp(OpBuilder &b, Location loc, Value val, int64_t dim)
Create one memref::DimOp or tensor::DimOp depending on the type of val.
bool isaContractionOpInterface(LinalgOp linalgOp)
Checks whether linalgOp conforms to ContractionOpInterface.
std::optional< Value > isaFillOpInterface(GenericOp genericOp)
Checks whether genericOp is semantically equivalent to a linalg.fill.
bool isaElemwiseSingleBinaryOpInterface(GenericOp genericOp)
Checks whether genericOp is semantically equivalent to a single linalg elementwise binary op e....
Include the generated interface declarations.
AffineMap concatAffineMaps(ArrayRef< AffineMap > maps, MLIRContext *context)
Concatenates a list of maps into a single AffineMap, stepping over potentially empty maps.
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
AffineExpr getAffineConstantExpr(int64_t constant, MLIRContext *context)
llvm::function_ref< Fn > function_ref
HasAffineDimExprVisitor(llvm::SmallBitVector positions)
bool visitDimExpr(AffineDimExpr dimExpr)
bool visitAffineBinaryOpExpr(AffineBinaryOpExpr binaryOpExpr)
bool visitSymbolExpr(AffineSymbolExpr symbolExpr)
bool visitConstantExpr(AffineConstantExpr constExpr)
Positions of a Linalg op loops that correspond to different kinds of a contraction dimension.
SmallVector< unsigned, 2 > batch
SmallVector< unsigned, 2 > m
SmallVector< unsigned, 2 > n
SmallVector< unsigned, 2 > k
Positions of a Linalg op loops that correspond to different kinds of a convolution dimension.
SmallVector< unsigned, 2 > depth
SmallVector< unsigned, 2 > outputImage
SmallVector< unsigned, 2 > outputChannel
SmallVector< int64_t, 2 > dilations
SmallVector< int64_t, 2 > strides
SmallVector< unsigned, 2 > inputChannel
SmallVector< unsigned, 2 > batch
SmallVector< unsigned, 2 > filterLoop