23#include "llvm/ADT/SetVector.h"
24#include "llvm/ADT/SmallBitVector.h"
25#include "llvm/ADT/SmallVectorExtras.h"
26#include "llvm/Support/FormatVariadic.h"
45 VectorType distributedType) {
51 for (
unsigned i = 0, e = sequentialType.getRank(); i < e; i++) {
52 if (sequentialType.getDimSize(i) != distributedType.getDimSize(i))
56 distributedType.getContext());
64 VectorType distributedType) {
65 assert(sequentialType.getRank() == distributedType.getRank() &&
66 "sequential and distributed vector types must have the same rank");
68 for (
int64_t i = 0; i < sequentialType.getRank(); ++i) {
69 if (distributedType.getDimSize(i) != sequentialType.getDimSize(i)) {
72 assert(distributedDim == -1 &&
"found multiple distributed dims");
76 return distributedDim;
86struct DistributedLoadStoreHelper {
87 DistributedLoadStoreHelper(Value sequentialVal, Value distributedVal,
88 Value laneId, Value zero)
89 : sequentialVal(sequentialVal), distributedVal(distributedVal),
90 laneId(laneId), zero(zero) {
91 sequentialVectorType = dyn_cast<VectorType>(sequentialVal.getType());
92 distributedVectorType = dyn_cast<VectorType>(distributedVal.getType());
93 if (sequentialVectorType && distributedVectorType)
98 Value buildDistributedOffset(RewriterBase &
b, Location loc, int64_t index) {
99 int64_t distributedSize = distributedVectorType.getDimSize(index);
101 return b.createOrFold<affine::AffineApplyOp>(loc, tid * distributedSize,
102 ArrayRef<Value>{laneId});
112 Operation *buildStore(RewriterBase &
b, Location loc, Value val,
114 assert((val == distributedVal || val == sequentialVal) &&
115 "Must store either the preregistered distributed or the "
116 "preregistered sequential value.");
118 if (!isa<VectorType>(val.
getType()))
119 return memref::StoreOp::create(
b, loc, val, buffer, zero);
123 int64_t rank = sequentialVectorType.getRank();
124 SmallVector<Value>
indices(rank, zero);
125 if (val == distributedVal) {
126 for (
auto dimExpr : distributionMap.getResults()) {
127 int64_t index = cast<AffineDimExpr>(dimExpr).getPosition();
128 indices[index] = buildDistributedOffset(
b, loc, index);
131 SmallVector<bool> inBounds(
indices.size(),
true);
132 return vector::TransferWriteOp::create(
134 ArrayRef<bool>(inBounds.begin(), inBounds.end()));
157 Value buildLoad(RewriterBase &
b, Location loc, Type type, Value buffer) {
160 if (!isa<VectorType>(type))
161 return memref::LoadOp::create(
b, loc, buffer, zero);
166 assert((type == distributedVectorType || type == sequentialVectorType) &&
167 "Must store either the preregistered distributed or the "
168 "preregistered sequential type.");
169 SmallVector<Value>
indices(sequentialVectorType.getRank(), zero);
170 if (type == distributedVectorType) {
171 for (
auto dimExpr : distributionMap.getResults()) {
172 int64_t index = cast<AffineDimExpr>(dimExpr).getPosition();
173 indices[index] = buildDistributedOffset(
b, loc, index);
176 SmallVector<bool> inBounds(
indices.size(),
true);
177 return vector::TransferReadOp::create(
178 b, loc, cast<VectorType>(type), buffer,
indices,
180 ArrayRef<bool>(inBounds.begin(), inBounds.end()));
183 Value sequentialVal, distributedVal, laneId, zero;
184 VectorType sequentialVectorType, distributedVectorType;
185 AffineMap distributionMap;
199 return rewriter.
create(res);
233 WarpOpToScfIfPattern(MLIRContext *context,
234 const WarpExecuteOnLane0LoweringOptions &options,
235 PatternBenefit benefit = 1)
236 : WarpDistributionPattern(context, benefit), options(options) {}
238 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
239 PatternRewriter &rewriter)
const override {
240 assert(warpOp.getBodyRegion().hasOneBlock() &&
241 "expected WarpOp with single block");
242 Block *warpOpBody = &warpOp.getBodyRegion().front();
243 Location loc = warpOp.getLoc();
246 OpBuilder::InsertionGuard g(rewriter);
251 Value isLane0 = arith::CmpIOp::create(
252 rewriter, loc, arith::CmpIPredicate::eq, warpOp.getLaneid(), c0);
253 auto ifOp = scf::IfOp::create(rewriter, loc, isLane0,
255 rewriter.
eraseOp(ifOp.thenBlock()->getTerminator());
259 SmallVector<Value> bbArgReplacements;
260 for (
const auto &it : llvm::enumerate(warpOp.getArgs())) {
261 Value sequentialVal = warpOpBody->
getArgument(it.index());
262 Value distributedVal = it.value();
263 DistributedLoadStoreHelper helper(sequentialVal, distributedVal,
264 warpOp.getLaneid(), c0);
268 Value buffer = options.warpAllocationFn(loc, rewriter, warpOp,
271 helper.buildStore(rewriter, loc, distributedVal, buffer);
274 bbArgReplacements.push_back(
275 helper.buildLoad(rewriter, loc, sequentialVal.
getType(), buffer));
279 if (!warpOp.getArgs().empty()) {
281 options.warpSynchronizationFn(loc, rewriter, warpOp);
285 rewriter.
mergeBlocks(warpOpBody, ifOp.thenBlock(), bbArgReplacements);
291 SmallVector<Value> replacements;
292 auto yieldOp = cast<gpu::YieldOp>(ifOp.thenBlock()->getTerminator());
293 Location yieldLoc = yieldOp.getLoc();
294 for (
const auto &it : llvm::enumerate(yieldOp.getOperands())) {
295 Value sequentialVal = it.value();
296 Value distributedVal = warpOp->getResult(it.index());
297 DistributedLoadStoreHelper helper(sequentialVal, distributedVal,
298 warpOp.getLaneid(), c0);
302 Value buffer = options.warpAllocationFn(loc, rewriter, warpOp,
308 helper.buildStore(rewriter, loc, sequentialVal, buffer);
319 replacements.push_back(
320 helper.buildLoad(rewriter, loc, distributedVal.
getType(), buffer));
324 if (!yieldOp.getOperands().empty()) {
326 options.warpSynchronizationFn(loc, rewriter, warpOp);
332 scf::YieldOp::create(rewriter, yieldLoc);
335 rewriter.
replaceOp(warpOp, replacements);
341 const WarpExecuteOnLane0LoweringOptions &options;
354static VectorType getDistributedType(VectorType originalType,
AffineMap map,
362 if (targetShape[position] % warpSize != 0) {
363 if (warpSize % targetShape[position] != 0) {
366 warpSize /= targetShape[position];
367 targetShape[position] = 1;
370 targetShape[position] = targetShape[position] / warpSize;
377 VectorType targetType =
378 VectorType::get(targetShape, originalType.getElementType());
388getInnerRegionEscapingValues(WarpExecuteOnLane0Op warpOp,
Region &innerRegion,
390 llvm::SmallSetVector<Value, 32> escapingValues;
393 if (innerRegion.
empty())
394 return {std::move(escapingValues), std::move(escapingValueTypes),
395 std::move(escapingValueDistTypes)};
398 if (warpOp->isAncestor(parent)) {
399 if (!escapingValues.insert(operand->
get()))
402 if (
auto vecType = dyn_cast<VectorType>(distType)) {
404 distType = getDistributedType(vecType, map,
405 map.
isEmpty() ? 1 : warpOp.getWarpSize());
407 escapingValueTypes.push_back(operand->
get().
getType());
408 escapingValueDistTypes.push_back(distType);
411 return {std::move(escapingValues), std::move(escapingValueTypes),
412 std::move(escapingValueDistTypes)};
436 unsigned maxNumElementsToExtract, PatternBenefit
b = 1)
437 : WarpDistributionPattern(ctx,
b), distributionMapFn(std::move(fn)),
438 maxNumElementsToExtract(maxNumElementsToExtract) {}
442 LogicalResult tryDistributeOp(RewriterBase &rewriter,
443 vector::TransferWriteOp writeOp,
444 WarpExecuteOnLane0Op warpOp)
const {
445 VectorType writtenVectorType = writeOp.getVectorType();
449 if (writtenVectorType.getRank() == 0)
453 AffineMap map = distributionMapFn(writeOp.getVector());
454 VectorType targetType =
455 getDistributedType(writtenVectorType, map, warpOp.getWarpSize());
461 if (writeOp.getMask()) {
468 if (!writeOp.getPermutationMap().isMinorIdentity())
471 getDistributedType(writeOp.getMaskType(), map, warpOp.getWarpSize());
476 vector::TransferWriteOp newWriteOp =
477 cloneWriteOp(rewriter, warpOp, writeOp, targetType, maskType);
481 newWriteOp.getVector().getDefiningOp<WarpExecuteOnLane0Op>();
487 SmallVector<OpFoldResult> delinearizedIdSizes;
488 for (
auto [seqSize, distSize] :
489 llvm::zip_equal(writtenVectorType.getShape(), targetType.getShape())) {
490 assert(seqSize % distSize == 0 &&
"Invalid distributed vector shape");
491 delinearizedIdSizes.push_back(rewriter.
getIndexAttr(seqSize / distSize));
493 SmallVector<Value> delinearized;
495 delinearized = mlir::affine::AffineDelinearizeIndexOp::create(
496 rewriter, newWarpOp.getLoc(), newWarpOp.getLaneid(),
502 delinearized.append(targetType.getRank(), newWarpOp.getLaneid());
505 AffineMap indexMap = map.
compose(newWriteOp.getPermutationMap());
506 Location loc = newWriteOp.getLoc();
507 SmallVector<Value>
indices(newWriteOp.getIndices().begin(),
508 newWriteOp.getIndices().end());
511 bindDims(newWarpOp.getContext(), d0, d1);
512 auto indexExpr = dyn_cast<AffineDimExpr>(std::get<0>(it));
515 unsigned indexPos = indexExpr.getPosition();
516 unsigned vectorPos = cast<AffineDimExpr>(std::get<1>(it)).getPosition();
517 Value laneId = delinearized[vectorPos];
521 rewriter, loc, d0 + scale * d1, {
indices[indexPos], laneId});
523 newWriteOp.getIndicesMutable().assign(
indices);
529 LogicalResult tryExtractOp(RewriterBase &rewriter,
530 vector::TransferWriteOp writeOp,
531 WarpExecuteOnLane0Op warpOp)
const {
532 Location loc = writeOp.getLoc();
533 VectorType vecType = writeOp.getVectorType();
535 if (vecType.getNumElements() > maxNumElementsToExtract) {
539 "writes more elements ({0}) than allowed to extract ({1})",
540 vecType.getNumElements(), maxNumElementsToExtract));
544 if (llvm::all_of(warpOp.getOps(),
545 llvm::IsaPred<vector::TransferWriteOp, gpu::YieldOp>))
548 SmallVector<Value> yieldValues = {writeOp.getVector()};
549 SmallVector<Type> retTypes = {vecType};
550 SmallVector<size_t> newRetIndices;
552 rewriter, warpOp, yieldValues, retTypes, newRetIndices);
556 auto secondWarpOp = WarpExecuteOnLane0Op::create(rewriter, loc,
TypeRange(),
557 newWarpOp.getLaneid(),
558 newWarpOp.getWarpSize());
559 Block &body = secondWarpOp.getBodyRegion().front();
562 cast<vector::TransferWriteOp>(rewriter.
clone(*writeOp.getOperation()));
563 newWriteOp.getValueToStoreMutable().assign(
564 newWarpOp.getResult(newRetIndices[0]));
566 gpu::YieldOp::create(rewriter, newWarpOp.getLoc());
571 PatternRewriter &rewriter)
const override {
572 gpu::YieldOp yield = warpOp.getTerminator();
573 Operation *lastNode = yield->getPrevNode();
574 auto writeOp = dyn_cast_or_null<vector::TransferWriteOp>(lastNode);
578 Value maybeMask = writeOp.getMask();
579 if (!llvm::all_of(writeOp->getOperands(), [&](Value value) {
580 return writeOp.getVector() == value ||
581 (maybeMask && maybeMask == value) ||
582 warpOp.isDefinedOutsideOfRegion(value);
586 if (succeeded(tryDistributeOp(rewriter, writeOp, warpOp)))
590 if (writeOp.getMask())
593 if (succeeded(tryExtractOp(rewriter, writeOp, warpOp)))
603 vector::TransferWriteOp cloneWriteOp(RewriterBase &rewriter,
604 WarpExecuteOnLane0Op warpOp,
605 vector::TransferWriteOp writeOp,
606 VectorType targetType,
607 VectorType maybeMaskType)
const {
608 assert(writeOp->getParentOp() == warpOp &&
609 "write must be nested immediately under warp");
610 OpBuilder::InsertionGuard g(rewriter);
611 SmallVector<size_t> newRetIndices;
612 WarpExecuteOnLane0Op newWarpOp;
615 rewriter, warpOp,
ValueRange{writeOp.getVector(), writeOp.getMask()},
616 TypeRange{targetType, maybeMaskType}, newRetIndices);
619 rewriter, warpOp,
ValueRange{{writeOp.getVector()}},
624 cast<vector::TransferWriteOp>(rewriter.
clone(*writeOp.getOperation()));
626 newWriteOp.getValueToStoreMutable().assign(
627 newWarpOp.getResult(newRetIndices[0]));
629 newWriteOp.getMaskMutable().assign(newWarpOp.getResult(newRetIndices[1]));
634 unsigned maxNumElementsToExtract = 1;
657 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
658 PatternRewriter &rewriter)
const override {
659 OpOperand *yieldOperand = getWarpResult(warpOp, [](Operation *op) {
667 Value distributedVal = warpOp.getResult(operandIndex);
668 SmallVector<Value> yieldValues;
669 SmallVector<Type> retTypes;
670 Location loc = warpOp.getLoc();
673 if (
auto vecType = dyn_cast<VectorType>(distributedVal.
getType())) {
675 auto operandType = cast<VectorType>(operand.
get().
getType());
677 VectorType::get(vecType.getShape(), operandType.getElementType());
680 assert(!isa<VectorType>(operandType) &&
681 "unexpected yield of vector from op with scalar result type");
682 targetType = operandType;
684 retTypes.push_back(targetType);
685 yieldValues.push_back(operand.
get());
687 SmallVector<size_t> newRetIndices;
688 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
689 rewriter, warpOp, yieldValues, retTypes, newRetIndices);
691 SmallVector<Value> newOperands(elementWise->
getOperands().begin(),
693 for (
unsigned i : llvm::seq(
unsigned(0), elementWise->
getNumOperands())) {
694 newOperands[i] = newWarpOp.getResult(newRetIndices[i]);
696 OpBuilder::InsertionGuard g(rewriter);
699 rewriter, loc, elementWise, newOperands,
700 {newWarpOp.getResult(operandIndex).getType()});
724 PatternRewriter &rewriter)
const override {
725 OpOperand *yieldOperand =
730 auto dense = dyn_cast<SplatElementsAttr>(constantOp.getValue());
737 Attribute scalarAttr = dense.getSplatValue<Attribute>();
739 cast<ShapedType>(warpOp.getResult(operandIndex).getType()), scalarAttr);
740 Location loc = warpOp.getLoc();
742 Value distConstant = arith::ConstantOp::create(rewriter, loc, newAttr);
771 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
772 PatternRewriter &rewriter)
const override {
773 OpOperand *yieldOperand =
774 getWarpResult(warpOp, llvm::IsaPred<vector::StepOp>);
780 if (resTy.getNumElements() !=
static_cast<int64_t
>(warpOp.getWarpSize()))
783 llvm::formatv(
"Expected result size ({0}) to be of warp size ({1})",
784 resTy.getNumElements(), warpOp.getWarpSize()));
785 VectorType newVecTy =
786 cast<VectorType>(warpOp.getResult(operandIdx).getType());
788 Value laneIdVec = vector::BroadcastOp::create(rewriter, warpOp.getLoc(),
789 newVecTy, warpOp.getLaneid());
816 PatternRewriter &rewriter)
const override {
820 OpOperand *operand =
getWarpResult(warpOp, [](Operation *op) {
822 return isa<vector::TransferReadOp>(op) && op->
hasOneUse();
826 warpOp,
"warp result is not a vector.transfer_read op");
830 if (!warpOp.isDefinedOutsideOfRegion(read.getBase()))
832 read,
"source must be defined outside of the region");
835 Value distributedVal = warpOp.getResult(operandIndex);
837 SmallVector<Value, 4>
indices(read.getIndices().begin(),
838 read.getIndices().end());
839 auto sequentialType = cast<VectorType>(read.getResult().getType());
840 auto distributedType = cast<VectorType>(distributedVal.
getType());
842 AffineMap indexMap = map.
compose(read.getPermutationMap());
846 SmallVector<Value> delinearizedIds;
848 distributedType.getShape(), warpOp.getWarpSize(),
849 warpOp.getLaneid(), delinearizedIds)) {
851 read,
"cannot delinearize lane ID for distribution");
853 assert(!delinearizedIds.empty() || map.
getNumResults() == 0);
856 OpBuilder::InsertionGuard g(rewriter);
857 SmallVector<Value> additionalResults(
indices.begin(),
indices.end());
858 SmallVector<Type> additionalResultTypes(
indices.size(),
860 additionalResults.push_back(read.getPadding());
861 additionalResultTypes.push_back(read.getPadding().getType());
863 bool hasMask =
false;
864 if (read.getMask()) {
874 read,
"non-trivial permutation maps not supported");
875 VectorType maskType =
876 getDistributedType(read.getMaskType(), map, warpOp.getWarpSize());
877 additionalResults.push_back(read.getMask());
878 additionalResultTypes.push_back(maskType);
881 SmallVector<size_t> newRetIndices;
883 rewriter, warpOp, additionalResults, additionalResultTypes,
885 distributedVal = newWarpOp.getResult(operandIndex);
888 SmallVector<Value> newIndices;
889 for (int64_t i = 0, e =
indices.size(); i < e; ++i)
890 newIndices.push_back(newWarpOp.getResult(newRetIndices[i]));
895 bindDims(read.getContext(), d0, d1);
896 auto indexExpr = dyn_cast<AffineDimExpr>(std::get<0>(it));
899 unsigned indexPos = indexExpr.getPosition();
900 unsigned vectorPos = cast<AffineDimExpr>(std::get<1>(it)).getPosition();
901 int64_t scale = distributedType.getDimSize(vectorPos);
903 rewriter, read.getLoc(), d0 + scale * d1,
904 {newIndices[indexPos], delinearizedIds[vectorPos]});
908 Value newPadding = newWarpOp.getResult(newRetIndices[
indices.size()]);
911 hasMask ? newWarpOp.getResult(newRetIndices[newRetIndices.size() - 1])
913 auto newRead = vector::TransferReadOp::create(
914 rewriter, read.getLoc(), distributedVal.
getType(), read.getBase(),
915 newIndices, read.getPermutationMapAttr(), newPadding, newMask,
916 read.getInBoundsAttr());
928 PatternRewriter &rewriter)
const override {
929 SmallVector<Type> newResultTypes;
930 newResultTypes.reserve(warpOp->getNumResults());
931 SmallVector<Value> newYieldValues;
932 newYieldValues.reserve(warpOp->getNumResults());
935 gpu::YieldOp yield = warpOp.getTerminator();
946 for (OpResult
result : warpOp.getResults()) {
949 Value yieldOperand = yield.getOperand(
result.getResultNumber());
950 auto it = dedupYieldOperandPositionMap.insert(
951 std::make_pair(yieldOperand, newResultTypes.size()));
952 dedupResultPositionMap.insert(std::make_pair(
result, it.first->second));
955 newResultTypes.push_back(
result.getType());
956 newYieldValues.push_back(yieldOperand);
959 if (yield.getNumOperands() == newYieldValues.size())
963 rewriter, warpOp, newYieldValues, newResultTypes);
966 newWarpOp.getBody()->walk([&](Operation *op) {
972 SmallVector<Value> newValues;
973 newValues.reserve(warpOp->getNumResults());
974 for (OpResult
result : warpOp.getResults()) {
976 newValues.push_back(Value());
979 newWarpOp.getResult(dedupResultPositionMap.lookup(
result)));
991 PatternRewriter &rewriter)
const override {
992 gpu::YieldOp yield = warpOp.getTerminator();
994 unsigned resultIndex;
995 for (OpOperand &operand : yield->getOpOperands()) {
1004 valForwarded = operand.
get();
1008 auto arg = dyn_cast<BlockArgument>(operand.
get());
1009 if (!arg || arg.getOwner()->getParentOp() != warpOp.getOperation())
1011 Value warpOperand = warpOp.getArgs()[arg.getArgNumber()];
1014 valForwarded = warpOperand;
1032 PatternRewriter &rewriter)
const override {
1033 OpOperand *operand =
1039 Location loc = broadcastOp.getLoc();
1041 cast<VectorType>(warpOp->getResultTypes()[operandNumber]);
1042 Value broadcastSrc = broadcastOp.getSource();
1043 Type broadcastSrcType = broadcastSrc.
getType();
1050 vector::BroadcastableToResult::Success)
1052 SmallVector<size_t> newRetIndices;
1054 rewriter, warpOp, {broadcastSrc}, {broadcastSrcType}, newRetIndices);
1056 Value broadcasted = vector::BroadcastOp::create(
1057 rewriter, loc, destVecType, newWarpOp->getResult(newRetIndices[0]));
1069 PatternRewriter &rewriter)
const override {
1070 OpOperand *operand =
1078 auto castDistributedType =
1079 cast<VectorType>(warpOp->getResultTypes()[operandNumber]);
1080 VectorType castOriginalType = oldCastOp.getSourceVectorType();
1081 VectorType castResultType = castDistributedType;
1083 FailureOr<VectorType> maybeSrcType =
1084 inferDistributedSrcType(castDistributedType, castOriginalType);
1085 if (
failed(maybeSrcType))
1087 castDistributedType = *maybeSrcType;
1089 SmallVector<size_t> newRetIndices;
1091 rewriter, warpOp, {oldCastOp.getSource()}, {castDistributedType},
1094 Value newCast = vector::ShapeCastOp::create(
1095 rewriter, oldCastOp.getLoc(), castResultType,
1096 newWarpOp->getResult(newRetIndices[0]));
1102 static FailureOr<VectorType>
1103 inferDistributedSrcType(VectorType distributedType, VectorType srcType) {
1104 unsigned distributedRank = distributedType.getRank();
1105 unsigned srcRank = srcType.getRank();
1106 if (distributedRank == srcRank)
1108 return distributedType;
1109 if (distributedRank < srcRank) {
1112 SmallVector<int64_t> shape(srcRank - distributedRank, 1);
1113 llvm::append_range(shape, distributedType.getShape());
1114 return VectorType::get(shape, distributedType.getElementType());
1126 return VectorType::get(distributedType.getNumElements(),
1127 srcType.getElementType());
1132 unsigned excessDims = distributedRank - srcRank;
1133 ArrayRef<int64_t> shape = distributedType.getShape();
1134 if (!llvm::all_of(shape.take_front(excessDims),
1135 [](int64_t d) { return d == 1; }))
1137 return VectorType::get(shape.drop_front(excessDims),
1138 distributedType.getElementType());
1162template <
typename OpType,
1163 typename = std::enable_if_t<llvm::is_one_of<
1164 OpType, vector::CreateMaskOp, vector::ConstantMaskOp>::value>>
1167 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
1168 PatternRewriter &rewriter)
const override {
1169 OpOperand *yieldOperand = getWarpResult(warpOp, (llvm::IsaPred<OpType>));
1177 if (mask->getOperands().size() &&
1178 !llvm::all_of(mask->getOperands(), [&](Value value) {
1179 return warpOp.isDefinedOutsideOfRegion(value);
1183 Location loc = mask->
getLoc();
1186 auto distType = cast<VectorType>(warpOp.getResult(operandIndex).getType());
1187 VectorType seqType = cast<VectorType>(mask->getResult(0).
getType());
1188 ArrayRef<int64_t> seqShape = seqType.getShape();
1189 ArrayRef<int64_t> distShape = distType.getShape();
1190 SmallVector<Value> materializedOperands;
1191 if constexpr (std::is_same_v<OpType, vector::CreateMaskOp>) {
1192 materializedOperands.append(mask->getOperands().begin(),
1193 mask->getOperands().end());
1195 auto constantMaskOp = cast<vector::ConstantMaskOp>(mask);
1196 auto dimSizes = constantMaskOp.getMaskDimSizesAttr().asArrayRef();
1197 for (
auto dimSize : dimSizes)
1198 materializedOperands.push_back(
1205 SmallVector<Value> delinearizedIds;
1206 if (!delinearizeLaneId(rewriter, loc, seqShape, distShape,
1207 warpOp.getWarpSize(), warpOp.getLaneid(),
1210 mask,
"cannot delinearize lane ID for distribution");
1211 assert(!delinearizedIds.empty());
1219 SmallVector<Value> newOperands;
1220 for (
int i = 0, e = distShape.size(); i < e; ++i) {
1226 Value maskDimIdx = affine::makeComposedAffineApply(
1227 rewriter, loc, s1 - s0 * distShape[i],
1228 {delinearizedIds[i], materializedOperands[i]});
1229 newOperands.push_back(maskDimIdx);
1233 vector::CreateMaskOp::create(rewriter, loc, distType, newOperands);
1268 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
1269 PatternRewriter &rewriter)
const override {
1270 OpOperand *operand =
1271 getWarpResult(warpOp, llvm::IsaPred<vector::InsertStridedSliceOp>);
1277 auto distributedType =
1278 cast<VectorType>(warpOp.getResult(operandNumber).getType());
1281 if (distributedType.getRank() < 2)
1283 insertOp,
"result vector type must be 2D or higher");
1286 auto yieldedType = cast<VectorType>(operand->
get().
getType());
1287 int64_t destDistributedDim =
1289 assert(destDistributedDim != -1 &&
"could not find distributed dimension");
1291 VectorType srcType = insertOp.getSourceVectorType();
1292 VectorType destType = insertOp.getDestVectorType();
1297 int64_t sourceDistributedDim =
1298 destDistributedDim - (destType.getRank() - srcType.getRank());
1299 if (sourceDistributedDim < 0)
1302 "distributed dimension must be in the last k dims of dest vector");
1304 if (srcType.getDimSize(sourceDistributedDim) !=
1305 destType.getDimSize(destDistributedDim))
1307 insertOp,
"distributed dimension must be fully inserted");
1308 SmallVector<int64_t> newSourceDistShape(
1309 insertOp.getSourceVectorType().getShape());
1310 newSourceDistShape[sourceDistributedDim] =
1311 distributedType.getDimSize(destDistributedDim);
1313 VectorType::get(newSourceDistShape, distributedType.getElementType());
1314 VectorType newDestTy = distributedType;
1315 SmallVector<size_t> newRetIndices;
1316 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
1317 rewriter, warpOp, {insertOp.getValueToStore(), insertOp.getDest()},
1318 {newSourceTy, newDestTy}, newRetIndices);
1320 Value distributedSource = newWarpOp->getResult(newRetIndices[0]);
1321 Value distributedDest = newWarpOp->getResult(newRetIndices[1]);
1324 Value newInsert = vector::InsertStridedSliceOp::create(
1325 rewriter, insertOp.getLoc(), distributedDest.
getType(),
1326 distributedSource, distributedDest, insertOp.getOffsets(),
1327 insertOp.getStrides());
1357 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
1358 PatternRewriter &rewriter)
const override {
1359 OpOperand *operand =
1360 getWarpResult(warpOp, llvm::IsaPred<vector::ExtractStridedSliceOp>);
1366 auto distributedType =
1367 cast<VectorType>(warpOp.getResult(operandNumber).getType());
1370 if (distributedType.getRank() < 2)
1372 extractOp,
"result vector type must be 2D or higher");
1375 auto yieldedType = cast<VectorType>(operand->
get().
getType());
1377 assert(distributedDim != -1 &&
"could not find distributed dimension");
1379 int64_t numOfExtractedDims =
1380 static_cast<int64_t
>(extractOp.getSizes().size());
1387 if (distributedDim < numOfExtractedDims) {
1388 int64_t distributedDimOffset =
1389 llvm::cast<IntegerAttr>(extractOp.getOffsets()[distributedDim])
1391 int64_t distributedDimSize =
1392 llvm::cast<IntegerAttr>(extractOp.getSizes()[distributedDim])
1394 if (distributedDimOffset != 0 ||
1395 distributedDimSize != yieldedType.getDimSize(distributedDim))
1397 extractOp,
"distributed dimension must be fully extracted");
1399 SmallVector<int64_t> newDistributedShape(
1400 extractOp.getSourceVectorType().getShape());
1401 newDistributedShape[distributedDim] =
1402 distributedType.getDimSize(distributedDim);
1403 auto newDistributedType =
1404 VectorType::get(newDistributedShape, distributedType.getElementType());
1405 SmallVector<size_t> newRetIndices;
1406 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
1407 rewriter, warpOp, {extractOp.getSource()}, {newDistributedType},
1410 SmallVector<Attribute> distributedSizes = llvm::map_to_vector(
1411 extractOp.getSizes(), [](Attribute attr) { return attr; });
1413 if (distributedDim <
static_cast<int64_t
>(distributedSizes.size()))
1415 distributedType.getDimSize(distributedDim));
1419 Value distributedVec = newWarpOp->getResult(newRetIndices[0]);
1420 Value newExtract = vector::ExtractStridedSliceOp::create(
1421 rewriter, extractOp.getLoc(), distributedType, distributedVec,
1422 extractOp.getOffsets(),
1423 ArrayAttr::get(rewriter.
getContext(), distributedSizes),
1424 extractOp.getStrides());
1435 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
1436 PatternRewriter &rewriter)
const override {
1437 OpOperand *operand =
1438 getWarpResult(warpOp, llvm::IsaPred<vector::ExtractOp>);
1443 VectorType extractSrcType = extractOp.getSourceVectorType();
1444 Location loc = extractOp.getLoc();
1447 if (extractSrcType.getRank() <= 1) {
1453 if (warpOp.getResult(operandNumber).getType() == operand->
get().
getType()) {
1459 SmallVector<size_t> newRetIndices;
1460 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
1461 rewriter, warpOp, {extractOp.getSource()},
1462 {extractOp.getSourceVectorType()}, newRetIndices);
1464 Value distributedVec = newWarpOp->getResult(newRetIndices[0]);
1466 Value newExtract = vector::ExtractOp::create(
1467 rewriter, loc, distributedVec, extractOp.getMixedPosition());
1474 auto distributedType =
1475 cast<VectorType>(warpOp.getResult(operandNumber).getType());
1476 auto yieldedType = cast<VectorType>(operand->
get().
getType());
1478 assert(distributedDim != -1 &&
"could not find distributed dimension");
1479 (void)distributedDim;
1482 SmallVector<int64_t> newDistributedShape(extractSrcType.getShape());
1483 for (
int i = 0; i < distributedType.getRank(); ++i)
1484 newDistributedShape[i + extractOp.getNumIndices()] =
1485 distributedType.getDimSize(i);
1486 auto newDistributedType =
1487 VectorType::get(newDistributedShape, distributedType.getElementType());
1488 SmallVector<size_t> newRetIndices;
1489 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
1490 rewriter, warpOp, {extractOp.getSource()}, {newDistributedType},
1493 Value distributedVec = newWarpOp->getResult(newRetIndices[0]);
1495 Value newExtract = vector::ExtractOp::create(rewriter, loc, distributedVec,
1496 extractOp.getMixedPosition());
1506 WarpOpExtractScalar(MLIRContext *ctx, WarpShuffleFromIdxFn fn,
1507 PatternBenefit
b = 1)
1508 : WarpDistributionPattern(ctx,
b), warpShuffleFromIdxFn(std::move(fn)) {}
1509 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
1510 PatternRewriter &rewriter)
const override {
1511 OpOperand *operand =
1512 getWarpResult(warpOp, llvm::IsaPred<vector::ExtractOp>);
1517 VectorType extractSrcType = extractOp.getSourceVectorType();
1519 if (extractSrcType.getRank() > 1) {
1521 extractOp,
"only 0-D or 1-D source supported for now");
1525 if (!extractSrcType.getElementType().isF32() &&
1526 !extractSrcType.getElementType().isInteger(32))
1528 extractOp,
"only f32/i32 element types are supported");
1529 bool is0dOrVec1Extract = extractSrcType.getNumElements() == 1;
1530 Type elType = extractSrcType.getElementType();
1531 VectorType distributedVecType;
1532 if (!is0dOrVec1Extract) {
1533 assert(extractSrcType.getRank() == 1 &&
1534 "expected that extract src rank is 0 or 1");
1535 if (extractSrcType.getShape()[0] % warpOp.getWarpSize() != 0)
1537 int64_t elementsPerLane =
1538 extractSrcType.getShape()[0] / warpOp.getWarpSize();
1539 distributedVecType = VectorType::get({elementsPerLane}, elType);
1541 distributedVecType = extractSrcType;
1544 SmallVector<Value> additionalResults{extractOp.getSource()};
1545 SmallVector<Type> additionalResultTypes{distributedVecType};
1546 additionalResults.append(
1547 SmallVector<Value>(extractOp.getDynamicPosition()));
1548 additionalResultTypes.append(
1549 SmallVector<Type>(extractOp.getDynamicPosition().getTypes()));
1551 Location loc = extractOp.getLoc();
1552 SmallVector<size_t> newRetIndices;
1553 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
1554 rewriter, warpOp, additionalResults, additionalResultTypes,
1557 Value distributedVec = newWarpOp->getResult(newRetIndices[0]);
1561 if (is0dOrVec1Extract) {
1563 SmallVector<int64_t>
indices(extractSrcType.getRank(), 0);
1565 vector::ExtractOp::create(rewriter, loc, distributedVec,
indices);
1571 int64_t staticPos = extractOp.getStaticPosition()[0];
1572 OpFoldResult pos = ShapedType::isDynamic(staticPos)
1573 ? (newWarpOp->getResult(newRetIndices[1]))
1577 int64_t elementsPerLane = distributedVecType.getShape()[0];
1580 Value broadcastFromTid = affine::makeComposedAffineApply(
1581 rewriter, loc, sym0.
ceilDiv(elementsPerLane), pos);
1584 elementsPerLane == 1
1586 : affine::makeComposedAffineApply(rewriter, loc,
1587 sym0 % elementsPerLane, pos);
1589 vector::ExtractOp::create(rewriter, loc, distributedVec, newPos);
1592 Value shuffled = warpShuffleFromIdxFn(
1593 loc, rewriter, extracted, broadcastFromTid, newWarpOp.getWarpSize());
1599 WarpShuffleFromIdxFn warpShuffleFromIdxFn;
1606 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
1607 PatternRewriter &rewriter)
const override {
1608 OpOperand *operand = getWarpResult(warpOp, llvm::IsaPred<vector::InsertOp>);
1613 VectorType vecType = insertOp.getDestVectorType();
1614 VectorType distrType =
1615 cast<VectorType>(warpOp.getResult(operandNumber).getType());
1618 if (vecType.getRank() > 1) {
1620 insertOp,
"only 0-D or 1-D source supported for now");
1624 SmallVector<Value> additionalResults{insertOp.getDest(),
1625 insertOp.getValueToStore()};
1626 SmallVector<Type> additionalResultTypes{
1627 distrType, insertOp.getValueToStore().getType()};
1628 additionalResults.append(SmallVector<Value>(insertOp.getDynamicPosition()));
1629 additionalResultTypes.append(
1630 SmallVector<Type>(insertOp.getDynamicPosition().getTypes()));
1632 Location loc = insertOp.getLoc();
1633 SmallVector<size_t> newRetIndices;
1634 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
1635 rewriter, warpOp, additionalResults, additionalResultTypes,
1638 Value distributedVec = newWarpOp->getResult(newRetIndices[0]);
1639 Value newSource = newWarpOp->getResult(newRetIndices[1]);
1643 if (vecType.getRank() != 0) {
1644 int64_t staticPos = insertOp.getStaticPosition()[0];
1645 pos = ShapedType::isDynamic(staticPos)
1646 ? (newWarpOp->getResult(newRetIndices[2]))
1651 if (vecType == distrType) {
1653 SmallVector<OpFoldResult>
indices;
1657 newInsert = vector::InsertOp::create(rewriter, loc, newSource,
1666 int64_t elementsPerLane = distrType.getShape()[0];
1669 Value insertingLane = affine::makeComposedAffineApply(
1670 rewriter, loc, sym0.
ceilDiv(elementsPerLane), pos);
1672 OpFoldResult newPos = affine::makeComposedFoldedAffineApply(
1673 rewriter, loc, sym0 % elementsPerLane, pos);
1674 Value isInsertingLane =
1675 arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::eq,
1676 newWarpOp.getLaneid(), insertingLane);
1679 rewriter, loc, isInsertingLane,
1681 [&](OpBuilder &builder, Location loc) {
1682 Value newInsert = vector::InsertOp::create(
1683 builder, loc, newSource, distributedVec, newPos);
1684 scf::YieldOp::create(builder, loc, newInsert);
1687 [&](OpBuilder &builder, Location loc) {
1688 scf::YieldOp::create(builder, loc, distributedVec);
1698 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
1699 PatternRewriter &rewriter)
const override {
1700 OpOperand *operand = getWarpResult(warpOp, llvm::IsaPred<vector::InsertOp>);
1705 Location loc = insertOp.getLoc();
1708 if (insertOp.getDestVectorType().getRank() <= 1) {
1714 if (warpOp.getResult(operandNumber).getType() == operand->
get().
getType()) {
1717 SmallVector<size_t> newRetIndices;
1718 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
1719 rewriter, warpOp, {insertOp.getValueToStore(), insertOp.getDest()},
1720 {insertOp.getValueToStoreType(), insertOp.getDestVectorType()},
1723 Value distributedSrc = newWarpOp->getResult(newRetIndices[0]);
1724 Value distributedDest = newWarpOp->getResult(newRetIndices[1]);
1725 Value newResult = vector::InsertOp::create(rewriter, loc, distributedSrc,
1727 insertOp.getMixedPosition());
1734 auto distrDestType =
1735 cast<VectorType>(warpOp.getResult(operandNumber).getType());
1736 auto yieldedType = cast<VectorType>(operand->
get().
getType());
1737 int64_t distrDestDim = -1;
1738 for (int64_t i = 0; i < yieldedType.getRank(); ++i) {
1739 if (distrDestType.getDimSize(i) != yieldedType.getDimSize(i)) {
1742 assert(distrDestDim == -1 &&
"found multiple distributed dims");
1746 assert(distrDestDim != -1 &&
"could not find distributed dimension");
1749 VectorType srcVecType = cast<VectorType>(insertOp.getValueToStoreType());
1750 SmallVector<int64_t> distrSrcShape(srcVecType.getShape());
1757 int64_t distrSrcDim = distrDestDim - insertOp.getNumIndices();
1758 if (distrSrcDim >= 0)
1759 distrSrcShape[distrSrcDim] = distrDestType.getDimSize(distrDestDim);
1761 VectorType::get(distrSrcShape, distrDestType.getElementType());
1764 SmallVector<size_t> newRetIndices;
1765 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
1766 rewriter, warpOp, {insertOp.getValueToStore(), insertOp.getDest()},
1767 {distrSrcType, distrDestType}, newRetIndices);
1769 Value distributedSrc = newWarpOp->getResult(newRetIndices[0]);
1770 Value distributedDest = newWarpOp->getResult(newRetIndices[1]);
1774 if (distrSrcDim >= 0) {
1776 newResult = vector::InsertOp::create(rewriter, loc, distributedSrc,
1778 insertOp.getMixedPosition());
1781 int64_t elementsPerLane = distrDestType.getDimSize(distrDestDim);
1782 SmallVector<OpFoldResult> pos = insertOp.getMixedPosition();
1786 rewriter, loc, newPos[distrDestDim] / elementsPerLane);
1787 Value isInsertingLane =
1788 arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::eq,
1789 newWarpOp.getLaneid(), insertingLane);
1791 newPos[distrDestDim] %= elementsPerLane;
1792 auto insertingBuilder = [&](OpBuilder &builder, Location loc) {
1793 Value newInsert = vector::InsertOp::create(builder, loc, distributedSrc,
1794 distributedDest, newPos);
1795 scf::YieldOp::create(builder, loc, newInsert);
1797 auto nonInsertingBuilder = [&](OpBuilder &builder, Location loc) {
1798 scf::YieldOp::create(builder, loc, distributedDest);
1800 newResult = scf::IfOp::create(rewriter, loc, isInsertingLane,
1802 nonInsertingBuilder)
1839 : WarpDistributionPattern(ctx,
b), distributionMapFn(std::move(fn)) {}
1841 PatternRewriter &rewriter)
const override {
1842 gpu::YieldOp warpOpYield = warpOp.getTerminator();
1844 Operation *lastNode = warpOpYield->getPrevNode();
1845 auto ifOp = dyn_cast_or_null<scf::IfOp>(lastNode);
1856 SmallVector<Value> nonIfYieldValues;
1857 SmallVector<unsigned> nonIfYieldIndices;
1858 llvm::SmallDenseMap<unsigned, unsigned> ifResultMapping;
1859 llvm::SmallDenseMap<unsigned, VectorType> ifResultDistTypes;
1860 for (OpOperand &yieldOperand : warpOpYield->getOpOperands()) {
1863 nonIfYieldValues.push_back(yieldOperand.
get());
1864 nonIfYieldIndices.push_back(yieldOperandIdx);
1867 OpResult ifResult = cast<OpResult>(yieldOperand.
get());
1869 ifResultMapping[yieldOperandIdx] = ifResultIdx;
1872 if (!isa<VectorType>(ifResult.
getType()))
1874 VectorType distType =
1875 cast<VectorType>(warpOp.getResult(yieldOperandIdx).getType());
1876 ifResultDistTypes[ifResultIdx] = distType;
1881 auto [escapingValuesThen, escapingValueInputTypesThen,
1882 escapingValueDistTypesThen] =
1883 getInnerRegionEscapingValues(warpOp, ifOp.getThenRegion(),
1885 auto [escapingValuesElse, escapingValueInputTypesElse,
1886 escapingValueDistTypesElse] =
1887 getInnerRegionEscapingValues(warpOp, ifOp.getElseRegion(),
1889 if (llvm::is_contained(escapingValueDistTypesThen, Type{}) ||
1890 llvm::is_contained(escapingValueDistTypesElse, Type{}))
1898 SmallVector<Value> newWarpOpYieldValues{ifOp.getCondition()};
1899 newWarpOpYieldValues.append(escapingValuesThen.begin(),
1900 escapingValuesThen.end());
1901 newWarpOpYieldValues.append(escapingValuesElse.begin(),
1902 escapingValuesElse.end());
1903 SmallVector<Type> newWarpOpDistTypes{ifOp.getCondition().getType()};
1904 newWarpOpDistTypes.append(escapingValueDistTypesThen.begin(),
1905 escapingValueDistTypesThen.end());
1906 newWarpOpDistTypes.append(escapingValueDistTypesElse.begin(),
1907 escapingValueDistTypesElse.end());
1909 for (
auto [idx, val] :
1910 llvm::zip_equal(nonIfYieldIndices, nonIfYieldValues)) {
1911 newWarpOpYieldValues.push_back(val);
1912 newWarpOpDistTypes.push_back(warpOp.getResult(idx).getType());
1916 SmallVector<size_t> newIndices;
1918 rewriter, warpOp, newWarpOpYieldValues, newWarpOpDistTypes, newIndices);
1920 SmallVector<Type> newIfOpDistResTypes;
1921 for (
auto [i, res] : llvm::enumerate(ifOp.getResults())) {
1922 Type distType = cast<Value>(res).getType();
1923 if (
auto vecType = dyn_cast<VectorType>(distType)) {
1924 AffineMap map = distributionMapFn(cast<Value>(res));
1926 distType = ifResultDistTypes.count(i)
1927 ? ifResultDistTypes[i]
1928 : getDistributedType(
1930 map.
isEmpty() ? 1 : newWarpOp.getWarpSize());
1932 newIfOpDistResTypes.push_back(distType);
1935 OpBuilder::InsertionGuard g(rewriter);
1937 auto newIfOp = scf::IfOp::create(
1938 rewriter, ifOp.getLoc(), newIfOpDistResTypes,
1939 newWarpOp.getResult(newIndices[0]),
static_cast<bool>(ifOp.thenBlock()),
1940 static_cast<bool>(ifOp.elseBlock()));
1941 auto encloseRegionInWarpOp =
1943 llvm::SmallSetVector<Value, 32> &escapingValues,
1944 SmallVector<Type> &escapingValueInputTypes,
1945 size_t warpResRangeStart) {
1946 OpBuilder::InsertionGuard g(rewriter);
1950 llvm::SmallDenseMap<Value, int64_t> escapeValToBlockArgIndex;
1951 SmallVector<Value> innerWarpInputVals;
1952 SmallVector<Type> innerWarpInputTypes;
1953 for (
size_t i = 0; i < escapingValues.size();
1954 ++i, ++warpResRangeStart) {
1955 innerWarpInputVals.push_back(
1956 newWarpOp.getResult(newIndices[warpResRangeStart]));
1957 escapeValToBlockArgIndex[escapingValues[i]] =
1958 innerWarpInputTypes.size();
1959 innerWarpInputTypes.push_back(escapingValueInputTypes[i]);
1961 auto innerWarp = WarpExecuteOnLane0Op::create(
1962 rewriter, newWarpOp.getLoc(), newIfOp.getResultTypes(),
1963 newWarpOp.getLaneid(), newWarpOp.getWarpSize(),
1964 innerWarpInputVals, innerWarpInputTypes);
1966 innerWarp.getWarpRegion().takeBody(*oldIfBranch->
getParent());
1967 innerWarp.getWarpRegion().addArguments(
1968 innerWarpInputTypes,
1969 SmallVector<Location>(innerWarpInputTypes.size(), ifOp.getLoc()));
1971 SmallVector<Value> yieldOperands;
1973 yieldOperands.push_back(operand);
1977 gpu::YieldOp::create(rewriter, innerWarp.getLoc(), yieldOperands);
1979 scf::YieldOp::create(rewriter, ifOp.getLoc(), innerWarp.getResults());
1983 innerWarp.walk([&](Operation *op) {
1984 SmallVector<std::pair<unsigned, Value>> replacements;
1986 auto it = escapeValToBlockArgIndex.find(operand.
get());
1987 if (it == escapeValToBlockArgIndex.end())
1989 replacements.emplace_back(
1991 innerWarp.getBodyRegion().getArgument(it->second));
1993 if (!replacements.empty()) {
1995 for (
auto [idx, newVal] : replacements)
2000 mlir::vector::moveScalarUniformCode(innerWarp);
2002 encloseRegionInWarpOp(&ifOp.getThenRegion().front(),
2003 &newIfOp.getThenRegion().front(), escapingValuesThen,
2004 escapingValueInputTypesThen, 1);
2005 if (!ifOp.getElseRegion().empty())
2006 encloseRegionInWarpOp(&ifOp.getElseRegion().front(),
2007 &newIfOp.getElseRegion().front(),
2008 escapingValuesElse, escapingValueInputTypesElse,
2009 1 + escapingValuesThen.size());
2012 for (
auto [origIdx, newIdx] : ifResultMapping)
2014 newIfOp.getResult(newIdx), newIfOp);
2022 OpBuilder::InsertionGuard guard(rewriter);
2024 Operation *yield = newWarpOp.getTerminator();
2026 for (
auto [origIdx, ifResultIdx] : ifResultMapping) {
2027 Value poison = ub::PoisonOp::create(
2028 rewriter, ifOp.getLoc(), ifOp.getResult(ifResultIdx).getType());
2077 : WarpDistributionPattern(ctx,
b), distributionMapFn(std::move(fn)) {}
2078 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
2079 PatternRewriter &rewriter)
const override {
2080 gpu::YieldOp warpOpYield = warpOp.getTerminator();
2082 Operation *lastNode = warpOpYield->getPrevNode();
2083 auto forOp = dyn_cast_or_null<scf::ForOp>(lastNode);
2088 auto [escapingValues, escapingValueInputTypes, escapingValueDistTypes] =
2089 getInnerRegionEscapingValues(warpOp, forOp.getBodyRegion(),
2091 if (llvm::is_contained(escapingValueDistTypes, Type{}))
2102 SmallVector<Value> nonForYieldedValues;
2103 SmallVector<unsigned> nonForResultIndices;
2104 llvm::SmallDenseMap<unsigned, unsigned> forResultMapping;
2105 llvm::SmallDenseMap<unsigned, VectorType> forResultDistTypes;
2106 llvm::SmallBitVector forResultsMapped(forOp.getNumResults());
2107 for (OpOperand &yieldOperand : warpOpYield->getOpOperands()) {
2110 nonForYieldedValues.push_back(yieldOperand.
get());
2114 OpResult forResult = cast<OpResult>(yieldOperand.
get());
2117 forResultsMapped.set(forResultNumber);
2120 if (!isa<VectorType>(forResult.
getType()))
2122 VectorType distType = cast<VectorType>(
2124 forResultDistTypes[forResultNumber] = distType;
2132 SmallVector<Value> newWarpOpYieldValues;
2133 SmallVector<Type> newWarpOpDistTypes;
2134 newWarpOpYieldValues.insert(
2135 newWarpOpYieldValues.end(),
2136 {forOp.getLowerBound(), forOp.getUpperBound(), forOp.getStep()});
2137 newWarpOpDistTypes.insert(newWarpOpDistTypes.end(),
2138 {forOp.getLowerBound().getType(),
2139 forOp.getUpperBound().getType(),
2140 forOp.getStep().getType()});
2141 for (
auto [i, initArg] : llvm::enumerate(forOp.getInitArgs())) {
2142 newWarpOpYieldValues.push_back(initArg);
2144 Type distType = initArg.getType();
2145 if (
auto vecType = dyn_cast<VectorType>(distType)) {
2149 AffineMap map = distributionMapFn(initArg);
2151 forResultDistTypes.count(i)
2152 ? forResultDistTypes[i]
2153 : getDistributedType(vecType, map,
2154 map.
isEmpty() ? 1 : warpOp.getWarpSize());
2156 newWarpOpDistTypes.push_back(distType);
2159 newWarpOpYieldValues.insert(newWarpOpYieldValues.end(),
2160 escapingValues.begin(), escapingValues.end());
2161 newWarpOpDistTypes.insert(newWarpOpDistTypes.end(),
2162 escapingValueDistTypes.begin(),
2163 escapingValueDistTypes.end());
2167 llvm::zip_equal(nonForResultIndices, nonForYieldedValues)) {
2168 newWarpOpYieldValues.push_back(v);
2169 newWarpOpDistTypes.push_back(warpOp.getResult(i).getType());
2172 SmallVector<size_t> newIndices;
2173 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
2174 rewriter, warpOp, newWarpOpYieldValues, newWarpOpDistTypes, newIndices);
2178 const unsigned initArgsStartIdx = 3;
2179 const unsigned escapingValuesStartIdx =
2181 forOp.getInitArgs().size();
2183 SmallVector<Value> newForOpOperands;
2184 for (
size_t i = initArgsStartIdx; i < escapingValuesStartIdx; ++i)
2185 newForOpOperands.push_back(newWarpOp.getResult(newIndices[i]));
2188 OpBuilder::InsertionGuard g(rewriter);
2190 auto newForOp = scf::ForOp::create(
2191 rewriter, forOp.getLoc(),
2192 newWarpOp.getResult(newIndices[0]),
2193 newWarpOp.getResult(newIndices[1]),
2194 newWarpOp.getResult(newIndices[2]), newForOpOperands,
2195 nullptr, forOp.getUnsignedCmp());
2201 SmallVector<Value> innerWarpInput(newForOp.getRegionIterArgs().begin(),
2202 newForOp.getRegionIterArgs().end());
2203 SmallVector<Type> innerWarpInputType(forOp.getResultTypes().begin(),
2204 forOp.getResultTypes().end());
2208 llvm::SmallDenseMap<Value, int64_t> argIndexMapping;
2209 for (
size_t i = escapingValuesStartIdx;
2210 i < escapingValuesStartIdx + escapingValues.size(); ++i) {
2211 innerWarpInput.push_back(newWarpOp.getResult(newIndices[i]));
2212 argIndexMapping[escapingValues[i - escapingValuesStartIdx]] =
2213 innerWarpInputType.size();
2214 innerWarpInputType.push_back(
2215 escapingValueInputTypes[i - escapingValuesStartIdx]);
2218 auto innerWarp = WarpExecuteOnLane0Op::create(
2219 rewriter, newWarpOp.getLoc(), newForOp.getResultTypes(),
2220 newWarpOp.getLaneid(), newWarpOp.getWarpSize(), innerWarpInput,
2221 innerWarpInputType);
2224 SmallVector<Value> argMapping;
2225 argMapping.push_back(newForOp.getInductionVar());
2226 for (Value args : innerWarp.getBody()->getArguments())
2227 argMapping.push_back(args);
2229 argMapping.resize(forOp.getBody()->getNumArguments());
2230 SmallVector<Value> yieldOperands;
2231 for (Value operand : forOp.getBody()->getTerminator()->getOperands()) {
2232 if (BlockArgument blockArg = dyn_cast<BlockArgument>(operand);
2233 blockArg && blockArg.getOwner() == forOp.getBody()) {
2234 yieldOperands.push_back(argMapping[blockArg.getArgNumber()]);
2237 yieldOperands.push_back(operand);
2240 rewriter.
eraseOp(forOp.getBody()->getTerminator());
2241 rewriter.
mergeBlocks(forOp.getBody(), innerWarp.getBody(), argMapping);
2246 gpu::YieldOp::create(rewriter, innerWarp.getLoc(), yieldOperands);
2250 if (!innerWarp.getResults().empty())
2251 scf::YieldOp::create(rewriter, forOp.getLoc(), innerWarp.getResults());
2255 for (
auto [origIdx, newIdx] : forResultMapping)
2257 newForOp.getResult(newIdx), newForOp);
2262 for (OpResult
result : forOp.getResults()) {
2263 if (forResultsMapped.test(
result.getResultNumber()))
2265 result, forOp.getInitArgs()[
result.getResultNumber()]);
2271 newForOp.walk([&](Operation *op) {
2272 SmallVector<std::pair<unsigned, Value>> replacements;
2274 auto it = argIndexMapping.find(operand.
get());
2275 if (it == argIndexMapping.end())
2277 replacements.emplace_back(
2279 innerWarp.getBodyRegion().getArgument(it->second));
2281 if (!replacements.empty()) {
2283 for (
auto [idx, newVal] : replacements)
2290 mlir::vector::moveScalarUniformCode(innerWarp);
2318 WarpOpReduction(MLIRContext *context,
2319 DistributedReductionFn distributedReductionFn,
2320 PatternBenefit benefit = 1)
2321 : WarpDistributionPattern(context, benefit),
2322 distributedReductionFn(std::move(distributedReductionFn)) {}
2324 LogicalResult matchAndRewrite(WarpExecuteOnLane0Op warpOp,
2325 PatternRewriter &rewriter)
const override {
2326 OpOperand *yieldOperand =
2327 getWarpResult(warpOp, llvm::IsaPred<vector::ReductionOp>);
2333 auto vectorType = cast<VectorType>(reductionOp.getVector().getType());
2335 if (vectorType.getRank() != 1)
2337 warpOp,
"Only rank 1 reductions can be distributed.");
2339 if (vectorType.getShape()[0] % warpOp.getWarpSize() != 0)
2341 warpOp,
"Reduction vector dimension must match was size.");
2342 if (!reductionOp.getType().isIntOrFloat())
2344 warpOp,
"Reduction distribution currently only supports floats and "
2347 int64_t numElements = vectorType.getShape()[0] / warpOp.getWarpSize();
2350 SmallVector<Value> yieldValues = {reductionOp.getVector()};
2351 SmallVector<Type> retTypes = {
2352 VectorType::get({numElements}, reductionOp.getType())};
2353 if (reductionOp.getAcc()) {
2354 yieldValues.push_back(reductionOp.getAcc());
2355 retTypes.push_back(reductionOp.getAcc().getType());
2357 SmallVector<size_t> newRetIndices;
2358 WarpExecuteOnLane0Op newWarpOp = moveRegionToNewWarpOpAndAppendReturns(
2359 rewriter, warpOp, yieldValues, retTypes, newRetIndices);
2363 Value laneValVec = newWarpOp.getResult(newRetIndices[0]);
2366 distributedReductionFn(reductionOp.getLoc(), rewriter, laneValVec,
2367 reductionOp.getKind(), newWarpOp.getWarpSize());
2368 if (reductionOp.getAcc()) {
2370 rewriter, reductionOp.getLoc(), reductionOp.getKind(), fullReduce,
2371 newWarpOp.getResult(newRetIndices[1]));
2378 DistributedReductionFn distributedReductionFn;
2389void mlir::vector::populateDistributeTransferWriteOpPatterns(
2392 patterns.
add<WarpOpTransferWrite>(patterns.
getContext(), distributionMapFn,
2393 maxNumElementsToExtract, benefit);
2396void mlir::vector::populatePropagateWarpVectorDistributionPatterns(
2398 const WarpShuffleFromIdxFn &warpShuffleFromIdxFn,
PatternBenefit benefit,
2400 patterns.
add<WarpOpTransferRead>(patterns.
getContext(), readBenefit);
2401 patterns.
add<WarpOpElementwise, WarpOpDeadResult, WarpOpBroadcast,
2402 WarpOpShapeCast, WarpOpExtract, WarpOpForwardOperand,
2403 WarpOpConstant, WarpOpInsertScalar, WarpOpInsert,
2404 WarpOpCreateMask<vector::CreateMaskOp>,
2405 WarpOpCreateMask<vector::ConstantMaskOp>,
2406 WarpOpExtractStridedSlice, WarpOpInsertStridedSlice, WarpOpStep>(
2408 patterns.
add<WarpOpExtractScalar>(patterns.
getContext(), warpShuffleFromIdxFn,
2410 patterns.
add<WarpOpScfForOp>(patterns.
getContext(), distributionMapFn,
2412 patterns.
add<WarpOpScfIfOp>(patterns.
getContext(), distributionMapFn,
2416void mlir::vector::populateDistributeReduction(
2418 const DistributedReductionFn &distributedReductionFn,
2420 patterns.
add<WarpOpReduction>(patterns.
getContext(), distributedReductionFn,
2427 return llvm::all_of(op->
getOperands(), definedOutside) &&
2431void mlir::vector::moveScalarUniformCode(WarpExecuteOnLane0Op warpOp) {
2432 Block *body = warpOp.getBody();
2435 llvm::SmallSetVector<Operation *, 8> opsToMove;
2438 auto isDefinedOutsideOfBody = [&](
Value value) {
2440 return (definingOp && opsToMove.count(definingOp)) ||
2441 warpOp.isDefinedOutsideOfRegion(value);
2448 return isa<VectorType>(result.getType());
2450 if (!hasVectorResult &&
canBeHoisted(&op, isDefinedOutsideOfBody))
2451 opsToMove.insert(&op);
static llvm::ManagedStatic< PassManagerOptions > options
static AffineMap calculateImplicitMap(VectorType sequentialType, VectorType distributedType)
Currently the distribution map is implicit based on the vector shape.
static Operation * cloneOpWithOperandsAndTypes(RewriterBase &rewriter, Location loc, Operation *op, ArrayRef< Value > operands, ArrayRef< Type > resultTypes)
static int getDistributedDim(VectorType sequentialType, VectorType distributedType)
Given a sequential and distributed vector type, returns the distributed dimension.
static bool canBeHoisted(Operation *op, function_ref< bool(Value)> definedOutside)
Helper to know if an op can be hoisted out of the region.
AffineExpr ceilDiv(uint64_t v) const
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: () -> ().
bool isEmpty() const
Returns true if this affine map is an empty map, i.e., () -> ().
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
bool isIdentity() const
Returns true if this affine map is an identity affine map.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Operation * getTerminator()
Get the terminator operation of this block.
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
IntegerAttr getIndexAttr(int64_t value)
AffineExpr getAffineConstantExpr(int64_t constant)
IntegerAttr getI64IntegerAttr(int64_t value)
MLIRContext * getContext() const
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
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...
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents an operand of an operation.
unsigned getOperandNumber() const
Return which operand this is in the OpOperand list of the Operation.
unsigned getResultNumber() const
Returns the number of this result.
StringRef getStringRef() const
Return the name of this operation. This always succeeds.
Operation is the basic unit of execution within MLIR.
void setOperand(unsigned idx, Value value)
bool hasOneUse()
Returns true if this operation has exactly one use.
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
unsigned getNumRegions()
Returns the number of regions held by this operation.
MutableArrayRef< OpOperand > getOpOperands()
unsigned getNumOperands()
Attribute getPropertiesAsAttribute()
Return the properties converted to an attribute.
OperationName getName()
The name of an operation is the key identifier for it.
DictionaryAttr getDiscardableAttrDictionary()
Return all of the discardable attributes on this operation as a DictionaryAttr.
operand_range getOperands()
Returns an iterator on the underlying Value's.
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
result_range getResults()
This class represents the benefit of a pattern match in a unitless scheme that ranges from 0 (very li...
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Operation * getParentOp()
Return the parent operation this region is attached to.
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.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void finalizeOpModification(Operation *op)
This method is used to signal the end of an in-place modification of the given operation.
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
void replaceAllUsesExcept(Value from, Value to, Operation *exceptedUser)
Find uses of from and replace them with to except if the user is exceptedUser.
void mergeBlocks(Block *source, Block *dest, ValueRange argValues={})
Inline the operations of block 'source' into the end of block 'dest'.
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,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
virtual void startOpModification(Operation *op)
This method is used to notify the rewriter that an in-place operation modification is about to happen...
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.
Location getLoc() const
Return the location 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.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
bool hasElementwiseMappableTraits(Operation *op)
Together, Elementwise, Scalarizable, Vectorizable, and Tensorizable provide an easy way for scalar op...
AffineApplyOp makeComposedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Returns a composed AffineApplyOp by composing map and operands with other AffineApplyOps supplying th...
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.
std::function< AffineMap(Value)> DistributionMapFn
BroadcastableToResult isBroadcastableTo(Type srcType, VectorType dstVectorType, std::pair< VectorDim, VectorDim > *mismatchingDims=nullptr)
Return whether srcType can be broadcast to dstVectorType under the semantics of the vector....
void populateWarpExecuteOnLane0OpToScfForPattern(RewritePatternSet &patterns, const WarpExecuteOnLane0LoweringOptions &options, PatternBenefit benefit=1)
SmallVector< int64_t > getAsIntegers(ArrayRef< Value > values)
Returns the integer numbers in values.
Include the generated interface declarations.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
bool isMemoryEffectFree(Operation *op)
Returns true if the given operation is free of memory effects.
bool isOpTriviallyDead(Operation *op)
Return true if the given operation is unused, and has no side effects on memory that prevent erasing.
void bindSymbols(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to SymbolExpr at positions: [0 .
AffineMap compressUnusedDims(AffineMap map)
Drop the dims that are not used.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.
void visitUsedValuesDefinedAbove(Region ®ion, Region &limit, function_ref< void(OpOperand *)> callback)
Calls callback for each use of a value within region or its descendants that was defined at the ances...
llvm::function_ref< Fn > function_ref
AffineExpr getAffineSymbolExpr(unsigned position, MLIRContext *context)
This represents an operation in an abstracted form, suitable for use with the builder APIs.
WarpExecuteOnLane0Op moveRegionToNewWarpOpAndAppendReturns(RewriterBase &rewriter, WarpExecuteOnLane0Op warpOp, ValueRange newYieldedValues, TypeRange newReturnTypes, SmallVector< size_t > &indices) const
Helper to create a new WarpExecuteOnLane0Op region with extra outputs.
bool delinearizeLaneId(OpBuilder &builder, Location loc, ArrayRef< int64_t > originalShape, ArrayRef< int64_t > distributedShape, int64_t warpSize, Value laneId, SmallVectorImpl< Value > &delinearizedIds) const
Delinearize the given laneId into multiple dimensions, where each dimension's size is determined by o...
WarpExecuteOnLane0Op moveRegionToNewWarpOpAndReplaceReturns(RewriterBase &rewriter, WarpExecuteOnLane0Op warpOp, ValueRange newYieldedValues, TypeRange newReturnTypes) const
Helper to create a new WarpExecuteOnLane0Op with different signature.
virtual LogicalResult matchAndRewrite(WarpExecuteOnLane0Op op, PatternRewriter &rewriter) const override=0
OpOperand * getWarpResult(WarpExecuteOnLane0Op warpOp, llvm::function_ref< bool(Operation *)> fn) const
Return a value yielded by warpOp which statifies the filter lamdba condition and is not dead.