19#include "llvm/Support/Casting.h"
32static Value contractionUsersAfterYield(
Value v) {
39 if (!isa<scf::YieldOp>(user))
42 auto yield = cast<scf::YieldOp>(user);
46 return contractionUsersAfterYield(parent->
getResult(idx));
53 ShapedType inputType = cast<ShapedType>(input.
getType());
54 int64_t firstDimToCollapse = inputType.getRank() - 2;
56 if (inputType.getRank() == 1)
60 for (
int64_t i = 0; i < firstDimToCollapse; ++i)
64 for (
int64_t i = firstDimToCollapse; i < inputType.getRank(); ++i)
65 collapsedIndices.push_back(i);
67 reassociation.push_back(collapsedIndices);
68 return memref::CollapseShapeOp::create(builder, loc, input, reassociation);
72static bool isReadSrcMemref(
Value operand) {
79 .Case<TransferReadOp, LoadOp>(
80 [&](
auto readOp) { srcBuff = readOp.getOperand(0); });
82 return srcBuff && isa<MemRefType>(srcBuff.
getType());
87static FailureOr<std::pair<Value, SmallVector<Value>>>
97 .Case<TransferReadOp, LoadOp>([&](
auto readOp) {
99 readOp.getIndices().end());
100 srcBuff = readOp.getOperand(0);
103 if (!srcBuff || !isa<MemRefType>(srcBuff.
getType()))
107 indexVals.pop_back();
110 indices.reserve(indexVals.size());
121 return std::make_pair(srcBuff,
indices);
125static LogicalResult validateLoopStep(
OpBuilder &rewriter,
Value step,
132 if (cst.value() != value && cst.value() != 1)
139static LogicalResult validateContractOps(
OpBuilder &rewriter,
140 vector::ContractionOp contractOp,
141 unsigned int blockingFactor,
147 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
148 contractOp.getLhs(),
false);
151 auto [buffLhs, indicesLhs] = *srcIndxLhs;
154 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
155 contractOp.getRhs(),
false);
158 auto [buffRhs, indicesRhs] = *srcIndxRhs;
161 if (buffLhs != srcBuffLhs)
164 if (buffRhs != srcBuffRhs)
168 if (!contractionUsersAfterYield(contractOp.getResult()))
171 VectorType accTy = dyn_cast<VectorType>(contractOp.getAccType());
178 llvm::copy_if(accShape, std::back_inserter(nonUnitDimAcc),
179 [](
int64_t dim) {
return (dim != 16 && dim != 1); });
181 if (nonUnitDimAcc.size() != 0)
186 VectorType lhsTy = contractOp.getLhsType();
189 llvm::copy_if(lhsShape, std::back_inserter(nonUnitDimLhs),
190 [](
int64_t dim) {
return (dim != 16 && dim != 1); });
192 if (nonUnitDimLhs.size() != 1)
195 if (nonUnitDimLhs[0] != blockingFactor)
200 VectorType rhsTy = contractOp.getRhsType();
203 llvm::copy_if(rhsShape, std::back_inserter(nonUnitDimRhs),
204 [](
int64_t dim) {
return (dim != 16 && dim != 1); });
206 if (nonUnitDimRhs.size() != 1)
209 if (nonUnitDimRhs[0] != blockingFactor)
217static unsigned getIndexPosition(
Value operand, scf::ForOp loop) {
218 Value iv = loop.getInductionVar();
222 .Case<TransferReadOp, LoadOp>(
223 [&](
auto readOp) { srcBuff = readOp.getOperand(0); });
229 auto offsets = subview.getOffsets();
231 for (
auto it : llvm::enumerate(offsets)) {
232 if (it.value() == iv)
242 bool rhs,
unsigned int offset,
245 auto srcIndx = getSrcIndxValue(rewriter, loc, operand,
false);
246 auto [srcBuff,
indices] = *srcIndx;
257 amx::TileType tileType = amx::TileType::get({16, (16 * offset)}, ipType);
258 return amx::TileLoadOp::create(rewriter, loc, tileType, mat,
indices);
262 Type ipType,
unsigned int offset,
Value packedBuffer,
263 Value indxToStoreInBuffer) {
268 llvm::cast<MemRefType>(matB.
getType()).getRank(), c0);
276 rewriter, loc, c0, cBound, cStep,
ValueRange{},
279 subviewOffset[subviewOffset.size() - 2] = iv;
283 auto vectorType = VectorType::get({2, (16 * (offset / 2))}, ipType);
285 vectorType = VectorType::get((16 * offset), ipType);
287 int64_t srcRank = (dyn_cast<ShapedType>(matB.
getType())).getRank();
288 Value padding = ub::PoisonOp::create(rewriter, loc, ipType);
292 Value vec1 = vector::TransferReadOp::create(
293 rewriter, loc, vectorType, matB,
ValueRange(subviewOffset), padding,
297 vec1 = vector::ShapeCastOp::create(
298 rewriter, loc, VectorType::get((16 * offset), ipType), vec1);
302 Value incIV = arith::AddIOp::create(rewriter, loc, offsetIndx, iv);
303 subviewOffset[subviewOffset.size() - 2] = incIV;
305 Value vec2 = vector::TransferReadOp::create(
306 rewriter, loc, vectorType, matB,
ValueRange(subviewOffset), padding,
309 vec2 = vector::ShapeCastOp::create(
310 rewriter, loc, VectorType::get((16 * offset), ipType), vec2);
312 vector::ShuffleOp shuffle1;
313 vector::ShuffleOp shuffle2;
317 shuffle1 = vector::ShuffleOp::create(
318 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
320 ArrayRef<int64_t>{0, 32, 1, 33, 2, 34, 3, 35, 8, 40, 9,
321 41, 10, 42, 11, 43, 16, 48, 17, 49, 18, 50,
322 19, 51, 24, 56, 25, 57, 26, 58, 27, 59});
324 shuffle2 = vector::ShuffleOp::create(
325 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
327 ArrayRef<int64_t>{4, 36, 5, 37, 6, 38, 7, 39, 12, 44, 13,
328 45, 14, 46, 15, 47, 20, 52, 21, 53, 22, 54,
329 23, 55, 28, 60, 29, 61, 30, 62, 31, 63});
335 shuffle1 = vector::ShuffleOp::create(
336 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
339 0, 32, 64, 96, 1, 33, 65, 97, 2, 34, 66, 98, 3,
340 35, 67, 99, 8, 40, 72, 104, 9, 41, 73, 105, 10, 42,
341 74, 106, 11, 43, 75, 107, 16, 48, 80, 112, 17, 49, 81,
342 113, 18, 50, 82, 114, 19, 51, 83, 115, 24, 56, 88, 120,
343 25, 57, 89, 121, 26, 58, 90, 122, 27, 59, 91, 123});
345 shuffle2 = vector::ShuffleOp::create(
346 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
349 4, 36, 68, 100, 5, 37, 69, 101, 6, 38, 70, 102, 7, 39,
350 71, 103, 12, 44, 76, 108, 13, 45, 77, 109, 14, 46, 78, 110,
351 15, 47, 79, 111, 20, 52, 84, 116, 21, 53, 85, 117, 22, 54,
352 86, 118, 23, 55, 87, 119, 28, 60, 92, 124, 29, 61, 93, 125,
353 30, 62, 94, 126, 31, 63, 95, 127});
357 Value ivShuff1 = arith::DivUIOp::create(rewriter, loc, iv, cStep);
358 Value ivShuff2 = arith::AddIOp::create(rewriter, loc, ivShuff1, c16);
360 vector::StoreOp::create(rewriter, loc, shuffle1, packedBuffer,
361 ValueRange{indxToStoreInBuffer, ivShuff1, c0});
362 vector::StoreOp::create(rewriter, loc, shuffle2, packedBuffer,
363 ValueRange{indxToStoreInBuffer, ivShuff2, c0});
365 scf::YieldOp::create(nestedBuilder, loc);
372 unsigned int offset,
Value packedBuffer,
bool pack,
373 Value indxToStoreInBuffer,
Value indxToLoadFromMatB) {
379 for (
size_t j = 0;
j < ops.size();
j++) {
380 for (
size_t i = 0; i < ops.size(); i++) {
384 Operation *readOpRhs = ops[
j].getRhs().getDefiningOp();
385 auto itRhs = readsToTileLoads.find(readOpRhs);
386 if (itRhs != readsToTileLoads.end()) {
391 performShuffle(rewriter, loc, matB, ipType, offset, packedBuffer,
392 indxToStoreInBuffer);
396 amx::TileType::get({16, (16 * offset)}, ipType);
398 amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
402 amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
405 readsToTileLoads.try_emplace(readOpRhs, loadRow1);
406 readsToTileLoads.try_emplace(ops[i].getRhs().getDefiningOp(), loadRow2);
411 return readsToTileLoads;
419 unsigned int offset,
bool isVnni,
Value packedBuffer,
bool pack,
420 Value indxToStoreInBuffer,
Value indxToLoadFromMatB) {
435 packInputs(rewriter, loc, ops, matB, ipType, offset, packedBuffer, pack,
436 indxToStoreInBuffer, indxToLoadFromMatB);
440 for (
size_t i = 0; i < ops.size(); i++) {
442 Operation *readOpLhs = ops[i].getLhs().getDefiningOp();
443 amx::TileLoadOp tilesLhs;
444 auto itLhs = readsToTileLoads.find(readOpLhs);
445 if (itLhs != readsToTileLoads.end()) {
446 tilesLhs = itLhs->second;
448 tilesLhs = createTileLoads(rewriter, loc, ops[i].getLhs(), matA, ipType,
449 false, offset, isVnni);
450 readsToTileLoads.try_emplace(readOpLhs, tilesLhs);
453 Operation *readOpRhs = ops[i].getRhs().getDefiningOp();
454 amx::TileLoadOp tilesRhs;
455 auto itRhs = readsToTileLoads.find(readOpRhs);
456 if (itRhs != readsToTileLoads.end()) {
457 tilesRhs = itRhs->second;
459 tilesRhs = createTileLoads(rewriter, loc, ops[i].getRhs(), matB, ipType,
460 true, offset, isVnni);
461 readsToTileLoads.try_emplace(readOpRhs, tilesRhs);
464 auto accTileType = amx::TileType::get({16, 16}, opType);
468 dp = amx::TileMulFOp::create(rewriter, loc, accTileType, tilesLhs,
469 tilesRhs, accIterArgs[i]);
472 dp = amx::TileMulIOp::create(rewriter, loc, accTileType, tilesLhs,
473 tilesRhs, accIterArgs[i]);
475 accumulators.push_back(dp);
481 Type opType, scf::ForOp outerLoop,
486 auto zeroTileType = amx::TileType::get({16, 16}, opType);
488 for (
int i = 0; i < size; i++) {
489 auto zeroTile = amx::TileZeroOp::create(rewriter, loc, zeroTileType);
490 loopItrArgs.push_back(zeroTile);
495static Value getIndxToLoadStoreFromPckBuffer(
497 bool isInnerLoopUBHasOddQuot,
bool isInnerLoopUBLarger,
bool pack,
498 unsigned int blockingFactor) {
504 Value quotientInnerLoop =
505 arith::DivUIOp::create(rewriter, loc, ivInnerLoop, packOffset);
506 Value remInnerLoop = arith::RemUIOp::create(
507 rewriter, loc, rewriter.
getIndexType(), quotientInnerLoop, c2);
509 if (!isInnerLoopUBLarger && !pack) {
510 remInnerLoop = arith::RemUIOp::create(
511 rewriter, loc, rewriter.
getIndexType(), ivOuterLoop, c2);
514 if (isInnerLoopUBHasOddQuot) {
515 auto remOuterLoop = arith::RemUIOp::create(
516 rewriter, loc, rewriter.
getIndexType(), ivOuterLoop, c2);
517 auto remAdd = arith::AddIOp::create(rewriter, loc, rewriter.
getIndexType(),
518 remInnerLoop, remOuterLoop);
519 remInnerLoop = arith::RemUIOp::create(rewriter, loc,
529 Type ipType,
Type opType,
unsigned int blockingFactor,
bool isVnni,
531 vector::ContractionOp contractOp, scf::ForOp outerLoop,
533 Value ivOuterLoop,
Value packedBuffer,
bool pack,
535 bool isInnerLoopUBHasOddQuot) {
541 int64_t offset = 16 * blockingFactor;
543 offset = cst.value();
545 auto newLoop = scf::ForOp::create(
546 rewriter, loc, lowerBound, upperBound, step, loopItrArgs,
552 getIndexPosition(contractOp.getLhs(), outerLoop) + 1),
556 getIndexPosition(contractOp.getLhs(), innerLoop) + 1),
558 auto lhsClone = rewriterNewInnerLoop.
clone(*vectorOpLhs, mapping);
560 Value indxToStoreInBuffer = c0;
561 Value indxToLoadFromBuffer = c0;
564 if (innerLoopIndex.
value() == 0) {
567 ivOuterLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
570 if (!isInnerLoopUBLarger || isInnerLoopUBHasOddQuot) {
571 indxToStoreInBuffer = arith::RemUIOp::create(
576 Value indxToLoadFromMatB = arith::AddIOp::create(
577 rewriter, loc, indxToStoreInBuffer, c1);
578 indxToLoadFromBuffer = arith::RemUIOp::create(
579 rewriter, loc, rewriter.
getIndexType(), indxToLoadFromMatB,
585 rewriter, locNewInnerLoop, offset);
586 ivNewInnerLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
587 nLoadIndx, ivNewInnerLoop);
588 indxToStoreInBuffer = getIndxToLoadStoreFromPckBuffer(
589 rewriter, loc, ivNewInnerLoop, ivOuterLoop,
590 isInnerLoopUBHasOddQuot, isInnerLoopUBLarger, pack,
592 Value indxToLoadFromMatB =
593 arith::AddIOp::create(rewriter, loc, indxToStoreInBuffer, c1);
594 indxToLoadFromBuffer =
595 arith::RemUIOp::create(rewriter, loc, rewriter.
getIndexType(),
596 indxToLoadFromMatB, c2);
601 rewriter, locNewInnerLoop, offset);
602 ivNewInnerLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
603 nLoadIndx, ivNewInnerLoop);
604 Value quotient_K = arith::DivUIOp::create(
605 rewriter, loc, ivNewInnerLoop, nLoadIndx);
606 indxToStoreInBuffer = arith::RemUIOp::create(
607 rewriter, loc, rewriter.
getIndexType(), quotient_K, c2);
609 Value indxToLoadFromMatB =
610 arith::AddIOp::create(rewriter, loc, indxToStoreInBuffer, c1);
611 indxToLoadFromBuffer =
612 arith::RemUIOp::create(rewriter, loc, rewriter.
getIndexType(),
613 indxToLoadFromMatB, c2);
626 int64_t outerPos = getIndexPosition(contractOp.getRhs(), outerLoop);
629 unsigned operandIdx =
static_cast<unsigned>(outerPos + 1);
631 if (operandIdx < rhsOp->getNumOperands())
636 int64_t innerPos = getIndexPosition(contractOp.getRhs(), innerLoop);
639 unsigned operandIdx =
static_cast<unsigned>(innerPos + 1);
641 if (operandIdx < rhsOp->getNumOperands())
642 rhsMapping.
map(rhsOp->
getOperand(operandIdx), ivNewInnerLoop);
645 auto rhsClone = rewriterNewInnerLoop.
clone(*rhsOp, rhsMapping);
646 matB = rhsClone->getResult(0);
657 rewriter, locNewInnerLoop, offset);
659 indxToLoadFromBuffer = c0;
660 indxToLoadFromBuffer = getIndxToLoadStoreFromPckBuffer(
661 rewriter, loc, nLoadIndx, ivOuterLoop,
662 isInnerLoopUBHasOddQuot, isInnerLoopUBLarger, pack,
668 rewriter, locNewInnerLoop, offset);
670 Value quotient_K = arith::DivUIOp::create(
671 rewriter, loc, ivNewInnerLoop, nLoadIndx);
672 indxToLoadFromBuffer = arith::RemUIOp::create(
673 rewriter, loc, rewriter.
getIndexType(), quotient_K, c2);
679 rewriter, locNewInnerLoop, ops, lhsClone->getResult(0), matB,
680 ipType, opType, iterArgsNewInnerLoop, blockingFactor, isVnni,
681 packedBuffer, pack, indxToStoreInBuffer, indxToLoadFromBuffer);
683 scf::YieldOp::create(rewriterNewInnerLoop, locNewInnerLoop,
754struct VectorContractToAMXDotProduct
756 using OpRewritePattern<vector::ContractionOp>::OpRewritePattern;
758 LogicalResult matchAndRewrite(vector::ContractionOp contractOp,
759 PatternRewriter &rewriter)
const override {
761 if (contractOp.getKind() != vector::CombiningKind::ADD)
763 "Expects add combining kind.");
765 unsigned int blockingFactor =
766 contractOp.getLhsType().getElementType().isBF16() ? 2 : 4;
769 contractOp.getIndexingMapsArray(), blockingFactor);
771 VectorType lhsTy = contractOp.getLhsType();
772 if (!lhsTy.getElementType().isBF16() &&
773 !lhsTy.getElementType().isSignlessInteger(8) &&
774 !lhsTy.getElementType().isF8E4M3FN() &&
775 !lhsTy.getElementType().isF8E5M2())
777 contractOp,
"Only BF16/Int8/F8 lowering is supported.");
779 if (lhsTy.getElementType() != contractOp.getRhsType().getElementType())
781 contractOp,
"Contraction should have same lhs and rhs type.");
783 VectorType accTy = dyn_cast<VectorType>(contractOp.getAccType());
787 if (((lhsTy.getElementType().isBF16() ||
788 lhsTy.getElementType().isF8E4M3FN() ||
789 lhsTy.getElementType().isF8E5M2()) &&
790 !accTy.getElementType().isF32()) ||
791 (lhsTy.getElementType().isSignlessInteger(8) &&
792 !accTy.getElementType().isSignlessInteger(32)))
794 "Only F32 for BF16 or Int32 for Int8 "
795 "accumulation type is supported.");
797 Operation *accReadOp =
800 Operation *resultWriteOp =
803 if (!accReadOp || !resultWriteOp)
805 contractOp,
"The ACC operand of the vector.contract should be a "
806 "transfer_read or a load. And, the result should be "
807 "stored using transfer_write or store.");
812 if (lhsTy.getElementType().isSignlessInteger(8)) {
817 if (lhsTy.getElementType().isF8E4M3FN())
820 if (lhsTy.getElementType().isF8E5M2())
823 if (accReadOp->
getBlock() == contractOp->getBlock() &&
824 resultWriteOp->
getBlock() != contractOp->getBlock())
826 contractOp,
"The accumulator store is in different block.");
828 if (accReadOp->
getBlock() != contractOp->getBlock() &&
829 resultWriteOp->
getBlock() == contractOp->getBlock())
831 contractOp,
"The accumulator read is in different block.");
833 if (!(isReadSrcMemref(contractOp.getLhs()) &&
834 isReadSrcMemref(contractOp.getRhs())))
836 contractOp,
"The LHS or RHS src is not a MemRef type.");
838 unsigned int dimValue = blockingFactor;
840 dimValue = 16 * blockingFactor;
844 if (accReadOp->
getBlock() == contractOp->getBlock() &&
845 resultWriteOp->
getBlock() == contractOp->getBlock()) {
847 if (!isReadSrcMemref(contractOp.getAcc()))
849 "The ACC src is not a MemRef type.");
851 bool collapse =
false;
855 LogicalResult validate = validateContractOps(
856 rewriter, contractOp, dimValue, Value(), Value(),
false);
860 contractOp,
"The contract operation doesn't satisfy the operands "
861 "dimensions. M, N, and vnni dims are 16, 16, and 2/4. "
862 "The rest dims should be 1. Op should have one user.");
864 Location loc = contractOp.getLoc();
866 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
867 contractOp.getLhs(), collapse);
870 "Failed to get the LHS src.");
871 auto [srcBuffLhs, indicesLhs] = *srcIndxLhs;
873 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
874 contractOp.getRhs(), collapse);
877 "Failed to get the RHS src.");
878 auto rhsSrc = *srcIndxRhs;
879 auto srcBuffRhs = rhsSrc.first;
880 auto indicesRhs = rhsSrc.second;
882 auto srcIndxAcc = getSrcIndxValue(rewriter, contractOp.getLoc(),
883 contractOp.getAcc(),
false);
886 "Failed to get the ACC src.");
887 auto [srcBuffAcc, indicesAcc] = *srcIndxAcc;
892 auto tileType = amx::TileType::get({16, (16 * blockingFactor)}, ipType);
893 auto loadLhs = amx::TileLoadOp::create(rewriter, loc, tileType,
894 srcBuffLhs, indicesLhs);
897 amx::TileLoadOp loadRhs;
900 SmallVector<OpFoldResult> indexVals;
901 llvm::TypeSwitch<Operation *>(contractOp.getRhs().getDefiningOp())
902 .Case<TransferReadOp, LoadOp>([&](
auto readOp) {
903 indexVals = SmallVector<OpFoldResult>(readOp.getIndices().begin(),
904 readOp.getIndices().end());
905 vecTy = readOp.getType();
908 SmallVector<OpFoldResult> strides(indexVals.size(), one);
910 contractOp.getRhs().getDefiningOp()->getContext(),
912 auto subview = memref::SubViewOp::create(rewriter, loc, srcBuffRhs,
913 indexVals, sizes, strides);
914 auto bufferType = MemRefType::get({16, (16 * blockingFactor)}, ipType);
915 auto packedBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
921 (blockingFactor * 16));
925 rewriter, loc, 16 * (blockingFactor / 2));
928 rewriter, loc, c0, uBound, step,
ValueRange{},
929 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
932 arith::AddIOp::create(rewriter, loc, nextLoadIndx, iv);
934 indicesRhs[indicesRhs.size() - 2] = iv;
935 indicesRhs[indicesRhs.size() - 1] = c0;
937 auto vec1 = vector::LoadOp::create(
939 VectorType::get(16 * (blockingFactor / 2), ipType), subview,
942 indicesRhs[indicesRhs.size() - 2] = i1_load;
944 auto vec2 = vector::LoadOp::create(
946 VectorType::get(16 * (blockingFactor / 2), ipType), subview,
949 vector::ShuffleOp shuffle1;
950 vector::ShuffleOp shuffle2;
952 if (blockingFactor == 2) {
954 shuffle1 = vector::ShuffleOp::create(
955 rewriter, loc, VectorType::get({16}, ipType), vec1, vec2,
956 ArrayRef<int64_t>{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21,
959 shuffle2 = vector::ShuffleOp::create(
960 rewriter, loc, VectorType::get({16}, ipType), vec1, vec2,
961 ArrayRef<int64_t>{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13,
962 29, 14, 30, 15, 31});
965 if (blockingFactor == 4) {
966 shuffle1 = vector::ShuffleOp::create(
967 rewriter, loc, VectorType::get({32}, ipType), vec1, vec2,
968 ArrayRef<int64_t>{0, 16, 32, 48, 1, 17, 33, 49,
969 2, 18, 34, 50, 3, 19, 35, 51,
970 4, 20, 36, 52, 5, 21, 37, 53,
971 6, 22, 38, 54, 7, 23, 39, 55});
973 shuffle2 = vector::ShuffleOp::create(
974 rewriter, loc, VectorType::get({32}, ipType), vec1, vec2,
975 ArrayRef<int64_t>{8, 24, 40, 56, 9, 25, 41, 57,
976 10, 26, 42, 58, 11, 27, 43, 59,
977 12, 28, 44, 60, 13, 29, 45, 61,
978 14, 30, 46, 62, 15, 31, 47, 63});
981 auto rem = arith::DivUIOp::create(
984 vector::StoreOp::create(rewriter, loc, shuffle1, packedBuffer,
986 vector::StoreOp::create(rewriter, loc, shuffle2, packedBuffer,
989 scf::YieldOp::create(nestedBuilder, loc);
991 loadRhs = amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
995 loadRhs = amx::TileLoadOp::create(rewriter, loc, tileType, srcBuffRhs,
999 auto tileTypeAcc = amx::TileType::get({16, 16}, opType);
1000 auto loadAcc = amx::TileLoadOp::create(rewriter, loc, tileTypeAcc,
1001 srcBuffAcc, indicesAcc);
1006 dp = amx::TileMulFOp::create(rewriter, loc, tileTypeAcc, loadLhs,
1010 dp = amx::TileMulIOp::create(rewriter, loc, tileTypeAcc, loadLhs,
1013 auto bufferType = MemRefType::get({16, 16}, opType);
1014 auto resultBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1016 amx::TileStoreOp::create(rewriter, loc, resultBuffer,
ValueRange{c0, c0},
1019 auto vectorType = mlir::VectorType::get({16, 16}, opType);
1021 (dyn_cast<ShapedType>(resultBuffer.getType())).getRank();
1022 Value padding = ub::PoisonOp::create(rewriter, loc, opType);
1025 SmallVector<bool> inBounds(vectorType.getRank(),
true);
1027 Value vecRow = vector::TransferReadOp::create(
1028 rewriter, loc, vectorType, resultBuffer,
ValueRange{c0, c0}, padding,
1031 Value resultOp = contractionUsersAfterYield(contractOp.getResult());
1032 if (
auto vecType = llvm::dyn_cast<VectorType>(resultOp.getType()))
1033 vecRow = vector::ShapeCastOp::create(rewriter, loc, vecType, vecRow);
1043 SmallVector<scf::ForOp> loopLists;
1044 Operation *current = contractOp;
1051 "Accumulator read and contract op not within scf.for op");
1053 loopLists.push_back(dyn_cast<scf::ForOp>(parent));
1061 if (loopLists.size() > 2 || loopLists.size() == 0)
1063 contractOp,
"Rewrite is supported until reduction loop depth of 2.");
1065 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1066 contractOp.getLhs(),
false);
1069 "Failed to get the LHS src.");
1070 auto [srcBuffLhs, indicesLhs] = *srcIndxLhs;
1072 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1073 contractOp.getRhs(),
false);
1076 "Failed to get the RHS src.");
1077 auto [srcBuffRhs, indicesRhs] = *srcIndxRhs;
1078 Operation *vectorOpLhs;
1079 llvm::TypeSwitch<Operation *>(contractOp.getLhs().getDefiningOp())
1080 .Case<TransferReadOp, LoadOp>([&](
auto readOp) {
1081 vectorOpLhs = readOp.getBase().getDefiningOp();
1084 Operation *vectorOpRhs;
1085 llvm::TypeSwitch<Operation *>(contractOp.getRhs().getDefiningOp())
1086 .Case<TransferReadOp, LoadOp>([&](
auto readOp) {
1087 vectorOpRhs = readOp.getBase().getDefiningOp();
1090 if (!vectorOpLhs || !vectorOpRhs)
1092 contractOp,
"Failed to find LHS or RHS read source operation");
1095 SmallVector<vector::ContractionOp> ops;
1096 for (mlir::Operation &op : loopLists[0].getBody()->getOperations()) {
1098 if (
auto contract = llvm::dyn_cast<mlir::vector::ContractionOp>(op)) {
1100 LogicalResult validate = validateContractOps(
1101 rewriter,
contract, dimValue, srcBuffLhs, srcBuffRhs,
true);
1106 "The associated contract operations doesn't satisfy "
1107 "the re-write conditions either the dimensions are "
1108 "wrong or MemRef source are different or many users.");
1115 unsigned int pairCount = 0;
1116 for (
size_t j = 0; j < ops.size(); j++) {
1117 for (
size_t i = j; i < ops.size(); i++) {
1119 pairCount = pairCount + 2;
1123 if (pairCount != ops.size())
1125 contractOp,
"Coudn't find the pair vector contract ");
1128 scf::ForOp innerLoop;
1129 scf::ForOp outerLoop;
1133 if (loopLists.size() == 2) {
1134 outerLoop = loopLists[1];
1135 innerLoop = loopLists[0];
1137 LogicalResult validateOuterLoopStep =
1138 validateLoopStep(rewriter, outerLoop.getStep(), 1);
1139 if (
failed(validateOuterLoopStep))
1142 int64_t stepValue = 16;
1144 stepValue = stepValue * blockingFactor;
1145 LogicalResult validateInnerLoopStep =
1146 validateLoopStep(rewriter, innerLoop.getStep(), stepValue);
1147 if (
failed(validateInnerLoopStep))
1149 contractOp,
"Invalid loop step. The step should be 32 for BF16 and "
1152 SmallVector<Value> loopItrArgs = createTileZeros(
1153 rewriter, outerLoop.getLoc(), opType, outerLoop, ops.size());
1156 newLoop = scf::ForOp::create(
1157 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1158 outerLoop.getUpperBound(), outerLoop.getStep(), loopItrArgs,
1159 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1160 Value ivOuterLoop,
ValueRange iterArgsOuterLoop) {
1161 auto newInnerLoop = createLoops(
1162 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1163 innerLoop.getUpperBound(), innerLoop.getStep(),
1164 iterArgsOuterLoop, ipType, opType, blockingFactor, isVnni,
1165 vectorOpLhs, vectorOpRhs, contractOp, outerLoop, innerLoop,
1166 ops, ivOuterLoop, nullptr, true, nullptr, false, false);
1168 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1169 newInnerLoop.getResults());
1174 bool isInnerLoopUBLarger =
false;
1175 bool isInnerLoopUBHasOddQuot =
false;
1177 int64_t ubVal = 16 * blockingFactor;
1178 mlir::Value ub = innerLoop.getUpperBound();
1179 if (
auto constOp = ub.
getDefiningOp<mlir::arith::ConstantOp>()) {
1181 llvm::dyn_cast<mlir::IntegerAttr>(constOp.getValue())) {
1182 ubVal = intAttr.getInt();
1186 isInnerLoopUBLarger = ubVal > 16 * blockingFactor;
1187 isInnerLoopUBHasOddQuot =
1188 (((ubVal / (16 * blockingFactor)) % 2) == 1) && isInnerLoopUBLarger;
1197 rewriter, outerLoop.getLoc(), 16 * blockingFactor);
1199 Value spillOuterLoop = arith::SubIOp::create(
1200 rewriter, outerLoop.getLoc(), outerLoop.getUpperBound(), c1);
1201 Value spillInnerLoop =
1202 arith::SubIOp::create(rewriter, innerLoop.getLoc(),
1203 innerLoop.getUpperBound(), spillLoopBound);
1205 MemRefType::get({2, 32, (blockingFactor * 16)}, ipType);
1207 memref::AllocaOp::create(rewriter, outerLoop.getLoc(), bufferType);
1210 IRMapping rhsMapping;
1212 vectorOpRhs->getOperand(
1213 getIndexPosition(contractOp.getRhs(), outerLoop) + 1),
1214 outerLoop.getLowerBound());
1216 vectorOpRhs->getOperand(
1217 getIndexPosition(contractOp.getRhs(), innerLoop) + 1),
1218 innerLoop.getLowerBound());
1219 auto rhsClone = rewriter.
clone(*vectorOpRhs, rhsMapping);
1221 Value quotient_batch = arith::DivUIOp::create(
1222 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1223 outerLoop.getStep());
1224 Value quotient_k = arith::DivUIOp::create(rewriter, outerLoop.getLoc(),
1225 innerLoop.getLowerBound(),
1226 innerLoop.getStep());
1228 Value quotient_add = arith::AddIOp::create(rewriter, outerLoop.getLoc(),
1229 quotient_batch, quotient_k);
1232 Value
rem = arith::RemUIOp::create(rewriter, outerLoop.getLoc(),
1235 performShuffle(rewriter, outerLoop.getLoc(), rhsClone->getResult(0),
1236 ipType, blockingFactor, packedBuffer,
rem);
1239 auto newLoopNonSpill = scf::ForOp::create(
1240 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1241 spillOuterLoop, outerLoop.getStep(), loopItrArgs,
1242 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1243 Value ivOuterLoop,
ValueRange iterArgsOuterLoop) {
1244 auto newInnerLoop1 = createLoops(
1245 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1246 spillInnerLoop, innerLoop.getStep(), iterArgsOuterLoop,
1247 ipType, opType, blockingFactor, isVnni, vectorOpLhs,
1248 vectorOpRhs, contractOp, outerLoop, innerLoop, ops,
1249 ivOuterLoop, packedBuffer, true, spillLoopBound,
1250 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1252 auto newInnerLoop = createLoops(
1253 rewriter, innerLoop.getLoc(), spillInnerLoop,
1254 innerLoop.getUpperBound(), innerLoop.getStep(),
1255 newInnerLoop1.getResults(), ipType, opType, blockingFactor,
1256 isVnni, vectorOpLhs, vectorOpRhs, contractOp, outerLoop,
1257 innerLoop, ops, ivOuterLoop, packedBuffer, true, c0,
1258 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1260 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1261 newInnerLoop.getResults());
1265 newLoop = scf::ForOp::create(
1266 rewriter, outerLoop.getLoc(), spillOuterLoop,
1267 outerLoop.getUpperBound(), outerLoop.getStep(),
1268 newLoopNonSpill.getResults(),
1269 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1270 Value ivOuterLoop,
ValueRange iterArgsOuterLoop) {
1271 auto newInnerLoop1 = createLoops(
1272 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1273 spillInnerLoop, innerLoop.getStep(), iterArgsOuterLoop,
1274 ipType, opType, blockingFactor, isVnni, vectorOpLhs,
1275 vectorOpRhs, contractOp, outerLoop, innerLoop, ops,
1276 ivOuterLoop, packedBuffer, true, spillLoopBound,
1277 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1279 auto newInnerLoop = createLoops(
1280 rewriter, innerLoop.getLoc(), spillInnerLoop,
1281 innerLoop.getUpperBound(), innerLoop.getStep(),
1282 newInnerLoop1.getResults(), ipType, opType, blockingFactor,
1283 isVnni, vectorOpLhs, vectorOpRhs, contractOp, outerLoop,
1284 innerLoop, ops, ivOuterLoop, packedBuffer, false, c0,
1285 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1287 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1288 newInnerLoop.getResults());
1294 if (loopLists.size() == 1) {
1296 innerLoop = loopLists[0];
1297 int64_t stepValue = 16;
1299 stepValue = stepValue * blockingFactor;
1301 LogicalResult validateInnerLoopStep =
1302 validateLoopStep(rewriter, innerLoop.getStep(), stepValue);
1303 if (
failed(validateInnerLoopStep))
1306 "Invalid loop step. The step should be 32 for BF16 and "
1307 "64 for Int8/F8 or 1 if it is rduction loop other than K.");
1309 SmallVector<Value> loopItrArgs = createTileZeros(
1310 rewriter, innerLoop.getLoc(), opType, innerLoop, ops.size());
1313 newLoop = createLoops(
1314 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1315 innerLoop.getUpperBound(), innerLoop.getStep(), loopItrArgs, ipType,
1316 opType, blockingFactor, isVnni, vectorOpLhs, vectorOpRhs,
1317 contractOp,
nullptr, innerLoop, ops,
nullptr,
nullptr,
true,
1318 nullptr,
false,
false);
1322 bool isInnerLoopUBLarger =
false;
1323 bool isInnerLoopUBHasOddQuot =
false;
1325 int64_t ubVal = 16 * blockingFactor;
1326 mlir::Value ub = innerLoop.getUpperBound();
1327 if (
auto constOp = ub.
getDefiningOp<mlir::arith::ConstantOp>()) {
1329 llvm::dyn_cast<mlir::IntegerAttr>(constOp.getValue())) {
1330 ubVal = intAttr.getInt();
1334 isInnerLoopUBLarger = ubVal > 16 * blockingFactor;
1335 isInnerLoopUBHasOddQuot =
1336 (((ubVal / (16 * blockingFactor)) % 2) == 1) && isInnerLoopUBLarger;
1342 int64_t offset = 16 * blockingFactor;
1344 innerLoop.getStep().getDefiningOp<arith::ConstantIndexOp>())
1345 offset = cst.value();
1348 rewriter, innerLoop.getLoc(), offset);
1349 Value spillInnerLoop =
1350 arith::SubIOp::create(rewriter, innerLoop.getLoc(),
1351 innerLoop.getUpperBound(), spillLoopBound);
1354 MemRefType::get({2, 32, (blockingFactor * 16)}, ipType);
1356 memref::AllocaOp::create(rewriter, innerLoop.getLoc(), bufferType);
1359 IRMapping rhsMapping;
1361 vectorOpRhs->getOperand(
1362 getIndexPosition(contractOp.getRhs(), innerLoop) + 1),
1363 innerLoop.getLowerBound());
1364 auto rhsClone = rewriter.
clone(*vectorOpRhs, rhsMapping);
1366 Value quotient_k = arith::DivUIOp::create(rewriter, innerLoop.getLoc(),
1367 innerLoop.getLowerBound(),
1368 innerLoop.getStep());
1371 Value
rem = arith::RemUIOp::create(rewriter, innerLoop.getLoc(),
1374 performShuffle(rewriter, innerLoop.getLoc(), rhsClone->getResult(0),
1375 ipType, blockingFactor, packedBuffer,
rem);
1377 auto newLoopNonSpill = createLoops(
1378 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1379 spillInnerLoop, innerLoop.getStep(), loopItrArgs, ipType, opType,
1380 blockingFactor, isVnni, vectorOpLhs, vectorOpRhs, contractOp,
1381 nullptr, innerLoop, ops,
nullptr, packedBuffer,
true,
1382 spillLoopBound, isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1384 newLoop = createLoops(rewriter, innerLoop.getLoc(), spillInnerLoop,
1385 innerLoop.getUpperBound(), innerLoop.getStep(),
1386 newLoopNonSpill.getResults(), ipType, opType,
1387 blockingFactor, isVnni, vectorOpLhs, vectorOpRhs,
1388 contractOp,
nullptr, innerLoop, ops,
nullptr,
1389 packedBuffer,
false, c0, isInnerLoopUBLarger,
1390 isInnerLoopUBHasOddQuot);
1395 outerLoop = innerLoop;
1400 Location loc = outerLoop.getLoc();
1402 SmallVector<Value> indicesAcc;
1404 llvm::TypeSwitch<Operation *>(accReadOp).Case<TransferReadOp, LoadOp>(
1406 srcBuffAcc = readOp.getOperand(0);
1408 auto indices = readOp.getIndices();
1409 indicesAcc.reserve(
indices.size());
1411 llvm::transform(
indices, std::back_inserter(indicesAcc),
1412 [&](OpFoldResult ofr) {
1414 rewriter, loc, ofr);
1419 mlir::cast<mlir::MemRefType>(srcBuffAcc.
getType()).getShape();
1420 unsigned int M = outputShapes[outputShapes.size() - 2];
1421 unsigned int N = outputShapes[outputShapes.size() - 1];
1423 SmallVector<Value> dps = newLoop.getResults();
1424 auto bufferType = MemRefType::get({M, N}, opType);
1425 auto resultBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1428 for (
unsigned int i = 0, k = 0; i < M; i = i + 16) {
1429 for (
unsigned int j = 0; j < N; j = j + 16) {
1432 amx::TileStoreOp::create(rewriter, loc, resultBuffer,
1445 rewriter, loc, c0, nBound, one,
ValueRange{},
1446 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
1449 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1453 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1456 Value shuffle1 = row;
1457 Value shuffle2 = row2;
1460 shuffle1 = vector::ShuffleOp::create(
1461 rewriter, loc, VectorType::get(16, opType), row, row2,
1462 ArrayRef<int64_t>{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20,
1465 shuffle2 = vector::ShuffleOp::create(
1466 rewriter, loc, VectorType::get(16, opType), row, row2,
1467 ArrayRef<int64_t>{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15,
1470 indicesAcc[indicesAcc.size() - 2] = iv;
1471 indicesAcc[indicesAcc.size() - 1] = c0;
1474 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1475 srcBuffAcc, indicesAcc);
1476 indicesAcc[indicesAcc.size() - 1] = c16;
1479 vector::LoadOp::create(rewriter, loc, VectorType::get(16, opType),
1480 srcBuffAcc, indicesAcc);
1486 addOp = arith::AddFOp::create(rewriter, loc, shuffle1, valueCRow1);
1488 addOp2 = arith::AddFOp::create(rewriter, loc, shuffle2, valueCRow2);
1492 addOp = arith::AddIOp::create(rewriter, loc, shuffle1, valueCRow1);
1494 addOp2 = arith::AddIOp::create(rewriter, loc, shuffle2, valueCRow2);
1497 vector::StoreOp::create(rewriter, loc, addOp, resultBuffer,
1499 vector::StoreOp::create(rewriter, loc, addOp2, resultBuffer,
1502 scf::YieldOp::create(nestedBuilder, loc);
1505 SmallVector<Value> writeResults;
1506 for (
unsigned int i = 0; i < M; i = i + 16) {
1507 for (
unsigned int j = 0; j < N; j = j + 16) {
1511 auto vectorType = mlir::VectorType::get({16, 16}, opType);
1514 (dyn_cast<ShapedType>(resultBuffer.getType())).getRank();
1515 Value padding = ub::PoisonOp::create(rewriter, loc, opType);
1518 SmallVector<bool> inBounds(vectorType.getRank(),
true);
1520 auto vec1 = vector::TransferReadOp::create(
1521 rewriter, loc, vectorType, resultBuffer,
1522 ValueRange{indexOp_i, indexOp_j}, padding, map, inBounds);
1523 writeResults.push_back(vec1);
1528 for (
size_t i = 0; i < ops.size(); i++) {
1529 vector::ContractionOp contOp = ops[i];
1530 Value vecRow = writeResults[i];
1532 Value resultWriteOp = contractionUsersAfterYield(contOp.getResult());
1533 if (
auto vecType = llvm::dyn_cast<VectorType>(resultWriteOp.
getType()))
1534 vecRow = mlir::vector::ShapeCastOp::create(rewriter, loc, vecType,
1548 patterns.
add<VectorContractToAMXDotProduct>(patterns.
getContext());
static void contract(RootOrderingGraph &graph, ArrayRef< Value > cycle, const DenseMap< Value, unsigned > &parentDepths, DenseMap< Value, Value > &actualSource, DenseMap< Value, Value > &actualTarget)
Contracts the specified cycle in the given graph in-place.
static AffineMap getMinorIdentityMap(unsigned dims, unsigned results, MLIRContext *context)
Returns an identity affine map (d0, ..., dn) -> (dp, ..., dn) on the most minor dimensions.
IntegerAttr getIndexAttr(int64_t value)
FloatType getF8E5M2Type()
IntegerType getIntegerType(unsigned width)
MLIRContext * getContext() const
FloatType getF8E4M3FNType()
This is a utility class for mapping one set of IR entities to another.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
This class represents a single result from folding an operation.
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
Block * getBlock()
Returns the operation block that contains this operation.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
unsigned getNumOperands()
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
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.
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,...
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isSignlessInteger() const
Return true if this is a signless integer type (with the specified width).
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
unsigned getNumUses() const
This method computes the number of uses of this Value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
use_iterator use_begin() const
Specialization of arith.constant op that returns an integer of index type.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Operation * getOwner() const
Return the owner of this operand.
mlir::x86::AMXTileType TileType
Operation * traceToVectorWriteLikeUserOperation(Value v)
bool isInVnniLayout(Operation *op, llvm::ArrayRef< AffineMap > indexingMaps, std::optional< unsigned > blockingFactor=std::nullopt)
Operation * traceToVectorReadLikeParentOperation(Value v)
bool validatePairVectorContract(vector::ContractionOp contractOp, vector::ContractionOp pairContOp, bool rhsHasMultipleNonUnitDims, int64_t nonUnitDimValue)
void populateVectorContractToAMXDotProductPatterns(RewritePatternSet &patterns)
Include the generated interface declarations.
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
SmallVector< int64_t, 2 > ReassociationIndices
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.