26#define DEBUG_TYPE "vector-contract-lowering"
48 for (
const auto &it : llvm::enumerate(iteratorTypes)) {
52 results.push_back(it.value());
69 results.push_back(targetExpr);
94 return vector::ExtractOp::create(rewriter, loc, val, pos);
97 VectorType type = cast<VectorType>(val.
getType());
99 Value result = arith::ConstantOp::create(rewriter, loc, resType,
101 for (
int64_t d = 0, e = resType.getDimSize(0); d < e; d++) {
102 Value ext = vector::ExtractOp::create(rewriter, loc, val, d);
127 return vector::InsertOp::create(rewriter, loc, val,
result, pos);
130 VectorType type = cast<VectorType>(
result.getType());
131 for (
int64_t d = 0, e = type.getDimSize(0); d < e; d++) {
132 Value ext = vector::ExtractOp::create(rewriter, loc,
result, d);
133 Value ins = vector::ExtractOp::create(rewriter, loc, val, d);
135 result = vector::InsertOp::create(rewriter, loc, sto,
result, d);
141static std::optional<Value>
145 arith::FastMathFlagsAttr fmf = {}) {
146 using vector::CombiningKind;
150 if (kind == CombiningKind::MINNUMF || kind == CombiningKind::MAXNUMF ||
151 kind == CombiningKind::MINIMUMF || kind == CombiningKind::MAXIMUMF)
154 mul = arith::MulIOp::create(rewriter, loc, x, y);
157 if (kind == CombiningKind::AND || kind == CombiningKind::MINUI ||
158 kind == CombiningKind::MINSI || kind == CombiningKind::MAXUI ||
159 kind == CombiningKind::MAXSI || kind == CombiningKind::OR ||
160 kind == CombiningKind::XOR)
164 if (
acc && isa<VectorType>(
acc.getType()) && kind == CombiningKind::ADD) {
165 Value fma = vector::FMAOp::create(rewriter, loc, x, y,
acc);
172 mul = arith::MulFOp::create(rewriter, loc, x, y, fmf);
176 return std::optional<Value>(
mul);
187 dimsIdx.push_back(i);
206 arith::FastMathFlagsAttr fmf = {}) {
208 return arith::AddIOp::create(rewriter, loc, x, y);
209 return arith::AddFOp::create(rewriter, loc, x, y, fmf);
216 arith::FastMathFlagsAttr fmf = {}) {
218 return arith::MulIOp::create(rewriter, loc, x, y);
219 return arith::MulFOp::create(rewriter, loc, x, y, fmf);
239class ContractionOpToOuterProductOpLowering
242 using MaskableOpRewritePattern::MaskableOpRewritePattern;
244 using FilterConstraintType =
245 std::function<LogicalResult(vector::ContractionOp op)>;
247 static LogicalResult defaultFilter(vector::ContractionOp op) {
251 ContractionOpToOuterProductOpLowering(
252 vector::VectorContractLowering vectorContractLowering,
253 MLIRContext *context, PatternBenefit benefit = 1,
254 FilterConstraintType constraint = defaultFilter)
255 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit),
256 vectorContractLowering(vectorContractLowering),
257 filter(std::move(constraint)) {}
261 PatternRewriter &rewriter)
const override;
265 vector::VectorContractLowering vectorContractLowering;
266 FilterConstraintType filter;
287class ContractionOpToDotLowering
290 using MaskableOpRewritePattern::MaskableOpRewritePattern;
292 using FilterConstraintType =
293 std::function<LogicalResult(vector::ContractionOp op)>;
295 static LogicalResult defaultFilter(vector::ContractionOp op) {
299 ContractionOpToDotLowering(
300 vector::VectorContractLowering vectorContractLowering,
301 MLIRContext *context, PatternBenefit benefit = 1,
302 const FilterConstraintType &constraint = defaultFilter)
303 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit),
304 vectorContractLowering(vectorContractLowering), filter(defaultFilter) {}
308 PatternRewriter &rewriter)
const override;
312 vector::VectorContractLowering vectorContractLowering;
313 FilterConstraintType filter;
330class ContractionOpLowering
333 using MaskableOpRewritePattern::MaskableOpRewritePattern;
334 using FilterConstraintType =
335 std::function<LogicalResult(vector::ContractionOp op)>;
337 static LogicalResult defaultFilter(vector::ContractionOp op) {
341 ContractionOpLowering(
342 vector::VectorContractLowering vectorContractLoweringOption,
343 MLIRContext *context, PatternBenefit benefit = 1,
344 FilterConstraintType constraint = defaultFilter)
345 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit),
346 vectorContractLoweringOption(vectorContractLoweringOption),
347 filter(std::move(constraint)) {}
351 PatternRewriter &rewriter)
const override;
355 vector::VectorContractLowering vectorContractLoweringOption;
356 FilterConstraintType filter;
358 FailureOr<Value> lowerParallel(PatternRewriter &rewriter,
359 vector::ContractionOp op, int64_t lhsIndex,
360 int64_t rhsIndex, Value mask)
const;
362 FailureOr<Value> lowerReduction(PatternRewriter &rewriter,
363 vector::ContractionOp op, Value mask)
const;
368struct UnrolledOuterProductGenerator
370 UnrolledOuterProductGenerator(RewriterBase &
b, vector::ContractionOp op)
371 : StructuredGenerator<vector::ContractionOp, vector::IteratorType>(
b, op),
372 kind(op.getKind()),
lhs(op.getLhs()),
rhs(op.getRhs()),
373 res(op.getAcc()), lhsType(op.getLhsType()) {
374 auto maskableOp = cast<MaskableOpInterface>(op.getOperation());
375 if (maskableOp.isMasked())
376 mask = maskableOp.getMaskingOp().getMask();
379 Value t(Value v, ArrayRef<int64_t> perm = {1, 0}) {
382 return vector::TransposeOp::create(rewriter, loc, v, perm);
385 Value
promote(Value v, Type dstElementType) {
386 Type elementType = v.
getType();
387 auto vecType = dyn_cast<VectorType>(elementType);
389 elementType = vecType.getElementType();
390 if (elementType == dstElementType)
392 Type promotedType = dstElementType;
394 promotedType = vecType.clone(promotedType);
395 if (isa<FloatType>(dstElementType))
396 return arith::ExtFOp::create(rewriter, loc, promotedType, v,
398 return arith::ExtSIOp::create(rewriter, loc, promotedType, v);
402 VectorType lhsType,
int reductionSize,
403 std::optional<Value> maybeMask = std::nullopt) {
405 if (mask && !maybeMask.has_value())
408 Type resElementType = cast<VectorType>(res.getType()).getElementType();
409 for (
int64_t k = 0; k < reductionSize; ++k) {
410 Value extractA = vector::ExtractOp::create(rewriter, loc, lhs, k);
411 Value extractB = vector::ExtractOp::create(rewriter, loc, rhs, k);
412 extractA = promote(extractA, resElementType);
413 extractB = promote(extractB, resElementType);
415 if (maybeMask.has_value() && maybeMask.value())
417 vector::ExtractOp::create(rewriter, loc, maybeMask.value(), k);
419 Operation *outerProdOp = vector::OuterProductOp::create(
420 rewriter, loc, res.getType(), extractA, extractB, res, kind);
432 if (vecType.getScalableDims()[reductionDim])
434 int64_t reductionSize = vecType.getDimSize(reductionDim);
435 assert(reductionSize > 0 &&
436 "Reduction dim must be a known static size to allow unrolling");
437 return reductionSize;
442 if (!
iters({Par(), Par(), Red()}))
449 if (
layout({{m, k}, {k, n}, {m, n}})) {
454 Value tMask = t(mask, {2, 0, 1});
455 return outerProd(tLhs, rhs, res, lhsType, *reductionSize, tMask);
459 if (
layout({{m, k}, {n, k}, {m, n}})) {
463 Value tMask = t(mask, {2, 0, 1});
464 return outerProd(tLhs, tRhs, res, lhsType, *reductionSize, tMask);
468 if (
layout({{k, m}, {k, n}, {m, n}})) {
470 Value tMask = t(mask, {2, 0, 1});
471 return outerProd(lhs, rhs, res, lhsType, *reductionSize, tMask);
475 if (
layout({{k, m}, {n, k}, {m, n}})) {
478 Value tMask = t(mask, {2, 0, 1});
479 return outerProd(lhs, tRhs, res, lhsType, *reductionSize, tMask);
484 if (
layout({{m, k}, {k, n}, {n, m}})) {
487 Value tMask = t(mask, {2, 0, 1});
488 return outerProd(rhs, tLhs, res, lhsType, *reductionSize, tMask);
492 if (
layout({{m, k}, {n, k}, {n, m}})) {
496 Value tMask = t(mask, {2, 0, 1});
497 return outerProd(tRhs, tLhs, res, lhsType, *reductionSize, tMask);
500 if (
layout({{k, m}, {k, n}, {n, m}})) {
502 Value tMask = t(mask, {2, 0, 1});
503 return outerProd(rhs, lhs, res, lhsType, *reductionSize, tMask);
506 if (
layout({{k, m}, {n, k}, {n, m}})) {
509 Value tMask = t(mask, {2, 0, 1});
510 return outerProd(tRhs, lhs, res, lhsType, *reductionSize, tMask);
522 if (!
iters({Par(), Red()}))
528 if (
layout({{m, k}, {k}, {m}})) {
531 Value tMask = t(mask);
532 return outerProd(tLhs, rhs, res, lhsType, *reductionSize, tMask);
536 if (
layout({{k, m}, {k}, {m}})) {
538 Value tMask = t(mask);
539 return outerProd(lhs, rhs, res, lhsType, *reductionSize, tMask);
543 if (
layout({{k}, {m, k}, {m}})) {
546 Value tMask = t(mask);
547 return outerProd(tRhs, lhs, res, lhsType, *reductionSize, tMask);
551 if (
layout({{k}, {k, m}, {m}})) {
553 Value tMask = t(mask);
554 return outerProd(rhs, lhs, res, lhsType, *reductionSize, tMask);
565 if (!
iters({Red(), Par()}))
571 if (
layout({{m, k}, {k}, {m}}))
573 return outerProd(t(lhs), rhs, res, lhsType, *reductionSize, mask);
575 if (
layout({{k, m}, {k}, {m}}))
577 return outerProd(lhs, rhs, res, lhsType, *reductionSize, mask);
579 if (
layout({{k}, {m, k}, {m}}))
581 return outerProd(t(rhs), lhs, res, lhsType, *reductionSize, mask);
583 if (
layout({{k}, {k, m}, {m}}))
585 return outerProd(rhs, lhs, res, lhsType, *reductionSize, mask);
590 vector::CombiningKind kind;
591 Value
lhs,
rhs, res, mask;
611ContractionOpToOuterProductOpLowering::matchAndRewriteMaskableOp(
612 vector::ContractionOp op, MaskingOpInterface maskOp,
614 if (vectorContractLowering != vector::VectorContractLowering::OuterProduct)
620 UnrolledOuterProductGenerator e(rewriter, op);
621 FailureOr<Value> matmatRes = e.matmat();
622 if (succeeded(matmatRes)) {
625 FailureOr<Value> matvecRes = e.matvec();
626 if (succeeded(matvecRes)) {
630 FailureOr<Value> tmatvecRes = e.tmatvec();
634FailureOr<Value> ContractionOpToDotLowering::matchAndRewriteMaskableOp(
635 vector::ContractionOp op, MaskingOpInterface maskOp,
644 if (vectorContractLowering != vector::VectorContractLowering::Dot)
647 auto iteratorTypes = op.getIteratorTypes().getValue();
648 static constexpr std::array<int64_t, 2> perm = {1, 0};
650 Value lhs = op.getLhs(), rhs = op.getRhs();
653 auto infer = [&](MapList m) {
669 if (maps == infer({{m, k}, {k, n}, {m, n}})) {
670 rhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
671 }
else if (maps == infer({{m, k}, {n, k}, {m, n}})) {
673 }
else if (maps == infer({{k, m}, {k, n}, {m, n}})) {
674 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
675 rhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
676 }
else if (maps == infer({{k, m}, {n, k}, {m, n}})) {
677 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
678 }
else if (maps == infer({{m, k}, {k, n}, {n, m}})) {
681 lhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
683 }
else if (maps == infer({{m, k}, {n, k}, {n, m}})) {
685 }
else if (maps == infer({{k, m}, {k, n}, {n, m}})) {
687 lhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
688 rhs = vector::TransposeOp::create(rewriter, loc, tmp, perm);
689 }
else if (maps == infer({{k, m}, {n, k}, {n, m}})) {
691 rhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
701 if (maps == infer({{m, n}, {n}, {m}})) {
703 }
else if (maps == infer({{n, m}, {n}, {m}})) {
704 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
705 }
else if (maps == infer({{n}, {m, n}, {m}})) {
707 }
else if (maps == infer({{n}, {n, m}, {m}})) {
709 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
717 VectorType dstType = cast<VectorType>(op.getResultType());
718 assert(dstType.getRank() >= 1 && dstType.getRank() <= 2 &&
719 "Expected dst type of rank 1 or 2");
721 unsigned rank = dstType.getRank();
722 unsigned dstRows = dstType.getShape()[0];
723 unsigned dstColumns = rank == 1 ? 1 : dstType.getShape()[1];
726 Value res = arith::ConstantOp::create(rewriter, loc, dstType,
728 bool isInt = isa<IntegerType>(dstType.getElementType());
729 arith::FastMathFlagsAttr fmf = op.getFastmathAttr();
731 extractedCols.reserve(dstColumns);
732 for (
unsigned r = 0; r < dstRows; ++r) {
733 Value rowLhs = vector::ExtractOp::create(rewriter, op.getLoc(), lhs, r);
734 for (
unsigned c = 0; c < dstColumns; ++c) {
741 : vector::ExtractOp::create(rewriter, op.getLoc(), rhs, c);
742 extractedCols.push_back(colRhs);
744 Value extractedColRhs = extractedCols[c];
746 createMul(op.getLoc(), rowLhs, extractedColRhs, isInt, rewriter, fmf);
747 Value sum = vector::ReductionOp::create(rewriter, op.getLoc(),
748 vector::CombiningKind::ADD,
753 res = vector::InsertOp::create(rewriter, op.getLoc(), sum, res, pos);
756 if (
auto acc = op.getAcc())
757 res =
createAdd(op.getLoc(), res,
acc, isInt, rewriter, fmf);
763struct ContractOpToElementwise
765 using MaskableOpRewritePattern::MaskableOpRewritePattern;
766 using FilterConstraintType =
767 std::function<LogicalResult(vector::ContractionOp op)>;
768 static LogicalResult defaultFilter(vector::ContractionOp op) {
771 ContractOpToElementwise(
772 vector::VectorContractLowering vectorContractLowering,
773 MLIRContext *context, PatternBenefit benefit = 1,
774 const FilterConstraintType &constraint = defaultFilter)
775 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit),
776 vectorContractLowering(vectorContractLowering), filter(defaultFilter) {}
779 matchAndRewriteMaskableOp(vector::ContractionOp contractOp,
780 MaskingOpInterface maskOp,
781 PatternRewriter &rewriter)
const override {
786 if (
failed(filter(contractOp)))
789 if (vectorContractLowering != vector::VectorContractLowering::ParallelArith)
792 ArrayRef<int64_t> lhsShape = contractOp.getLhsType().getShape();
793 ArrayRef<int64_t> rhsShape = contractOp.getRhsType().getShape();
794 AffineMap lhsMap = contractOp.getIndexingMapsArray()[0];
795 AffineMap rhsMap = contractOp.getIndexingMapsArray()[1];
796 SmallVector<int64_t> lhsReductionDims =
798 SmallVector<int64_t> rhsReductionDims =
801 for (int64_t dim : lhsReductionDims) {
802 if (lhsShape[dim] != 1)
805 for (int64_t dim : rhsReductionDims) {
806 if (rhsShape[dim] != 1)
809 AffineMap accMap = contractOp.getIndexingMapsArray()[2];
811 unsigned numLhsDimToBroadcast =
812 numParallelDims - (lhsMap.
getNumResults() - lhsReductionDims.size());
813 unsigned numRhsDimToBroadcast =
814 numParallelDims - (rhsMap.
getNumResults() - rhsReductionDims.size());
815 SmallVector<int64_t> lhsDims;
816 SmallVector<int64_t> lhsTranspose;
817 SmallVector<int64_t> rhsDims;
818 SmallVector<int64_t> rhsTranspose;
819 for (int64_t dim : lhsReductionDims)
820 lhsTranspose.push_back(numLhsDimToBroadcast + dim);
821 for (int64_t dim : rhsReductionDims)
822 rhsTranspose.push_back(numRhsDimToBroadcast + dim);
825 for (
unsigned i = 0; i < numParallelDims; i++) {
826 std::optional<unsigned> lhsDim =
829 lhsTranspose.push_back(numLhsDimToBroadcast + *lhsDim);
833 cast<VectorType>(contractOp.getResultType()).getDimSize(i));
834 lhsTranspose.push_back(lhsDims.size() - 1);
836 std::optional<unsigned> rhsDim =
839 rhsTranspose.push_back(numRhsDimToBroadcast + *rhsDim);
843 cast<VectorType>(contractOp.getResultType()).getDimSize(i));
844 rhsTranspose.push_back(rhsDims.size() - 1);
847 Value newLhs = contractOp.getLhs();
848 Value newRhs = contractOp.getRhs();
849 Location loc = contractOp.getLoc();
850 if (!lhsDims.empty()) {
851 lhsDims.append(lhsShape.begin(), lhsShape.end());
853 VectorType::get(lhsDims, contractOp.getLhsType().getElementType());
854 newLhs = vector::BroadcastOp::create(rewriter, loc, expandedType, newLhs);
856 if (!rhsDims.empty()) {
857 rhsDims.append(rhsShape.begin(), rhsShape.end());
859 VectorType::get(rhsDims, contractOp.getRhsType().getElementType());
860 newRhs = vector::BroadcastOp::create(rewriter, loc, expandedType, newRhs);
862 bool isInt = contractOp.getLhsType().getElementType().isIntOrIndex();
863 newLhs = vector::TransposeOp::create(rewriter, loc, newLhs, lhsTranspose);
864 newRhs = vector::TransposeOp::create(rewriter, loc, newRhs, rhsTranspose);
865 SmallVector<int64_t> lhsOffsets(lhsReductionDims.size(), 0);
866 SmallVector<int64_t> rhsOffsets(rhsReductionDims.size(), 0);
867 newLhs = vector::ExtractOp::create(rewriter, loc, newLhs, lhsOffsets);
868 newRhs = vector::ExtractOp::create(rewriter, loc, newRhs, rhsOffsets);
869 std::optional<Value>
result =
871 contractOp.getKind(), rewriter, isInt,
872 Value(), contractOp.getFastmathAttr());
881 vector::VectorContractLowering vectorContractLowering;
882 FilterConstraintType filter;
902FailureOr<Value> ContractionOpLowering::matchAndRewriteMaskableOp(
903 vector::ContractionOp op, MaskingOpInterface maskOp,
909 if (op.getLhsType().getElementType() !=
916 if (op.getKind() != vector::CombiningKind::ADD) {
918 op,
"contractions other than 'add' not supported");
924 ContractionOpToOuterProductOpLowering pat1(vectorContractLoweringOption, ctx);
925 FailureOr<Value> newVal1 =
926 pat1.matchAndRewriteMaskableOp(op, maskOp, rewriter);
930 ContractionOpToDotLowering pat2(vectorContractLoweringOption, ctx);
931 FailureOr<Value> newVal2 =
932 pat2.matchAndRewriteMaskableOp(op, maskOp, rewriter);
936 ContractOpToElementwise pat4(vectorContractLoweringOption, ctx);
937 FailureOr<Value> newVal4 =
938 pat4.matchAndRewriteMaskableOp(op, maskOp, rewriter);
946 mask = maskOp.getMask();
948 std::vector<std::pair<int64_t, int64_t>> batchDimMap = op.getBatchDimMap();
949 if (!batchDimMap.empty()) {
950 int64_t lhsIndex = batchDimMap[0].first;
951 int64_t rhsIndex = batchDimMap[0].second;
952 auto newOp = lowerParallel(rewriter, op, lhsIndex, rhsIndex, mask);
959 std::vector<std::pair<int64_t, int64_t>> contractingDimMap =
960 op.getContractingDimMap();
963 for (
auto &dimPair : contractingDimMap) {
964 lhsContractingDimSet.insert(dimPair.first);
965 rhsContractingDimSet.insert(dimPair.second);
969 VectorType lhsType = op.getLhsType();
970 for (
int64_t lhsIndex = 0, e = lhsType.getRank(); lhsIndex < e; ++lhsIndex) {
971 if (lhsContractingDimSet.count(lhsIndex) == 0) {
972 auto newOp = lowerParallel(rewriter, op, lhsIndex, -1, mask);
980 VectorType rhsType = op.getRhsType();
981 for (
int64_t rhsIndex = 0, e = rhsType.getRank(); rhsIndex < e; ++rhsIndex) {
982 if (rhsContractingDimSet.count(rhsIndex) == 0) {
983 auto newOp = lowerParallel(rewriter, op, -1, rhsIndex, mask);
991 if (!contractingDimMap.empty()) {
992 auto newOp = lowerReduction(rewriter, op, mask);
1004FailureOr<Value> ContractionOpLowering::lowerParallel(
PatternRewriter &rewriter,
1005 vector::ContractionOp op,
1009 VectorType lhsType = op.getLhsType();
1010 VectorType rhsType = op.getRhsType();
1011 VectorType resType = cast<VectorType>(op.getResultType());
1016 if (lhsIndex >= 0) {
1017 iterIndex = iMap[0].getDimPosition(lhsIndex);
1018 if (rhsIndex >= 0 && iterIndex != iMap[1].
getDimPosition(rhsIndex))
1020 diag <<
"expected lhsIndex=" << lhsIndex <<
" and rhsIndex=" << rhsIndex
1021 <<
" to map to the same dimension";
1023 if (lhsType.getScalableDims()[lhsIndex])
1025 diag <<
"Unrolling scalable dimension (lhsIndex=" << lhsIndex
1026 <<
") is not supported yet";
1028 dimSize = lhsType.getDimSize(lhsIndex);
1029 }
else if (rhsIndex >= 0) {
1030 iterIndex = iMap[1].getDimPosition(rhsIndex);
1031 if (rhsType.getScalableDims()[rhsIndex])
1033 diag <<
"Unrolling scalable dimension (rhsIndex=" << rhsIndex
1034 <<
") is not supported yet";
1036 dimSize = rhsType.getDimSize(rhsIndex);
1040 diag <<
"expected either lhsIndex=" << lhsIndex
1041 <<
" or rhsIndex=" << rhsIndex <<
" to be nonnegative";
1051 if (resIndex == -1 && dimSize != 1)
1053 diag <<
"expected the dimension for iterIndex=" << iterIndex
1054 <<
" to either appear in the result map, or to be a unit dimension";
1058 std::array<AffineMap, 3> lowIndexingMaps = {
1059 adjustMap(iMap[0], iterIndex, rewriter),
1060 adjustMap(iMap[1], iterIndex, rewriter),
1061 adjustMap(iMap[2], iterIndex, rewriter)};
1067 Value result = arith::ConstantOp::create(rewriter, loc, resType,
1070 for (
int64_t d = 0; d < dimSize; ++d) {
1071 auto lhs =
reshapeLoad(loc, op.getLhs(), lhsIndex, d, rewriter);
1072 auto rhs =
reshapeLoad(loc, op.getRhs(), rhsIndex, d, rewriter);
1077 lowMask =
reshapeLoad(loc, mask, iterIndex, d, rewriter);
1080 vector::ContractionOp::create(rewriter, loc, lhs, rhs,
acc, lowAffine,
1081 lowIter, op.getKind(), op.getFastmath());
1082 lowContract =
maskOperation(rewriter, lowContract, lowMask);
1090FailureOr<Value> ContractionOpLowering::lowerReduction(
1092 auto loc = op.getLoc();
1093 VectorType lhsType = op.getLhsType();
1094 VectorType rhsType = op.getRhsType();
1095 Type resType = op.getResultType();
1096 if (isa<VectorType>(resType))
1098 "did not expect a VectorType result");
1099 bool isInt = isa<IntegerType>(resType);
1103 std::optional<int64_t> lookupLhs =
getResultIndex(iMap[0], iterIndex);
1104 std::optional<int64_t> lookupRhs =
getResultIndex(iMap[1], iterIndex);
1105 if (!lookupLhs.has_value())
1107 diag <<
"expected iterIndex=" << iterIndex <<
"to map to a LHS dimension";
1109 if (!lookupRhs.has_value())
1111 diag <<
"expected iterIndex=" << iterIndex <<
"to map to a RHS dimension";
1113 int64_t lhsIndex = *lookupLhs;
1114 int64_t rhsIndex = *lookupRhs;
1115 int64_t dimSize = lhsType.getDimSize(lhsIndex);
1116 if (dimSize != rhsType.getDimSize(rhsIndex))
1118 diag <<
"expect LHS dimension " << lhsIndex
1119 <<
" to have the same size as RHS dimension " << rhsIndex;
1122 if (lhsType.getRank() == 1) {
1123 if (rhsType.getRank() != 1)
1125 op,
"When LHS has rank 1, expected also RHS to have rank 1");
1126 arith::FastMathFlagsAttr fmf = op.getFastmathAttr();
1127 Value m = createMul(loc, op.getLhs(), op.getRhs(), isInt, rewriter, fmf);
1128 auto kind = vector::CombiningKind::ADD;
1132 acc ? vector::ReductionOp::create(rewriter, loc, kind, m,
acc,
1134 :
vector::ReductionOp::create(rewriter, loc, kind, m,
1139 std::array<AffineMap, 3> lowIndexingMaps = {
1140 adjustMap(iMap[0], iterIndex, rewriter),
1141 adjustMap(iMap[1], iterIndex, rewriter),
1142 adjustMap(iMap[2], iterIndex, rewriter)};
1151 for (
int64_t d = 0; d < dimSize; ++d) {
1152 auto lhs =
reshapeLoad(loc, op.getLhs(), lhsIndex, d, rewriter);
1153 auto rhs =
reshapeLoad(loc, op.getRhs(), rhsIndex, d, rewriter);
1156 newMask =
reshapeLoad(loc, mask, iterIndex, d, rewriter);
1158 Operation *newContract = vector::ContractionOp::create(
1159 rewriter, loc, lhs, rhs,
result, lowAffine, lowIter, op.getKind(),
1179class OuterProductOpLowering :
public OpRewritePattern<vector::OuterProductOp> {
1183 LogicalResult matchAndRewrite(vector::OuterProductOp op,
1184 PatternRewriter &rewriter)
const override {
1185 VectorType resType = op.getResultVectorType();
1186 if ((resType.getShape().size() >= 2) && resType.allDimsScalable())
1189 auto loc = op.getLoc();
1191 VectorType lhsType = op.getOperandVectorTypeLHS();
1192 VectorType rhsType = dyn_cast<VectorType>(op.getOperandTypeRHS());
1193 Type eltType = resType.getElementType();
1194 bool isInt = isa<IntegerType, IndexType>(eltType);
1195 Value acc = op.getAcc();
1196 vector::CombiningKind kind = op.getKind();
1199 OpBuilder::InsertionGuard guard(rewriter);
1200 auto maskableOp = cast<vector::MaskableOpInterface>(op.getOperation());
1203 if (maskableOp.isMasked()) {
1205 rootOp = maskableOp.getMaskingOp();
1206 mask = maskableOp.getMaskingOp().getMask();
1214 vector::BroadcastOp::create(rewriter, loc, lhsType, op.getRhs());
1216 loc, op.getLhs(),
b, acc, kind, rewriter, isInt, mask);
1217 if (!mult.has_value())
1223 Value
result = arith::ConstantOp::create(rewriter, loc, resType,
1225 for (int64_t d = 0, e = resType.getDimSize(0); d < e; ++d) {
1226 Value x = vector::ExtractOp::create(rewriter, loc, op.getLhs(), d);
1227 Value a = vector::BroadcastOp::create(rewriter, loc, rhsType, x);
1230 r = vector::ExtractOp::create(rewriter, loc, acc, d);
1233 extrMask = vector::ExtractOp::create(rewriter, loc, mask, d);
1236 loc, a, op.getRhs(), r, kind, rewriter, isInt, extrMask);
1239 result = vector::InsertOp::create(rewriter, loc, *m,
result, d);
1251 VectorContractLowering vectorContractLoweringOption,
PatternBenefit benefit,
1252 bool disableOuterProductLowering) {
1253 if (!disableOuterProductLowering)
1254 patterns.
add<OuterProductOpLowering>(patterns.
getContext(), benefit);
1255 patterns.
add<ContractionOpLowering, ContractionOpToOuterProductOpLowering>(
1256 vectorContractLoweringOption, patterns.
getContext(), benefit);
1261 patterns.
add<OuterProductOpLowering>(patterns.
getContext(), benefit);
static int64_t product(ArrayRef< int64_t > vals)
static std::optional< int64_t > getResultIndex(AffineMap map, int64_t index)
static SmallVector< int64_t > getReductionIndex(AffineMap map, ArrayAttr iteratorTypes)
Return the positions of the reductions in the given map.
static std::optional< unsigned > getDimPosition(AffineMap map, unsigned dim)
Look for a given dimension in an affine map and return its position.
static Value reshapeStore(Location loc, Value val, Value result, int64_t index, int64_t pos, PatternRewriter &rewriter)
Inserts val into result at position pos along dimension index.
static SmallVector< Attribute > adjustIter(ArrayAttr iteratorTypes, int64_t index)
FailureOr< Value > tmatvec()
static Value createAdd(Location loc, Value x, Value y, bool isInt, PatternRewriter &rewriter, arith::FastMathFlagsAttr fmf={})
Creates an AddIOp if isInt is true otherwise create an arith::AddFOp using operands x and y.
FailureOr< Value > outerProd(Value lhs, Value rhs, Value res, VectorType lhsType, int reductionSize, std::optional< Value > maybeMask=std::nullopt)
FailureOr< Value > matvec()
static AffineMap adjustMap(AffineMap map, int64_t index, PatternRewriter &rewriter)
static Value reshapeLoad(Location loc, Value val, int64_t index, int64_t pos, PatternRewriter &rewriter)
Returns val with the dimension at position index dropped by indexing that dimension with pos.
FailureOr< Value > matmat()
Two outer parallel, one inner reduction (matmat flavor).
static std::optional< Value > createContractArithOp(Location loc, Value x, Value y, Value acc, vector::CombiningKind kind, PatternRewriter &rewriter, bool isInt, Value mask=Value(), arith::FastMathFlagsAttr fmf={})
Helper to create arithmetic operation associated with a kind of contraction.
std::optional< int64_t > getReductionSize(VectorType vecType, int64_t reductionDim)
Helper function for matmat, matvec, tmatvec. Returns the size of dimension reductionDim....
static std::string diag(const llvm::Value &value)
Base type for affine expression.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
unsigned getDimPosition(unsigned idx) const
Extracts the position of the dimensional expression at the given result, when the caller knows it is ...
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumDims() const
unsigned getNumResults() const
static SmallVector< AffineMap, 4 > inferFromExprList(ArrayRef< ArrayRef< AffineExpr > > exprsList, MLIRContext *context)
Returns a vector of AffineMaps; each with as many results as exprs.size(), as many dims as the larges...
TypedAttr getZeroAttr(Type type)
ArrayAttr getArrayAttr(ArrayRef< Attribute > value)
MLIRContext * getContext() const
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation is the basic unit of execution within MLIR.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
Helper StructuredGenerator class to manipulate and rewrite ops with StructuredOpInterface.
bool iters(ArrayRef< IteratorType > its)
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
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.
This is a builder type that keeps local references to arguments.
Builder & dropDim(unsigned pos)
Erase a dim from shape @pos.
void promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
Value makeArithReduction(OpBuilder &b, Location loc, CombiningKind kind, Value v1, Value acc, arith::FastMathFlagsAttr fastmath=nullptr, Value mask=nullptr)
Returns the result value of reducing two scalar/vector values with the corresponding arith operation.
Operation * maskOperation(OpBuilder &builder, Operation *maskableOp, Value mask, Value passthru=Value())
Creates a vector.mask operation around a maskable operation.
bool isReductionIterator(Attribute attr)
Returns true if attr has "reduction" iterator type semantics.
Value selectPassthru(OpBuilder &builder, Value mask, Value newValue, Value passthru)
Creates a vector select operation that picks values from newValue or passthru for each result vector ...
bool isParallelIterator(Attribute attr)
Returns true if attr has "parallel" iterator type semantics.
void populateVectorOuterProductLoweringPatterns(RewritePatternSet &patterns, PatternBenefit benefit=1)
Populate the pattern set with the following patterns:
void populateVectorContractLoweringPatterns(RewritePatternSet &patterns, VectorContractLowering vectorContractLoweringOption, PatternBenefit benefit=1, bool disableOuterProductLowering=false)
Populate the pattern set with the following patterns:
Include the generated interface declarations.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
llvm::DenseSet< ValueT, ValueInfoT > DenseSet
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
OpRewritePattern Base
Type alias to allow derived classes to inherit constructors with using Base::Base;.
A pattern for ops that implement MaskableOpInterface and that might be masked (i.e.
virtual FailureOr< Value > matchAndRewriteMaskableOp(SourceOp sourceOp, MaskingOpInterface maskingOp, PatternRewriter &rewriter) const =0