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 ||
152 kind == CombiningKind::MINIMUMNUMF ||
153 kind == CombiningKind::MAXIMUMNUMF)
156 mul = arith::MulIOp::create(rewriter, loc, x, y);
159 if (kind == CombiningKind::AND || kind == CombiningKind::MINUI ||
160 kind == CombiningKind::MINSI || kind == CombiningKind::MAXUI ||
161 kind == CombiningKind::MAXSI || kind == CombiningKind::OR ||
162 kind == CombiningKind::XOR)
166 if (
acc && isa<VectorType>(
acc.getType()) && kind == CombiningKind::ADD) {
167 Value fma = vector::FMAOp::create(rewriter, loc, x, y,
acc);
174 mul = arith::MulFOp::create(rewriter, loc, x, y, fmf);
178 return std::optional<Value>(
mul);
189 dimsIdx.push_back(i);
208 arith::FastMathFlagsAttr fmf = {}) {
210 return arith::AddIOp::create(rewriter, loc, x, y);
211 return arith::AddFOp::create(rewriter, loc, x, y, fmf);
218 arith::FastMathFlagsAttr fmf = {}) {
220 return arith::MulIOp::create(rewriter, loc, x, y);
221 return arith::MulFOp::create(rewriter, loc, x, y, fmf);
241class ContractionOpToOuterProductOpLowering
244 using MaskableOpRewritePattern::MaskableOpRewritePattern;
246 using FilterConstraintType =
247 std::function<LogicalResult(vector::ContractionOp op)>;
249 static LogicalResult defaultFilter(vector::ContractionOp op) {
253 ContractionOpToOuterProductOpLowering(
254 vector::VectorContractLowering vectorContractLowering,
255 MLIRContext *context, PatternBenefit benefit = 1,
256 FilterConstraintType constraint = defaultFilter)
257 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit),
258 vectorContractLowering(vectorContractLowering),
259 filter(std::move(constraint)) {}
263 PatternRewriter &rewriter)
const override;
267 vector::VectorContractLowering vectorContractLowering;
268 FilterConstraintType filter;
289class ContractionOpToDotLowering
292 using MaskableOpRewritePattern::MaskableOpRewritePattern;
294 using FilterConstraintType =
295 std::function<LogicalResult(vector::ContractionOp op)>;
297 static LogicalResult defaultFilter(vector::ContractionOp op) {
301 ContractionOpToDotLowering(
302 vector::VectorContractLowering vectorContractLowering,
303 MLIRContext *context, PatternBenefit benefit = 1,
304 const FilterConstraintType &constraint = defaultFilter)
305 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit),
306 vectorContractLowering(vectorContractLowering), filter(defaultFilter) {}
310 PatternRewriter &rewriter)
const override;
314 vector::VectorContractLowering vectorContractLowering;
315 FilterConstraintType filter;
332class ContractionOpLowering
335 using MaskableOpRewritePattern::MaskableOpRewritePattern;
336 using FilterConstraintType =
337 std::function<LogicalResult(vector::ContractionOp op)>;
339 static LogicalResult defaultFilter(vector::ContractionOp op) {
343 ContractionOpLowering(
344 vector::VectorContractLowering vectorContractLoweringOption,
345 MLIRContext *context, PatternBenefit benefit = 1,
346 FilterConstraintType constraint = defaultFilter)
347 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit),
348 vectorContractLoweringOption(vectorContractLoweringOption),
349 filter(std::move(constraint)) {}
353 PatternRewriter &rewriter)
const override;
357 vector::VectorContractLowering vectorContractLoweringOption;
358 FilterConstraintType filter;
360 FailureOr<Value> lowerParallel(PatternRewriter &rewriter,
361 vector::ContractionOp op, int64_t lhsIndex,
362 int64_t rhsIndex, Value mask)
const;
364 FailureOr<Value> lowerReduction(PatternRewriter &rewriter,
365 vector::ContractionOp op, Value mask)
const;
370struct UnrolledOuterProductGenerator
372 UnrolledOuterProductGenerator(RewriterBase &
b, vector::ContractionOp op)
373 : StructuredGenerator<vector::ContractionOp, vector::IteratorType>(
b, op),
374 kind(op.getKind()),
lhs(op.getLhs()),
rhs(op.getRhs()),
375 res(op.getAcc()), lhsType(op.getLhsType()) {
376 auto maskableOp = cast<MaskableOpInterface>(op.getOperation());
377 if (maskableOp.isMasked())
378 mask = maskableOp.getMaskingOp().getMask();
381 Value t(Value v, ArrayRef<int64_t> perm = {1, 0}) {
384 return vector::TransposeOp::create(rewriter, loc, v, perm);
387 Value
promote(Value v, Type dstElementType) {
388 Type elementType = v.
getType();
389 auto vecType = dyn_cast<VectorType>(elementType);
391 elementType = vecType.getElementType();
392 if (elementType == dstElementType)
394 Type promotedType = dstElementType;
396 promotedType = vecType.clone(promotedType);
397 if (isa<FloatType>(dstElementType))
398 return arith::ExtFOp::create(rewriter, loc, promotedType, v,
400 return arith::ExtSIOp::create(rewriter, loc, promotedType, v);
404 VectorType lhsType,
int reductionSize,
405 std::optional<Value> maybeMask = std::nullopt) {
407 if (mask && !maybeMask.has_value())
410 Type resElementType = cast<VectorType>(res.getType()).getElementType();
411 for (
int64_t k = 0; k < reductionSize; ++k) {
412 Value extractA = vector::ExtractOp::create(rewriter, loc, lhs, k);
413 Value extractB = vector::ExtractOp::create(rewriter, loc, rhs, k);
414 extractA = promote(extractA, resElementType);
415 extractB = promote(extractB, resElementType);
417 if (maybeMask.has_value() && maybeMask.value())
419 vector::ExtractOp::create(rewriter, loc, maybeMask.value(), k);
421 Operation *outerProdOp = vector::OuterProductOp::create(
422 rewriter, loc, res.getType(), extractA, extractB, res, kind);
434 if (vecType.getScalableDims()[reductionDim])
436 int64_t reductionSize = vecType.getDimSize(reductionDim);
437 assert(reductionSize > 0 &&
438 "Reduction dim must be a known static size to allow unrolling");
439 return reductionSize;
444 if (!
iters({Par(), Par(), Red()}))
451 if (
layout({{m, k}, {k, n}, {m, n}})) {
456 Value tMask = t(mask, {2, 0, 1});
457 return outerProd(tLhs, rhs, res, lhsType, *reductionSize, tMask);
461 if (
layout({{m, k}, {n, k}, {m, n}})) {
465 Value tMask = t(mask, {2, 0, 1});
466 return outerProd(tLhs, tRhs, res, lhsType, *reductionSize, tMask);
470 if (
layout({{k, m}, {k, n}, {m, n}})) {
472 Value tMask = t(mask, {2, 0, 1});
473 return outerProd(lhs, rhs, res, lhsType, *reductionSize, tMask);
477 if (
layout({{k, m}, {n, k}, {m, n}})) {
480 Value tMask = t(mask, {2, 0, 1});
481 return outerProd(lhs, tRhs, res, lhsType, *reductionSize, tMask);
486 if (
layout({{m, k}, {k, n}, {n, m}})) {
489 Value tMask = t(mask, {2, 0, 1});
490 return outerProd(rhs, tLhs, res, lhsType, *reductionSize, tMask);
494 if (
layout({{m, k}, {n, k}, {n, m}})) {
498 Value tMask = t(mask, {2, 0, 1});
499 return outerProd(tRhs, tLhs, res, lhsType, *reductionSize, tMask);
502 if (
layout({{k, m}, {k, n}, {n, m}})) {
504 Value tMask = t(mask, {2, 0, 1});
505 return outerProd(rhs, lhs, res, lhsType, *reductionSize, tMask);
508 if (
layout({{k, m}, {n, k}, {n, m}})) {
511 Value tMask = t(mask, {2, 0, 1});
512 return outerProd(tRhs, lhs, res, lhsType, *reductionSize, tMask);
524 if (!
iters({Par(), Red()}))
530 if (
layout({{m, k}, {k}, {m}})) {
533 Value tMask = t(mask);
534 return outerProd(tLhs, rhs, res, lhsType, *reductionSize, tMask);
538 if (
layout({{k, m}, {k}, {m}})) {
540 Value tMask = t(mask);
541 return outerProd(lhs, rhs, res, lhsType, *reductionSize, tMask);
545 if (
layout({{k}, {m, k}, {m}})) {
548 Value tMask = t(mask);
549 return outerProd(tRhs, lhs, res, lhsType, *reductionSize, tMask);
553 if (
layout({{k}, {k, m}, {m}})) {
555 Value tMask = t(mask);
556 return outerProd(rhs, lhs, res, lhsType, *reductionSize, tMask);
567 if (!
iters({Red(), Par()}))
573 if (
layout({{m, k}, {k}, {m}}))
575 return outerProd(t(lhs), rhs, res, lhsType, *reductionSize, mask);
577 if (
layout({{k, m}, {k}, {m}}))
579 return outerProd(lhs, rhs, res, lhsType, *reductionSize, mask);
581 if (
layout({{k}, {m, k}, {m}}))
583 return outerProd(t(rhs), lhs, res, lhsType, *reductionSize, mask);
585 if (
layout({{k}, {k, m}, {m}}))
587 return outerProd(rhs, lhs, res, lhsType, *reductionSize, mask);
592 vector::CombiningKind kind;
593 Value
lhs,
rhs, res, mask;
613ContractionOpToOuterProductOpLowering::matchAndRewriteMaskableOp(
614 vector::ContractionOp op, MaskingOpInterface maskOp,
616 if (vectorContractLowering != vector::VectorContractLowering::OuterProduct)
622 UnrolledOuterProductGenerator e(rewriter, op);
623 FailureOr<Value> matmatRes = e.matmat();
624 if (succeeded(matmatRes)) {
627 FailureOr<Value> matvecRes = e.matvec();
628 if (succeeded(matvecRes)) {
632 FailureOr<Value> tmatvecRes = e.tmatvec();
636FailureOr<Value> ContractionOpToDotLowering::matchAndRewriteMaskableOp(
637 vector::ContractionOp op, MaskingOpInterface maskOp,
646 if (vectorContractLowering != vector::VectorContractLowering::Dot)
651 if (
auto dstType = dyn_cast<VectorType>(op.getResultType());
652 dstType && dstType.isScalable())
654 op,
"cannot unroll a scalable dimension in dot-product lowering");
656 auto iteratorTypes = op.getIteratorTypes().getValue();
657 static constexpr std::array<int64_t, 2> perm = {1, 0};
659 Value lhs = op.getLhs(), rhs = op.getRhs();
662 auto infer = [&](MapList m) {
678 if (maps == infer({{m, k}, {k, n}, {m, n}})) {
679 rhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
680 }
else if (maps == infer({{m, k}, {n, k}, {m, n}})) {
682 }
else if (maps == infer({{k, m}, {k, n}, {m, n}})) {
683 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
684 rhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
685 }
else if (maps == infer({{k, m}, {n, k}, {m, n}})) {
686 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
687 }
else if (maps == infer({{m, k}, {k, n}, {n, m}})) {
690 lhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
692 }
else if (maps == infer({{m, k}, {n, k}, {n, m}})) {
694 }
else if (maps == infer({{k, m}, {k, n}, {n, m}})) {
696 lhs = vector::TransposeOp::create(rewriter, loc, rhs, perm);
697 rhs = vector::TransposeOp::create(rewriter, loc, tmp, perm);
698 }
else if (maps == infer({{k, m}, {n, k}, {n, m}})) {
700 rhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
710 if (maps == infer({{m, n}, {n}, {m}})) {
712 }
else if (maps == infer({{n, m}, {n}, {m}})) {
713 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
714 }
else if (maps == infer({{n}, {m, n}, {m}})) {
716 }
else if (maps == infer({{n}, {n, m}, {m}})) {
718 lhs = vector::TransposeOp::create(rewriter, loc, lhs, perm);
726 VectorType dstType = cast<VectorType>(op.getResultType());
727 assert(dstType.getRank() >= 1 && dstType.getRank() <= 2 &&
728 "Expected dst type of rank 1 or 2");
730 unsigned rank = dstType.getRank();
731 unsigned dstRows = dstType.getShape()[0];
732 unsigned dstColumns = rank == 1 ? 1 : dstType.getShape()[1];
735 Value res = arith::ConstantOp::create(rewriter, loc, dstType,
737 bool isInt = isa<IntegerType>(dstType.getElementType());
738 arith::FastMathFlagsAttr fmf = op.getFastmathAttr();
740 extractedCols.reserve(dstColumns);
741 for (
unsigned r = 0; r < dstRows; ++r) {
742 Value rowLhs = vector::ExtractOp::create(rewriter, op.getLoc(), lhs, r);
743 for (
unsigned c = 0; c < dstColumns; ++c) {
750 : vector::ExtractOp::create(rewriter, op.getLoc(), rhs, c);
751 extractedCols.push_back(colRhs);
753 Value extractedColRhs = extractedCols[c];
755 createMul(op.getLoc(), rowLhs, extractedColRhs, isInt, rewriter, fmf);
756 Value sum = vector::ReductionOp::create(rewriter, op.getLoc(),
757 vector::CombiningKind::ADD,
762 res = vector::InsertOp::create(rewriter, op.getLoc(), sum, res, pos);
765 if (
auto acc = op.getAcc())
766 res =
createAdd(op.getLoc(), res,
acc, isInt, rewriter, fmf);
772struct ContractOpToElementwise
774 using MaskableOpRewritePattern::MaskableOpRewritePattern;
775 using FilterConstraintType =
776 std::function<LogicalResult(vector::ContractionOp op)>;
777 static LogicalResult defaultFilter(vector::ContractionOp op) {
780 ContractOpToElementwise(
781 vector::VectorContractLowering vectorContractLowering,
782 MLIRContext *context, PatternBenefit benefit = 1,
783 const FilterConstraintType &constraint = defaultFilter)
784 : MaskableOpRewritePattern<vector::ContractionOp>(context, benefit),
785 vectorContractLowering(vectorContractLowering), filter(defaultFilter) {}
788 matchAndRewriteMaskableOp(vector::ContractionOp contractOp,
789 MaskingOpInterface maskOp,
790 PatternRewriter &rewriter)
const override {
795 if (
failed(filter(contractOp)))
798 if (vectorContractLowering != vector::VectorContractLowering::ParallelArith)
801 ArrayRef<int64_t> lhsShape = contractOp.getLhsType().getShape();
802 ArrayRef<int64_t> rhsShape = contractOp.getRhsType().getShape();
803 AffineMap lhsMap = contractOp.getIndexingMapsArray()[0];
804 AffineMap rhsMap = contractOp.getIndexingMapsArray()[1];
805 SmallVector<int64_t> lhsReductionDims =
807 SmallVector<int64_t> rhsReductionDims =
810 for (int64_t dim : lhsReductionDims) {
811 if (lhsShape[dim] != 1)
814 for (int64_t dim : rhsReductionDims) {
815 if (rhsShape[dim] != 1)
818 AffineMap accMap = contractOp.getIndexingMapsArray()[2];
820 unsigned numLhsDimToBroadcast =
821 numParallelDims - (lhsMap.
getNumResults() - lhsReductionDims.size());
822 unsigned numRhsDimToBroadcast =
823 numParallelDims - (rhsMap.
getNumResults() - rhsReductionDims.size());
824 SmallVector<int64_t> lhsDims;
825 SmallVector<int64_t> lhsTranspose;
826 SmallVector<int64_t> rhsDims;
827 SmallVector<int64_t> rhsTranspose;
828 for (int64_t dim : lhsReductionDims)
829 lhsTranspose.push_back(numLhsDimToBroadcast + dim);
830 for (int64_t dim : rhsReductionDims)
831 rhsTranspose.push_back(numRhsDimToBroadcast + dim);
834 for (
unsigned i = 0; i < numParallelDims; i++) {
835 std::optional<unsigned> lhsDim =
838 lhsTranspose.push_back(numLhsDimToBroadcast + *lhsDim);
842 cast<VectorType>(contractOp.getResultType()).getDimSize(i));
843 lhsTranspose.push_back(lhsDims.size() - 1);
845 std::optional<unsigned> rhsDim =
848 rhsTranspose.push_back(numRhsDimToBroadcast + *rhsDim);
852 cast<VectorType>(contractOp.getResultType()).getDimSize(i));
853 rhsTranspose.push_back(rhsDims.size() - 1);
856 Value newLhs = contractOp.getLhs();
857 Value newRhs = contractOp.getRhs();
858 Location loc = contractOp.getLoc();
859 if (!lhsDims.empty()) {
860 lhsDims.append(lhsShape.begin(), lhsShape.end());
862 VectorType::get(lhsDims, contractOp.getLhsType().getElementType());
863 newLhs = vector::BroadcastOp::create(rewriter, loc, expandedType, newLhs);
865 if (!rhsDims.empty()) {
866 rhsDims.append(rhsShape.begin(), rhsShape.end());
868 VectorType::get(rhsDims, contractOp.getRhsType().getElementType());
869 newRhs = vector::BroadcastOp::create(rewriter, loc, expandedType, newRhs);
871 bool isInt = contractOp.getLhsType().getElementType().isIntOrIndex();
872 newLhs = vector::TransposeOp::create(rewriter, loc, newLhs, lhsTranspose);
873 newRhs = vector::TransposeOp::create(rewriter, loc, newRhs, rhsTranspose);
874 SmallVector<int64_t> lhsOffsets(lhsReductionDims.size(), 0);
875 SmallVector<int64_t> rhsOffsets(rhsReductionDims.size(), 0);
876 newLhs = vector::ExtractOp::create(rewriter, loc, newLhs, lhsOffsets);
877 newRhs = vector::ExtractOp::create(rewriter, loc, newRhs, rhsOffsets);
878 std::optional<Value>
result =
880 contractOp.getKind(), rewriter, isInt,
881 Value(), contractOp.getFastmathAttr());
890 vector::VectorContractLowering vectorContractLowering;
891 FilterConstraintType filter;
911FailureOr<Value> ContractionOpLowering::matchAndRewriteMaskableOp(
912 vector::ContractionOp op, MaskingOpInterface maskOp,
918 if (op.getLhsType().getElementType() !=
925 if (!op.isAddKind()) {
927 op,
"contractions other than 'add' not supported");
933 ContractionOpToOuterProductOpLowering pat1(vectorContractLoweringOption, ctx);
934 FailureOr<Value> newVal1 =
935 pat1.matchAndRewriteMaskableOp(op, maskOp, rewriter);
939 ContractionOpToDotLowering pat2(vectorContractLoweringOption, ctx);
940 FailureOr<Value> newVal2 =
941 pat2.matchAndRewriteMaskableOp(op, maskOp, rewriter);
945 ContractOpToElementwise pat4(vectorContractLoweringOption, ctx);
946 FailureOr<Value> newVal4 =
947 pat4.matchAndRewriteMaskableOp(op, maskOp, rewriter);
955 mask = maskOp.getMask();
957 std::vector<std::pair<int64_t, int64_t>> batchDimMap = op.getBatchDimMap();
958 if (!batchDimMap.empty()) {
959 int64_t lhsIndex = batchDimMap[0].first;
960 int64_t rhsIndex = batchDimMap[0].second;
961 auto newOp = lowerParallel(rewriter, op, lhsIndex, rhsIndex, mask);
968 std::vector<std::pair<int64_t, int64_t>> contractingDimMap =
969 op.getContractingDimMap();
972 for (
auto &dimPair : contractingDimMap) {
973 lhsContractingDimSet.insert(dimPair.first);
974 rhsContractingDimSet.insert(dimPair.second);
978 VectorType lhsType = op.getLhsType();
979 for (
int64_t lhsIndex = 0, e = lhsType.getRank(); lhsIndex < e; ++lhsIndex) {
980 if (lhsContractingDimSet.count(lhsIndex) == 0) {
981 auto newOp = lowerParallel(rewriter, op, lhsIndex, -1, mask);
989 VectorType rhsType = op.getRhsType();
990 for (
int64_t rhsIndex = 0, e = rhsType.getRank(); rhsIndex < e; ++rhsIndex) {
991 if (rhsContractingDimSet.count(rhsIndex) == 0) {
992 auto newOp = lowerParallel(rewriter, op, -1, rhsIndex, mask);
1000 if (!contractingDimMap.empty()) {
1001 auto newOp = lowerReduction(rewriter, op, mask);
1012FailureOr<Value> ContractionOpLowering::lowerParallel(
PatternRewriter &rewriter,
1013 vector::ContractionOp op,
1017 VectorType lhsType = op.getLhsType();
1018 VectorType rhsType = op.getRhsType();
1019 VectorType resType = cast<VectorType>(op.getResultType());
1024 if (lhsIndex >= 0) {
1025 iterIndex = iMap[0].getDimPosition(lhsIndex);
1026 if (rhsIndex >= 0 && iterIndex != iMap[1].
getDimPosition(rhsIndex))
1028 diag <<
"expected lhsIndex=" << lhsIndex <<
" and rhsIndex=" << rhsIndex
1029 <<
" to map to the same dimension";
1031 if (lhsType.getScalableDims()[lhsIndex])
1033 diag <<
"Unrolling scalable dimension (lhsIndex=" << lhsIndex
1034 <<
") is not supported yet";
1036 dimSize = lhsType.getDimSize(lhsIndex);
1037 }
else if (rhsIndex >= 0) {
1038 iterIndex = iMap[1].getDimPosition(rhsIndex);
1039 if (rhsType.getScalableDims()[rhsIndex])
1041 diag <<
"Unrolling scalable dimension (rhsIndex=" << rhsIndex
1042 <<
") is not supported yet";
1044 dimSize = rhsType.getDimSize(rhsIndex);
1048 diag <<
"expected either lhsIndex=" << lhsIndex
1049 <<
" or rhsIndex=" << rhsIndex <<
" to be nonnegative";
1053 assert(resIndex != -1 &&
"A dimension missing from the accumulator indexing "
1054 "map is not a parallel dim!");
1057 std::array<AffineMap, 3> lowIndexingMaps = {
1058 adjustMap(iMap[0], iterIndex, rewriter),
1059 adjustMap(iMap[1], iterIndex, rewriter),
1060 adjustMap(iMap[2], iterIndex, rewriter)};
1066 Value result = arith::ConstantOp::create(rewriter, loc, resType,
1069 for (
int64_t d = 0; d < dimSize; ++d) {
1070 auto lhs =
reshapeLoad(loc, op.getLhs(), lhsIndex, d, rewriter);
1071 auto rhs =
reshapeLoad(loc, op.getRhs(), rhsIndex, d, rewriter);
1076 lowMask =
reshapeLoad(loc, mask, iterIndex, d, rewriter);
1079 vector::ContractionOp::create(rewriter, loc, lhs, rhs,
acc, lowAffine,
1080 lowIter, op.getKind(), op.getFastmath());
1081 lowContract =
maskOperation(rewriter, lowContract, lowMask);
1089FailureOr<Value> ContractionOpLowering::lowerReduction(
1091 auto loc = op.getLoc();
1092 VectorType lhsType = op.getLhsType();
1093 VectorType rhsType = op.getRhsType();
1094 Type resType = op.getResultType();
1095 auto resVectorType = dyn_cast<VectorType>(resType);
1096 if (resVectorType && resVectorType.getRank() != 0)
1098 "did not expect a VectorType result");
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;
1135 acc ? vector::ReductionOp::create(rewriter, loc, kind, m,
acc,
1137 :
vector::ReductionOp::create(rewriter, loc, kind, m,
1142 vector::BroadcastOp::create(rewriter, loc, resVectorType, reduced);
1146 std::array<AffineMap, 3> lowIndexingMaps = {
1147 adjustMap(iMap[0], iterIndex, rewriter),
1148 adjustMap(iMap[1], iterIndex, rewriter),
1149 adjustMap(iMap[2], iterIndex, rewriter)};
1158 for (
int64_t d = 0; d < dimSize; ++d) {
1159 auto lhs =
reshapeLoad(loc, op.getLhs(), lhsIndex, d, rewriter);
1160 auto rhs =
reshapeLoad(loc, op.getRhs(), rhsIndex, d, rewriter);
1163 newMask =
reshapeLoad(loc, mask, iterIndex, d, rewriter);
1165 Operation *newContract = vector::ContractionOp::create(
1166 rewriter, loc, lhs, rhs,
result, lowAffine, lowIter, op.getKind(),
1186class OuterProductOpLowering :
public OpRewritePattern<vector::OuterProductOp> {
1190 LogicalResult matchAndRewrite(vector::OuterProductOp op,
1191 PatternRewriter &rewriter)
const override {
1192 VectorType resType = op.getResultVectorType();
1193 if ((resType.getShape().size() >= 2) && resType.allDimsScalable())
1196 auto loc = op.getLoc();
1198 VectorType lhsType = op.getOperandVectorTypeLHS();
1199 VectorType rhsType = dyn_cast<VectorType>(op.getOperandTypeRHS());
1200 Type eltType = resType.getElementType();
1201 bool isInt = isa<IntegerType, IndexType>(eltType);
1202 Value acc = op.getAcc();
1203 vector::CombiningKind kind = op.getKind();
1206 OpBuilder::InsertionGuard guard(rewriter);
1207 auto maskableOp = cast<vector::MaskableOpInterface>(op.getOperation());
1210 if (maskableOp.isMasked()) {
1212 rootOp = maskableOp.getMaskingOp();
1213 mask = maskableOp.getMaskingOp().getMask();
1221 vector::BroadcastOp::create(rewriter, loc, lhsType, op.getRhs());
1223 loc, op.getLhs(),
b, acc, kind, rewriter, isInt, mask);
1224 if (!mult.has_value())
1230 Value
result = arith::ConstantOp::create(rewriter, loc, resType,
1232 for (int64_t d = 0, e = resType.getDimSize(0); d < e; ++d) {
1233 Value x = vector::ExtractOp::create(rewriter, loc, op.getLhs(), d);
1234 Value a = vector::BroadcastOp::create(rewriter, loc, rhsType, x);
1237 r = vector::ExtractOp::create(rewriter, loc, acc, d);
1240 extrMask = vector::ExtractOp::create(rewriter, loc, mask, d);
1243 loc, a, op.getRhs(), r, kind, rewriter, isInt, extrMask);
1246 result = vector::InsertOp::create(rewriter, loc, *m,
result, d);
1258 VectorContractLowering vectorContractLoweringOption,
PatternBenefit benefit,
1259 bool disableOuterProductLowering) {
1260 if (!disableOuterProductLowering)
1261 patterns.
add<OuterProductOpLowering>(patterns.
getContext(), benefit);
1262 patterns.
add<ContractionOpLowering, ContractionOpToOuterProductOpLowering>(
1263 vectorContractLoweringOption, patterns.
getContext(), benefit);
1268 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....
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.
LogicalResult 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