19#include "llvm/Support/Casting.h"
32static bool isZeroVectorConstant(
Operation *op) {
33 auto constantOp = dyn_cast_if_present<arith::ConstantOp>(op);
37 if (!isa<VectorType>(constantOp.getType()))
40 auto denseAttr = dyn_cast<DenseElementsAttr>(constantOp.getValue());
41 if (!denseAttr || !denseAttr.isSplat())
56 if (!isa<scf::YieldOp>(user))
59 auto yield = cast<scf::YieldOp>(user);
69 ShapedType inputType = cast<ShapedType>(input.
getType());
70 int64_t firstDimToCollapse = inputType.getRank() - 2;
72 if (inputType.getRank() == 1)
76 for (
int64_t i = 0; i < firstDimToCollapse; ++i)
80 for (
int64_t i = firstDimToCollapse; i < inputType.getRank(); ++i)
81 collapsedIndices.push_back(i);
83 reassociation.push_back(collapsedIndices);
84 return memref::CollapseShapeOp::create(builder, loc, input, reassociation);
88static bool isReadSrcMemref(
Value operand) {
95 .Case<TransferReadOp, LoadOp>(
96 [&](
auto readOp) { srcBuff = readOp.getOperand(0); });
98 return srcBuff && isa<MemRefType>(srcBuff.
getType());
103static bool isVnniOperandRead(
Value operand,
int64_t vnni) {
112 srcBuff = readOp.getBase();
113 vnniIndex = readOp.getIndices().back();
118 auto srcType = dyn_cast<MemRefType>(srcBuff.
getType());
119 return srcType && srcType.getRank() > 0 &&
120 srcType.getShape().back() == vnni &&
isZeroInteger(vnniIndex);
129 Value scaled = arith::MulIOp::create(rewriter, loc,
indices.back(), cVnni);
130 indices.back() = arith::AddIOp::create(rewriter, loc, scaled, inner);
135static FailureOr<std::pair<Value, SmallVector<Value>>>
145 .Case<TransferReadOp, LoadOp>([&](
auto readOp) {
147 readOp.getIndices().end());
148 srcBuff = readOp.getOperand(0);
151 if (!srcBuff || !isa<MemRefType>(srcBuff.
getType()))
155 indices.reserve(indexVals.size());
163 collapseVnniIndices(rewriter, loc,
indices,
164 cast<MemRefType>(srcBuff.
getType()).getShape().back());
168 return std::make_pair(srcBuff,
indices);
172static LogicalResult validateLoopStep(
OpBuilder &rewriter,
Value step,
179 if (cst.value() != value && cst.value() != 1)
186static LogicalResult validateContractOps(
OpBuilder &rewriter,
187 vector::ContractionOp contractOp,
188 unsigned int blockingFactor,
194 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
195 contractOp.getLhs(),
false);
198 auto [buffLhs, indicesLhs] = *srcIndxLhs;
201 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
202 contractOp.getRhs(),
false);
205 auto [buffRhs, indicesRhs] = *srcIndxRhs;
208 if (buffLhs != srcBuffLhs)
211 if (buffRhs != srcBuffRhs)
218 VectorType accTy = dyn_cast<VectorType>(contractOp.getAccType());
225 llvm::copy_if(accShape, std::back_inserter(nonUnitDimAcc),
226 [](
int64_t dim) {
return (dim != 16 && dim != 1); });
228 if (nonUnitDimAcc.size() != 0)
233 VectorType lhsTy = contractOp.getLhsType();
236 llvm::copy_if(lhsShape, std::back_inserter(nonUnitDimLhs),
237 [](
int64_t dim) {
return (dim != 16 && dim != 1); });
239 if (nonUnitDimLhs.size() != 1)
242 if (nonUnitDimLhs[0] != blockingFactor)
247 VectorType rhsTy = contractOp.getRhsType();
250 llvm::copy_if(rhsShape, std::back_inserter(nonUnitDimRhs),
251 [](
int64_t dim) {
return (dim != 16 && dim != 1); });
253 if (nonUnitDimRhs.size() != 1)
256 if (nonUnitDimRhs[0] != blockingFactor)
266 while (
auto arg = dyn_cast<BlockArgument>(
acc)) {
267 auto loop = dyn_cast_if_present<scf::ForOp>(arg.getOwner()->getParentOp());
268 if (!loop || !llvm::is_contained(loops, loop))
270 OpOperand *init = loop.getTiedLoopInit(arg);
280static unsigned getIndexPosition(
Value operand, scf::ForOp loop) {
281 Value iv = loop.getInductionVar();
285 .Case<TransferReadOp, LoadOp>(
286 [&](
auto readOp) { srcBuff = readOp.getOperand(0); });
292 auto offsets = subview.getOffsets();
294 for (
auto it : llvm::enumerate(offsets)) {
295 if (it.value() == iv)
305 unsigned int offset,
bool isVnni) {
307 auto srcIndx = getSrcIndxValue(rewriter, loc, operand,
false);
308 auto [srcBuff,
indices] = *srcIndx;
310 collapseVnniIndices(rewriter, loc,
indices,
311 cast<MemRefType>(srcBuff.
getType()).getShape().back());
313 amx::TileType tileType = amx::TileType::get({16, (16 * offset)}, ipType);
314 return amx::TileLoadOp::create(rewriter, loc, tileType, mat,
indices);
318 Type ipType,
unsigned int offset,
Value packedBuffer,
319 Value indxToStoreInBuffer) {
324 llvm::cast<MemRefType>(matB.
getType()).getRank(), c0);
332 rewriter, loc, c0, cBound, cStep,
ValueRange{},
335 subviewOffset[subviewOffset.size() - 2] = iv;
339 auto vectorType = VectorType::get({2, (16 * (offset / 2))}, ipType);
341 vectorType = VectorType::get((16 * offset), ipType);
343 int64_t srcRank = (dyn_cast<ShapedType>(matB.
getType())).getRank();
344 Value padding = ub::PoisonOp::create(rewriter, loc, ipType);
348 Value vec1 = vector::TransferReadOp::create(
349 rewriter, loc, vectorType, matB,
ValueRange(subviewOffset), padding,
353 vec1 = vector::ShapeCastOp::create(
354 rewriter, loc, VectorType::get((16 * offset), ipType), vec1);
358 Value incIV = arith::AddIOp::create(rewriter, loc, offsetIndx, iv);
359 subviewOffset[subviewOffset.size() - 2] = incIV;
361 Value vec2 = vector::TransferReadOp::create(
362 rewriter, loc, vectorType, matB,
ValueRange(subviewOffset), padding,
365 vec2 = vector::ShapeCastOp::create(
366 rewriter, loc, VectorType::get((16 * offset), ipType), vec2);
368 vector::ShuffleOp writeVal1;
369 vector::ShuffleOp writeVal2;
373 writeVal1 = vector::ShuffleOp::create(
374 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
376 ArrayRef<int64_t>{0, 32, 1, 33, 2, 34, 3, 35, 8, 40, 9,
377 41, 10, 42, 11, 43, 16, 48, 17, 49, 18, 50,
378 19, 51, 24, 56, 25, 57, 26, 58, 27, 59});
380 writeVal2 = vector::ShuffleOp::create(
381 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
383 ArrayRef<int64_t>{4, 36, 5, 37, 6, 38, 7, 39, 12, 44, 13,
384 45, 14, 46, 15, 47, 20, 52, 21, 53, 22, 54,
385 23, 55, 28, 60, 29, 61, 30, 62, 31, 63});
391 writeVal1 = vector::ShuffleOp::create(
392 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
395 0, 32, 64, 96, 1, 33, 65, 97, 2, 34, 66, 98, 3,
396 35, 67, 99, 8, 40, 72, 104, 9, 41, 73, 105, 10, 42,
397 74, 106, 11, 43, 75, 107, 16, 48, 80, 112, 17, 49, 81,
398 113, 18, 50, 82, 114, 19, 51, 83, 115, 24, 56, 88, 120,
399 25, 57, 89, 121, 26, 58, 90, 122, 27, 59, 91, 123});
401 writeVal2 = vector::ShuffleOp::create(
402 rewriter, loc, VectorType::get({(16 * offset)}, ipType), vec1,
405 4, 36, 68, 100, 5, 37, 69, 101, 6, 38, 70, 102, 7, 39,
406 71, 103, 12, 44, 76, 108, 13, 45, 77, 109, 14, 46, 78, 110,
407 15, 47, 79, 111, 20, 52, 84, 116, 21, 53, 85, 117, 22, 54,
408 86, 118, 23, 55, 87, 119, 28, 60, 92, 124, 29, 61, 93, 125,
409 30, 62, 94, 126, 31, 63, 95, 127});
413 Value ivShuff1 = arith::DivUIOp::create(rewriter, loc, iv, cStep);
414 Value ivShuff2 = arith::AddIOp::create(rewriter, loc, ivShuff1, c16);
416 vector::StoreOp::create(rewriter, loc, writeVal1, packedBuffer,
417 ValueRange{indxToStoreInBuffer, ivShuff1, c0});
418 vector::StoreOp::create(rewriter, loc, writeVal2, packedBuffer,
419 ValueRange{indxToStoreInBuffer, ivShuff2, c0});
421 scf::YieldOp::create(nestedBuilder, loc);
428 unsigned int offset,
Value packedBuffer,
bool pack,
429 Value indxToStoreInBuffer,
Value indxToLoadFromMatB) {
435 for (
size_t j = 0;
j < ops.size();
j++) {
436 for (
size_t i = 0; i < ops.size(); i++) {
440 Operation *readOpRhs = ops[
j].getRhs().getDefiningOp();
441 auto itRhs = readsToTileLoads.find(readOpRhs);
442 if (itRhs != readsToTileLoads.end()) {
447 performShuffle(rewriter, loc, matB, ipType, offset, packedBuffer,
448 indxToStoreInBuffer);
452 amx::TileType::get({16, (16 * offset)}, ipType);
454 amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
458 amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
461 readsToTileLoads.try_emplace(readOpRhs, loadRow1);
462 readsToTileLoads.try_emplace(ops[i].getRhs().getDefiningOp(), loadRow2);
467 return readsToTileLoads;
475 unsigned int offset,
bool isVnni,
Value packedBuffer,
bool pack,
476 Value indxToStoreInBuffer,
Value indxToLoadFromMatB) {
491 packInputs(rewriter, loc, ops, matB, ipType, offset, packedBuffer, pack,
492 indxToStoreInBuffer, indxToLoadFromMatB);
496 for (
size_t i = 0; i < ops.size(); i++) {
498 Operation *readOpLhs = ops[i].getLhs().getDefiningOp();
499 amx::TileLoadOp tilesLhs;
500 auto itLhs = readsToTileLoads.find(readOpLhs);
501 if (itLhs != readsToTileLoads.end()) {
502 tilesLhs = itLhs->second;
504 tilesLhs = createTileLoads(rewriter, loc, ops[i].getLhs(), matA, ipType,
506 readsToTileLoads.try_emplace(readOpLhs, tilesLhs);
509 Operation *readOpRhs = ops[i].getRhs().getDefiningOp();
510 amx::TileLoadOp tilesRhs;
511 auto itRhs = readsToTileLoads.find(readOpRhs);
512 if (itRhs != readsToTileLoads.end()) {
513 tilesRhs = itRhs->second;
515 tilesRhs = createTileLoads(rewriter, loc, ops[i].getRhs(), matB, ipType,
517 readsToTileLoads.try_emplace(readOpRhs, tilesRhs);
520 auto accTileType = amx::TileType::get({16, 16}, opType);
524 dp = amx::TileMulFOp::create(rewriter, loc, accTileType, tilesLhs,
525 tilesRhs, accIterArgs[i]);
528 dp = amx::TileMulIOp::create(rewriter, loc, accTileType, tilesLhs,
529 tilesRhs, accIterArgs[i]);
531 accumulators.push_back(dp);
537 Type opType, scf::ForOp outerLoop,
543 auto zeroTileType = amx::TileType::get({16, 16}, opType);
545 for (
int i = 0; i < size; i++) {
546 auto zeroTile = amx::TileZeroOp::create(rewriter, loc, zeroTileType);
547 loopItrArgs.push_back(zeroTile);
555static std::optional<std::pair<Value, SmallVector<Value>>>
563 if (
auto transfer = dyn_cast<VectorTransferOpInterface>(op)) {
564 if (transfer.getMask() || transfer.hasOutOfBoundsDim() ||
565 !transfer.getPermutationMap().isMinorIdentity())
567 base = transfer.getBase();
568 vecTy = transfer.getVectorType();
569 indices = transfer.getIndices();
570 }
else if (
auto load = dyn_cast<vector::LoadOp>(op)) {
571 base =
load.getBase();
572 vecTy =
load.getVectorType();
574 }
else if (
auto store = dyn_cast<vector::StoreOp>(op)) {
575 base = store.getBase();
576 vecTy = store.getVectorType();
582 auto memrefTy = dyn_cast<MemRefType>(base.
getType());
584 if (!memrefTy || memrefTy.getRank() < 2 || !memrefTy.isLastDimUnitStride() ||
586 llvm::any_of(
shape.drop_back(2), [](
int64_t d) { return d != 1; }))
596static std::pair<std::optional<SmallVector<Operation *>>,
597 std::optional<SmallVector<Operation *>>>
600 for (vector::ContractionOp op : ops) {
603 return {std::nullopt, std::nullopt};
605 if (!isa<vector::TransferWriteOp, vector::StoreOp>(write) ||
607 return {std::nullopt, std::nullopt};
608 writes.push_back(write);
614 if (!read || !getAccTileAccess(read))
615 return {std::nullopt, std::nullopt};
616 reads.push_back(read);
620 return {std::nullopt, writes};
621 return std::make_pair(reads, writes);
626 Type opType, scf::ForOp loop,
630 auto tileType = amx::TileType::get({16, 16}, opType);
633 auto [base,
indices] = *getAccTileAccess(read);
635 amx::TileLoadOp::create(rewriter, loc, tileType, base,
indices));
643 bool isInnerLoopUBHasOddQuot,
644 bool isInnerLoopUBLarger,
645 bool pack,
Value blockStride) {
653 Value quotientInnerLoop =
654 arith::DivUIOp::create(rewriter, loc, ivInnerLoop, blockStride);
655 Value remInnerLoop = arith::RemUIOp::create(
656 rewriter, loc, rewriter.
getIndexType(), quotientInnerLoop, c2);
658 if (!isInnerLoopUBLarger && !pack) {
659 remInnerLoop = arith::RemUIOp::create(
660 rewriter, loc, rewriter.
getIndexType(), ivOuterLoop, c2);
663 if (isInnerLoopUBHasOddQuot) {
664 auto remOuterLoop = arith::RemUIOp::create(
665 rewriter, loc, rewriter.
getIndexType(), ivOuterLoop, c2);
666 auto remAdd = arith::AddIOp::create(rewriter, loc, rewriter.
getIndexType(),
667 remInnerLoop, remOuterLoop);
668 remInnerLoop = arith::RemUIOp::create(rewriter, loc,
678 Type ipType,
Type opType,
unsigned int blockingFactor,
bool isVnni,
680 vector::ContractionOp contractOp, scf::ForOp outerLoop,
682 Value ivOuterLoop,
Value packedBuffer,
bool pack,
684 bool isInnerLoopUBHasOddQuot) {
690 int64_t offset = 16 * blockingFactor;
692 offset = cst.value();
694 auto newLoop = scf::ForOp::create(
695 rewriter, loc, lowerBound, upperBound, step, loopItrArgs,
701 getIndexPosition(contractOp.getLhs(), outerLoop) + 1),
705 getIndexPosition(contractOp.getLhs(), innerLoop) + 1),
707 auto lhsClone = rewriterNewInnerLoop.
clone(*vectorOpLhs, mapping);
709 Value indxToStoreInBuffer = c0;
710 Value indxToLoadFromBuffer = c0;
713 if (innerLoopIndex.
value() == 0) {
716 ivOuterLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
719 if (!isInnerLoopUBLarger || isInnerLoopUBHasOddQuot) {
720 indxToStoreInBuffer = arith::RemUIOp::create(
725 Value indxToLoadFromMatB = arith::AddIOp::create(
726 rewriter, loc, indxToStoreInBuffer, c1);
727 indxToLoadFromBuffer = arith::RemUIOp::create(
728 rewriter, loc, rewriter.
getIndexType(), indxToLoadFromMatB,
734 rewriter, locNewInnerLoop, offset);
735 ivNewInnerLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
736 nLoadIndx, ivNewInnerLoop);
737 indxToStoreInBuffer = getIndxToLoadStoreFromPckBuffer(
738 rewriter, loc, ivNewInnerLoop, ivOuterLoop,
739 isInnerLoopUBHasOddQuot, isInnerLoopUBLarger, pack, step);
740 Value indxToLoadFromMatB =
741 arith::AddIOp::create(rewriter, loc, indxToStoreInBuffer, c1);
742 indxToLoadFromBuffer =
743 arith::RemUIOp::create(rewriter, loc, rewriter.
getIndexType(),
744 indxToLoadFromMatB, c2);
749 rewriter, locNewInnerLoop, offset);
750 ivNewInnerLoop = arith::AddIOp::create(rewriter, locNewInnerLoop,
751 nLoadIndx, ivNewInnerLoop);
752 Value quotient_K = arith::DivUIOp::create(
753 rewriter, loc, ivNewInnerLoop, nLoadIndx);
754 indxToStoreInBuffer = arith::RemUIOp::create(
755 rewriter, loc, rewriter.
getIndexType(), quotient_K, c2);
757 Value indxToLoadFromMatB =
758 arith::AddIOp::create(rewriter, loc, indxToStoreInBuffer, c1);
759 indxToLoadFromBuffer =
760 arith::RemUIOp::create(rewriter, loc, rewriter.
getIndexType(),
761 indxToLoadFromMatB, c2);
774 int64_t outerPos = getIndexPosition(contractOp.getRhs(), outerLoop);
777 unsigned operandIdx =
static_cast<unsigned>(outerPos + 1);
784 int64_t innerPos = getIndexPosition(contractOp.getRhs(), innerLoop);
787 unsigned operandIdx =
static_cast<unsigned>(innerPos + 1);
790 rhsMapping.
map(rhsOp->
getOperand(operandIdx), ivNewInnerLoop);
793 auto rhsClone = rewriterNewInnerLoop.
clone(*rhsOp, rhsMapping);
794 matB = rhsClone->getResult(0);
805 indxToLoadFromBuffer = c0;
811 indxToLoadFromBuffer = getIndxToLoadStoreFromPckBuffer(
812 rewriter, loc, ivNewInnerLoop, ivOuterLoop,
813 isInnerLoopUBHasOddQuot, isInnerLoopUBLarger, pack, step);
818 rewriter, locNewInnerLoop, offset);
820 Value quotient_K = arith::DivUIOp::create(
821 rewriter, loc, ivNewInnerLoop, nLoadIndx);
822 indxToLoadFromBuffer = arith::RemUIOp::create(
823 rewriter, loc, rewriter.
getIndexType(), quotient_K, c2);
829 rewriter, locNewInnerLoop, ops, lhsClone->getResult(0), matB,
830 ipType, opType, iterArgsNewInnerLoop, blockingFactor, isVnni,
831 packedBuffer, pack, indxToStoreInBuffer, indxToLoadFromBuffer);
833 scf::YieldOp::create(rewriterNewInnerLoop, locNewInnerLoop,
904struct VectorContractToAMXDotProduct
906 using OpRewritePattern<vector::ContractionOp>::OpRewritePattern;
908 LogicalResult matchAndRewrite(vector::ContractionOp contractOp,
909 PatternRewriter &rewriter)
const override {
911 if (contractOp.getKind() != vector::CombiningKind::ADD)
913 "Expects add combining kind.");
915 unsigned int blockingFactor =
916 contractOp.getLhsType().getElementType().isBF16() ? 2 : 4;
919 contractOp.getIndexingMapsArray(), blockingFactor);
921 VectorType lhsTy = contractOp.getLhsType();
922 if (!lhsTy.getElementType().isBF16() &&
923 !lhsTy.getElementType().isSignlessInteger(8) &&
924 !lhsTy.getElementType().isF8E4M3FN() &&
925 !lhsTy.getElementType().isF8E5M2())
927 contractOp,
"Only BF16/Int8/F8 lowering is supported.");
929 if (lhsTy.getElementType() != contractOp.getRhsType().getElementType())
931 contractOp,
"Contraction should have same lhs and rhs type.");
933 VectorType accTy = dyn_cast<VectorType>(contractOp.getAccType());
937 if (((lhsTy.getElementType().isBF16() ||
938 lhsTy.getElementType().isF8E4M3FN() ||
939 lhsTy.getElementType().isF8E5M2()) &&
940 !accTy.getElementType().isF32()) ||
941 (lhsTy.getElementType().isSignlessInteger(8) &&
942 !accTy.getElementType().isSignlessInteger(32)))
944 "Only F32 for BF16 or Int32 for Int8 "
945 "accumulation type is supported.");
947 Operation *accReadOp =
955 if (!accReadOp || !resultChainEnd)
957 contractOp,
"The ACC operand of the vector.contract should be a "
958 "transfer_read or a load. And, the result should have a "
959 "single-use chain to its consumer.");
961 bool isAccZeroVectorConstant = isZeroVectorConstant(accReadOp);
968 if (lhsTy.getElementType().isSignlessInteger(8)) {
973 if (lhsTy.getElementType().isF8E4M3FN())
976 if (lhsTy.getElementType().isF8E5M2())
979 if (accReadOp->
getBlock() == contractOp->getBlock() &&
980 resultBlock != contractOp->getBlock())
982 contractOp,
"The accumulator store is in different block.");
984 if (accReadOp->
getBlock() != contractOp->getBlock() &&
985 resultBlock == contractOp->getBlock())
987 contractOp,
"The accumulator read is in different block.");
989 if (!(isReadSrcMemref(contractOp.getLhs()) &&
990 isReadSrcMemref(contractOp.getRhs())))
992 contractOp,
"The LHS or RHS src is not a MemRef type.");
994 if (isVnni && !(isVnniOperandRead(contractOp.getLhs(), blockingFactor) &&
995 isVnniOperandRead(contractOp.getRhs(), blockingFactor)))
997 contractOp,
"The LHS or RHS src does not read whole VNNI groups.");
999 unsigned int dimValue = blockingFactor;
1001 dimValue = 16 * blockingFactor;
1005 if (accReadOp->
getBlock() == contractOp->getBlock() &&
1006 resultBlock == contractOp->getBlock()) {
1008 if (!isReadSrcMemref(contractOp.getAcc()))
1010 "The ACC src is not a MemRef type.");
1012 bool collapse =
false;
1016 LogicalResult validate = validateContractOps(
1017 rewriter, contractOp, dimValue, Value(), Value(),
false);
1021 contractOp,
"The contract operation doesn't satisfy the operands "
1022 "dimensions. M, N, and vnni dims are 16, 16, and 2/4. "
1023 "The rest dims should be 1. Op should have one user.");
1025 Location loc = contractOp.getLoc();
1027 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1028 contractOp.getLhs(), collapse);
1031 "Failed to get the LHS src.");
1032 auto [srcBuffLhs, indicesLhs] = *srcIndxLhs;
1034 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1035 contractOp.getRhs(), collapse);
1038 "Failed to get the RHS src.");
1039 auto rhsSrc = *srcIndxRhs;
1040 auto srcBuffRhs = rhsSrc.first;
1041 auto indicesRhs = rhsSrc.second;
1043 auto srcIndxAcc = getSrcIndxValue(rewriter, contractOp.getLoc(),
1044 contractOp.getAcc(),
false);
1047 "Failed to get the ACC src.");
1048 auto [srcBuffAcc, indicesAcc] = *srcIndxAcc;
1053 auto tileType = amx::TileType::get({16, (16 * blockingFactor)}, ipType);
1054 auto loadLhs = amx::TileLoadOp::create(rewriter, loc, tileType,
1055 srcBuffLhs, indicesLhs);
1058 amx::TileLoadOp loadRhs;
1061 SmallVector<OpFoldResult> indexVals;
1062 llvm::TypeSwitch<Operation *>(contractOp.getRhs().getDefiningOp())
1063 .Case<TransferReadOp, LoadOp>([&](
auto readOp) {
1064 indexVals = SmallVector<OpFoldResult>(readOp.getIndices().begin(),
1065 readOp.getIndices().end());
1066 vecTy = readOp.getType();
1069 SmallVector<OpFoldResult> strides(indexVals.size(), one);
1072 SmallVector<int64_t> subviewShape(indexVals.size() - vecTy.getRank(),
1074 llvm::append_range(subviewShape, vecTy.getShape());
1075 SmallVector<OpFoldResult> sizes =
1077 auto subview = memref::SubViewOp::create(rewriter, loc, srcBuffRhs,
1078 indexVals, sizes, strides);
1079 auto bufferType = MemRefType::get({16, (16 * blockingFactor)}, ipType);
1080 auto packedBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1086 (blockingFactor * 16));
1087 Value nextLoadIndx =
1090 rewriter, loc, 16 * (blockingFactor / 2));
1093 rewriter, loc, c0, uBound, step,
ValueRange{},
1094 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
1097 arith::AddIOp::create(rewriter, loc, nextLoadIndx, iv);
1101 size_t numOffsets = indicesRhs.size();
1102 SmallVector<Value>
indices(numOffsets, c0);
1104 auto vec1 = vector::LoadOp::create(
1106 VectorType::get(16 * (blockingFactor / 2), ipType), subview,
1109 indices[numOffsets - 2] = i1_load;
1110 auto vec2 = vector::LoadOp::create(
1112 VectorType::get(16 * (blockingFactor / 2), ipType), subview,
1115 vector::ShuffleOp writeVal1;
1116 vector::ShuffleOp writeVal2;
1118 if (blockingFactor == 2) {
1120 writeVal1 = vector::ShuffleOp::create(
1121 rewriter, loc, VectorType::get({16}, ipType), vec1, vec2,
1122 ArrayRef<int64_t>{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21,
1125 writeVal2 = vector::ShuffleOp::create(
1126 rewriter, loc, VectorType::get({16}, ipType), vec1, vec2,
1127 ArrayRef<int64_t>{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13,
1128 29, 14, 30, 15, 31});
1131 if (blockingFactor == 4) {
1132 writeVal1 = vector::ShuffleOp::create(
1133 rewriter, loc, VectorType::get({32}, ipType), vec1, vec2,
1134 ArrayRef<int64_t>{0, 16, 32, 48, 1, 17, 33, 49,
1135 2, 18, 34, 50, 3, 19, 35, 51,
1136 4, 20, 36, 52, 5, 21, 37, 53,
1137 6, 22, 38, 54, 7, 23, 39, 55});
1139 writeVal2 = vector::ShuffleOp::create(
1140 rewriter, loc, VectorType::get({32}, ipType), vec1, vec2,
1141 ArrayRef<int64_t>{8, 24, 40, 56, 9, 25, 41, 57,
1142 10, 26, 42, 58, 11, 27, 43, 59,
1143 12, 28, 44, 60, 13, 29, 45, 61,
1144 14, 30, 46, 62, 15, 31, 47, 63});
1147 auto rem = arith::DivUIOp::create(
1150 vector::StoreOp::create(rewriter, loc, writeVal1, packedBuffer,
1152 vector::StoreOp::create(rewriter, loc, writeVal2, packedBuffer,
1155 scf::YieldOp::create(nestedBuilder, loc);
1157 loadRhs = amx::TileLoadOp::create(rewriter, loc, tileType, packedBuffer,
1161 loadRhs = amx::TileLoadOp::create(rewriter, loc, tileType, srcBuffRhs,
1165 auto tileTypeAcc = amx::TileType::get({16, 16}, opType);
1166 auto loadAcc = amx::TileLoadOp::create(rewriter, loc, tileTypeAcc,
1167 srcBuffAcc, indicesAcc);
1172 dp = amx::TileMulFOp::create(rewriter, loc, tileTypeAcc, loadLhs,
1176 dp = amx::TileMulIOp::create(rewriter, loc, tileTypeAcc, loadLhs,
1179 auto bufferType = MemRefType::get({16, 16}, opType);
1180 auto resultBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1182 amx::TileStoreOp::create(rewriter, loc, resultBuffer,
ValueRange{c0, c0},
1185 auto vectorType = VectorType::get({16, 16}, opType);
1187 (dyn_cast<ShapedType>(resultBuffer.getType())).getRank();
1188 Value padding = ub::PoisonOp::create(rewriter, loc, opType);
1191 SmallVector<bool> inBounds(vectorType.getRank(),
true);
1193 Value vecRow = vector::TransferReadOp::create(
1194 rewriter, loc, vectorType, resultBuffer,
ValueRange{c0, c0}, padding,
1198 if (
auto vecType = dyn_cast<VectorType>(resultOp.getType()))
1199 vecRow = vector::ShapeCastOp::create(rewriter, loc, vecType, vecRow);
1209 SmallVector<scf::ForOp> loopLists;
1210 Operation *current = contractOp;
1220 if (!loopLists.empty())
1224 "Accumulator read and contract op not within scf.for op");
1227 loopLists.push_back(dyn_cast<scf::ForOp>(parent));
1229 if (resultBlock == parent->
getBlock()) {
1236 if (loopLists.size() > 2 || loopLists.size() == 0)
1238 contractOp,
"Rewrite is supported until reduction loop depth of 2.");
1240 auto srcIndxLhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1241 contractOp.getLhs(),
false);
1244 "Failed to get the LHS src.");
1245 auto [srcBuffLhs, indicesLhs] = *srcIndxLhs;
1247 auto srcIndxRhs = getSrcIndxValue(rewriter, contractOp.getLoc(),
1248 contractOp.getRhs(),
false);
1251 "Failed to get the RHS src.");
1252 auto [srcBuffRhs, indicesRhs] = *srcIndxRhs;
1253 Operation *vectorOpLhs;
1254 llvm::TypeSwitch<Operation *>(contractOp.getLhs().getDefiningOp())
1255 .Case<TransferReadOp, LoadOp>([&](
auto readOp) {
1256 vectorOpLhs = readOp.getBase().getDefiningOp();
1259 Operation *vectorOpRhs;
1260 llvm::TypeSwitch<Operation *>(contractOp.getRhs().getDefiningOp())
1261 .Case<TransferReadOp, LoadOp>([&](
auto readOp) {
1262 vectorOpRhs = readOp.getBase().getDefiningOp();
1265 if (!vectorOpLhs || !vectorOpRhs)
1267 contractOp,
"Failed to find LHS or RHS read source operation");
1270 SmallVector<vector::ContractionOp> ops;
1274 SmallVector<std::pair<size_t, Value>> carriedAccs;
1275 for (Operation &op : loopLists[0].getBody()->getOperations()) {
1277 if (
auto contract = dyn_cast<vector::ContractionOp>(op)) {
1278 if (isVnni && !(isVnniOperandRead(
contract.getLhs(), blockingFactor) &&
1279 isVnniOperandRead(
contract.getRhs(), blockingFactor)))
1281 contractOp,
"The LHS or RHS src of an associated contract "
1282 "operation does not read whole VNNI groups.");
1284 LogicalResult validate = validateContractOps(
1285 rewriter,
contract, dimValue, srcBuffLhs, srcBuffRhs,
true);
1290 "The associated contract operations doesn't satisfy "
1291 "the re-write conditions either the dimensions are "
1292 "wrong or MemRef source are different or many users.");
1294 Operation *contractReadOp =
1297 if (isAccZeroVectorConstant != isZeroVectorConstant(contractReadOp))
1299 contractOp,
"The input acc should be a zero constant or "
1300 "transfer_read for all contracts");
1302 Value accInit = getLoopNestInit(
contract.getAcc(), loopLists);
1304 bool carried = !isa_and_present<TransferReadOp, LoadOp>(accInitOp) &&
1305 !isZeroVectorConstant(accInitOp);
1307 if (loopLists.back().getRegion().isAncestor(
1310 contractOp,
"The accumulator should enter the reduction loops "
1311 "through their init args.");
1312 carriedAccs.emplace_back(ops.size(), accInit);
1321 if (!isAccZeroVectorConstant && !carriedAccs.empty() &&
1322 carriedAccs.size() != ops.size())
1324 contractOp,
"The accumulators read from memory can't be mixed with "
1325 "carried in accumulators.");
1328 unsigned int pairCount = 0;
1329 for (
size_t j = 0; j < ops.size(); j++) {
1330 for (
size_t i = j; i < ops.size(); i++) {
1332 pairCount = pairCount + 2;
1336 if (pairCount != ops.size())
1338 contractOp,
"Coudn't find the pair vector contract ");
1341 scf::ForOp innerLoop;
1342 scf::ForOp outerLoop;
1347 std::optional<SmallVector<Operation *>> inPlaceAccReads, inPlaceAccWrites;
1349 std::tie(inPlaceAccReads, inPlaceAccWrites) =
1350 getInPlaceAccAccesses(ops, isAccZeroVectorConstant);
1351 auto createInitTiles = [&](scf::ForOp loop) {
1352 if (inPlaceAccReads)
1353 return createAccTileLoads(rewriter, loop.getLoc(), opType, loop,
1355 return createTileZeros(rewriter, loop.getLoc(), opType, loop, ops.size());
1360 if (loopLists.size() == 2) {
1361 outerLoop = loopLists[1];
1362 innerLoop = loopLists[0];
1364 LogicalResult validateOuterLoopStep =
1365 validateLoopStep(rewriter, outerLoop.getStep(), 1);
1366 if (
failed(validateOuterLoopStep))
1369 int64_t stepValue = 16;
1371 stepValue = stepValue * blockingFactor;
1372 LogicalResult validateInnerLoopStep =
1373 validateLoopStep(rewriter, innerLoop.getStep(), stepValue);
1374 if (
failed(validateInnerLoopStep))
1376 contractOp,
"Invalid loop step. The step should be 32 for BF16 and "
1379 SmallVector<Value> loopItrArgs = createInitTiles(outerLoop);
1383 newLoop = scf::ForOp::create(
1384 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1385 outerLoop.getUpperBound(), outerLoop.getStep(), loopItrArgs,
1386 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1387 Value ivOuterLoop,
ValueRange iterArgsOuterLoop) {
1388 auto newInnerLoop = createLoops(
1389 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1390 innerLoop.getUpperBound(), innerLoop.getStep(),
1391 iterArgsOuterLoop, ipType, opType, blockingFactor, isVnni,
1392 vectorOpLhs, vectorOpRhs, contractOp, outerLoop, innerLoop,
1393 ops, ivOuterLoop, nullptr, true, nullptr, false, false);
1395 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1396 newInnerLoop.getResults());
1401 bool isInnerLoopUBLarger =
false;
1402 bool isInnerLoopUBHasOddQuot =
false;
1404 int64_t ubVal = 16 * blockingFactor;
1405 Value ub = innerLoop.getUpperBound();
1407 if (
auto intAttr = dyn_cast<IntegerAttr>(constOp.getValue())) {
1408 ubVal = intAttr.getInt();
1412 isInnerLoopUBLarger = ubVal > 16 * blockingFactor;
1413 isInnerLoopUBHasOddQuot =
1414 (((ubVal / (16 * blockingFactor)) % 2) == 1) && isInnerLoopUBLarger;
1423 rewriter, outerLoop.getLoc(), 16 * blockingFactor);
1425 Value spillOuterLoop = arith::SubIOp::create(
1426 rewriter, outerLoop.getLoc(), outerLoop.getUpperBound(), c1);
1427 Value spillInnerLoop =
1428 arith::SubIOp::create(rewriter, innerLoop.getLoc(),
1429 innerLoop.getUpperBound(), spillLoopBound);
1431 MemRefType::get({2, 32, (blockingFactor * 16)}, ipType);
1433 memref::AllocaOp::create(rewriter, outerLoop.getLoc(), bufferType);
1436 IRMapping rhsMapping;
1438 vectorOpRhs->getOperand(
1439 getIndexPosition(contractOp.getRhs(), outerLoop) + 1),
1440 outerLoop.getLowerBound());
1442 vectorOpRhs->getOperand(
1443 getIndexPosition(contractOp.getRhs(), innerLoop) + 1),
1444 innerLoop.getLowerBound());
1445 auto rhsClone = rewriter.
clone(*vectorOpRhs, rhsMapping);
1447 Value quotient_batch = arith::DivUIOp::create(
1448 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1449 outerLoop.getStep());
1450 Value quotient_k = arith::DivUIOp::create(rewriter, outerLoop.getLoc(),
1451 innerLoop.getLowerBound(),
1452 innerLoop.getStep());
1454 Value quotient_add = arith::AddIOp::create(rewriter, outerLoop.getLoc(),
1455 quotient_batch, quotient_k);
1458 Value
rem = arith::RemUIOp::create(rewriter, outerLoop.getLoc(),
1461 performShuffle(rewriter, outerLoop.getLoc(), rhsClone->getResult(0),
1462 ipType, blockingFactor, packedBuffer,
rem);
1465 auto newLoopNonSpill = scf::ForOp::create(
1466 rewriter, outerLoop.getLoc(), outerLoop.getLowerBound(),
1467 spillOuterLoop, outerLoop.getStep(), loopItrArgs,
1468 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1469 Value ivOuterLoop,
ValueRange iterArgsOuterLoop) {
1470 auto newInnerLoop1 = createLoops(
1471 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1472 spillInnerLoop, innerLoop.getStep(), iterArgsOuterLoop,
1473 ipType, opType, blockingFactor, isVnni, vectorOpLhs,
1474 vectorOpRhs, contractOp, outerLoop, innerLoop, ops,
1475 ivOuterLoop, packedBuffer, true, spillLoopBound,
1476 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1478 auto newInnerLoop = createLoops(
1479 rewriter, innerLoop.getLoc(), spillInnerLoop,
1480 innerLoop.getUpperBound(), innerLoop.getStep(),
1481 newInnerLoop1.getResults(), ipType, opType, blockingFactor,
1482 isVnni, vectorOpLhs, vectorOpRhs, contractOp, outerLoop,
1483 innerLoop, ops, ivOuterLoop, packedBuffer, true, c0,
1484 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1486 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1487 newInnerLoop.getResults());
1491 newLoop = scf::ForOp::create(
1492 rewriter, outerLoop.getLoc(), spillOuterLoop,
1493 outerLoop.getUpperBound(), outerLoop.getStep(),
1494 newLoopNonSpill.getResults(),
1495 [&](OpBuilder &rewriterOuterLoop, Location locOuterLoop,
1496 Value ivOuterLoop,
ValueRange iterArgsOuterLoop) {
1497 auto newInnerLoop1 = createLoops(
1498 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1499 spillInnerLoop, innerLoop.getStep(), iterArgsOuterLoop,
1500 ipType, opType, blockingFactor, isVnni, vectorOpLhs,
1501 vectorOpRhs, contractOp, outerLoop, innerLoop, ops,
1502 ivOuterLoop, packedBuffer, true, spillLoopBound,
1503 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1505 auto newInnerLoop = createLoops(
1506 rewriter, innerLoop.getLoc(), spillInnerLoop,
1507 innerLoop.getUpperBound(), innerLoop.getStep(),
1508 newInnerLoop1.getResults(), ipType, opType, blockingFactor,
1509 isVnni, vectorOpLhs, vectorOpRhs, contractOp, outerLoop,
1510 innerLoop, ops, ivOuterLoop, packedBuffer, false, c0,
1511 isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1513 scf::YieldOp::create(rewriterOuterLoop, locOuterLoop,
1514 newInnerLoop.getResults());
1520 if (loopLists.size() == 1) {
1522 innerLoop = loopLists[0];
1523 int64_t stepValue = 16;
1525 stepValue = stepValue * blockingFactor;
1527 LogicalResult validateInnerLoopStep =
1528 validateLoopStep(rewriter, innerLoop.getStep(), stepValue);
1529 if (
failed(validateInnerLoopStep))
1532 "Invalid loop step. The step should be 32 for BF16 and "
1533 "64 for Int8/F8 or 1 if it is rduction loop other than K.");
1535 SmallVector<Value> loopItrArgs = createInitTiles(innerLoop);
1539 newLoop = createLoops(
1540 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1541 innerLoop.getUpperBound(), innerLoop.getStep(), loopItrArgs, ipType,
1542 opType, blockingFactor, isVnni, vectorOpLhs, vectorOpRhs,
1543 contractOp,
nullptr, innerLoop, ops,
nullptr,
nullptr,
true,
1544 nullptr,
false,
false);
1548 bool isInnerLoopUBLarger =
false;
1549 bool isInnerLoopUBHasOddQuot =
false;
1551 int64_t ubVal = 16 * blockingFactor;
1552 Value ub = innerLoop.getUpperBound();
1554 if (
auto intAttr = dyn_cast<IntegerAttr>(constOp.getValue())) {
1555 ubVal = intAttr.getInt();
1559 isInnerLoopUBLarger = ubVal > 16 * blockingFactor;
1560 isInnerLoopUBHasOddQuot =
1561 (((ubVal / (16 * blockingFactor)) % 2) == 1) && isInnerLoopUBLarger;
1567 int64_t offset = 16 * blockingFactor;
1569 innerLoop.getStep().getDefiningOp<arith::ConstantIndexOp>())
1570 offset = cst.value();
1573 rewriter, innerLoop.getLoc(), offset);
1574 Value spillInnerLoop =
1575 arith::SubIOp::create(rewriter, innerLoop.getLoc(),
1576 innerLoop.getUpperBound(), spillLoopBound);
1579 MemRefType::get({2, 32, (blockingFactor * 16)}, ipType);
1581 memref::AllocaOp::create(rewriter, innerLoop.getLoc(), bufferType);
1584 IRMapping rhsMapping;
1586 vectorOpRhs->getOperand(
1587 getIndexPosition(contractOp.getRhs(), innerLoop) + 1),
1588 innerLoop.getLowerBound());
1589 auto rhsClone = rewriter.
clone(*vectorOpRhs, rhsMapping);
1591 Value quotient_k = arith::DivUIOp::create(rewriter, innerLoop.getLoc(),
1592 innerLoop.getLowerBound(),
1593 innerLoop.getStep());
1596 Value
rem = arith::RemUIOp::create(rewriter, innerLoop.getLoc(),
1599 performShuffle(rewriter, innerLoop.getLoc(), rhsClone->getResult(0),
1600 ipType, blockingFactor, packedBuffer,
rem);
1602 auto newLoopNonSpill = createLoops(
1603 rewriter, innerLoop.getLoc(), innerLoop.getLowerBound(),
1604 spillInnerLoop, innerLoop.getStep(), loopItrArgs, ipType, opType,
1605 blockingFactor, isVnni, vectorOpLhs, vectorOpRhs, contractOp,
1606 nullptr, innerLoop, ops,
nullptr, packedBuffer,
true,
1607 spillLoopBound, isInnerLoopUBLarger, isInnerLoopUBHasOddQuot);
1609 newLoop = createLoops(rewriter, innerLoop.getLoc(), spillInnerLoop,
1610 innerLoop.getUpperBound(), innerLoop.getStep(),
1611 newLoopNonSpill.getResults(), ipType, opType,
1612 blockingFactor, isVnni, vectorOpLhs, vectorOpRhs,
1613 contractOp,
nullptr, innerLoop, ops,
nullptr,
1614 packedBuffer,
false, c0, isInnerLoopUBLarger,
1615 isInnerLoopUBHasOddQuot);
1620 outerLoop = innerLoop;
1625 Location loc = outerLoop.getLoc();
1627 if (inPlaceAccWrites) {
1631 for (
auto [i, write] : llvm::enumerate(*inPlaceAccWrites)) {
1632 auto [base,
indices] = *getAccTileAccess(write);
1634 amx::TileStoreOp::create(rewriter, write->
getLoc(), base,
indices,
1635 newLoop.getResult(i));
1642 bool addAccFromMemory = !isAccZeroVectorConstant && carriedAccs.empty();
1648 SmallVector<Value> indicesAcc;
1650 if (addAccFromMemory) {
1653 llvm::TypeSwitch<Operation *>(accReadOp).Case<TransferReadOp, LoadOp>(
1655 srcBuffAcc = readOp.getOperand(0);
1657 auto indices = readOp.getIndices();
1658 indicesAcc.reserve(
indices.size());
1660 llvm::transform(
indices, std::back_inserter(indicesAcc),
1661 [&](OpFoldResult ofr) {
1663 rewriter, loc, ofr);
1666 auto outputShapes = cast<MemRefType>(srcBuffAcc.
getType()).getShape();
1667 M = outputShapes[outputShapes.size() - 2];
1668 N = outputShapes[outputShapes.size() - 1];
1674 M = isVnni ? ops.size() * 16 : ops.size() / 2 * 16;
1675 N = isVnni ? 16 : 2 * 16;
1678 SmallVector<Value> dps = newLoop.getResults();
1679 auto bufferType = MemRefType::get({M, N}, opType);
1680 auto resultBuffer = memref::AllocaOp::create(rewriter, loc, bufferType);
1683 for (
unsigned int i = 0, k = 0; i < M; i = i + 16) {
1684 for (
unsigned int j = 0; j < N; j = j + 16) {
1687 amx::TileStoreOp::create(rewriter, loc, resultBuffer,
1698 if (!isVnni || addAccFromMemory) {
1703 rewriter, loc, c0, mBound, one,
ValueRange{},
1704 [&](OpBuilder &nestedBuilder, Location loc, Value iv,
1706 auto row = vector::LoadOp::create(rewriter, loc,
1707 VectorType::get(16, opType),
1710 auto row2 = vector::LoadOp::create(
1711 rewriter, loc, VectorType::get(16, opType), resultBuffer,
1714 Value writeVal1 = row;
1715 Value writeVal2 = row2;
1718 writeVal1 = vector::ShuffleOp::create(
1719 rewriter, loc, VectorType::get(16, opType), row, row2,
1720 ArrayRef<int64_t>{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20,
1723 writeVal2 = vector::ShuffleOp::create(
1724 rewriter, loc, VectorType::get(16, opType), row, row2,
1725 ArrayRef<int64_t>{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14,
1726 15, 28, 29, 30, 31});
1729 if (addAccFromMemory) {
1730 indicesAcc[indicesAcc.size() - 2] = iv;
1731 indicesAcc[indicesAcc.size() - 1] = c0;
1733 Value valueCRow1 = vector::LoadOp::create(
1734 rewriter, loc, VectorType::get(16, opType), srcBuffAcc,
1736 indicesAcc[indicesAcc.size() - 1] = c16;
1738 Value valueCRow2 = vector::LoadOp::create(
1739 rewriter, loc, VectorType::get(16, opType), srcBuffAcc,
1744 arith::AddFOp::create(rewriter, loc, writeVal1, valueCRow1);
1747 arith::AddFOp::create(rewriter, loc, writeVal2, valueCRow2);
1752 arith::AddIOp::create(rewriter, loc, writeVal1, valueCRow1);
1755 arith::AddIOp::create(rewriter, loc, writeVal2, valueCRow2);
1759 vector::StoreOp::create(rewriter, loc, writeVal1, resultBuffer,
1761 vector::StoreOp::create(rewriter, loc, writeVal2, resultBuffer,
1764 scf::YieldOp::create(nestedBuilder, loc);
1768 SmallVector<Value> writeResults;
1769 for (
unsigned int i = 0; i < M; i = i + 16) {
1770 for (
unsigned int j = 0; j < N; j = j + 16) {
1774 auto vectorType = VectorType::get({16, 16}, opType);
1777 (dyn_cast<ShapedType>(resultBuffer.getType())).getRank();
1778 Value padding = ub::PoisonOp::create(rewriter, loc, opType);
1781 SmallVector<bool> inBounds(vectorType.getRank(),
true);
1783 auto vec1 = vector::TransferReadOp::create(
1784 rewriter, loc, vectorType, resultBuffer,
1785 ValueRange{indexOp_i, indexOp_j}, padding, map, inBounds);
1786 writeResults.push_back(vec1);
1790 for (
auto [idx, acc] : carriedAccs) {
1791 Type resultType = writeResults[idx].getType();
1792 if (acc.
getType() != resultType)
1793 acc = vector::ShapeCastOp::create(rewriter, loc, resultType, acc);
1794 if (isa<FloatType>(opType))
1796 arith::AddFOp::create(rewriter, loc, writeResults[idx], acc);
1799 arith::AddIOp::create(rewriter, loc, writeResults[idx], acc);
1803 for (
size_t i = 0; i < ops.size(); i++) {
1804 vector::ContractionOp contOp = ops[i];
1805 Value vecRow = writeResults[i];
1808 if (
auto vecType = dyn_cast<VectorType>(resultWriteOp.
getType()))
1809 vecRow = vector::ShapeCastOp::create(rewriter, loc, vecType,
1823 patterns.
add<VectorContractToAMXDotProduct>(patterns.
getContext());
getNumOperands() - 1))) return failure()
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
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.
Attributes are known-constant values of operations.
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'.
IRValueT get() const
Return the current value being used by this operand.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
RAII guard to reset the insertion point of the builder when destroyed.
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.
Location getLoc()
The source location the operation was defined or derived from.
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.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
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.
user_iterator user_begin() const
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.
Region * getParentRegion()
Return the Region in which this Value is defined.
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
bool isInVnniLayout(Operation *op, llvm::ArrayRef< AffineMap > indexingMaps, std::optional< unsigned > blockingFactor=std::nullopt)
Value contractionUsersAfterYield(Value v)
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.
bool isZeroIntegerOrFloat(OpFoldResult v)
Return "true" if v is an integer/float value/attribute with constant value zero.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
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.