36#include "llvm/ADT/DenseSet.h"
37#include "llvm/ADT/Repeated.h"
38#include "llvm/ADT/STLExtras.h"
39#include "llvm/ADT/SmallBitVector.h"
40#include "llvm/ADT/SmallVectorExtras.h"
41#include "llvm/ADT/StringRef.h"
42#include "llvm/Support/Casting.h"
43#include "llvm/Support/MathExtras.h"
56 auto verifiable = dyn_cast_or_null<VerifiableTensorEncoding>(encoding);
64 return succeeded(verifiable.verifyEncoding(
shape, elementType,
emit))
74 if (
auto op = arith::ConstantOp::materialize(builder, value, type, loc))
76 if (complex::ConstantOp::isBuildableWith(value, type))
77 return complex::ConstantOp::create(builder, loc, type,
78 llvm::cast<ArrayAttr>(value));
84 auto tensorType = llvm::cast<RankedTensorType>(value.
getType());
85 if (tensorType.isDynamicDim(dim))
86 return builder.
createOrFold<tensor::DimOp>(loc, value, dim);
93 auto tensorType = llvm::cast<RankedTensorType>(value.
getType());
95 for (
int64_t i = 0; i < tensorType.getRank(); ++i)
102 auto tensorType = llvm::dyn_cast<TensorType>(opResult.
getType());
103 assert(tensorType &&
"expected tensor type");
107 auto destOp = opResult.
getDefiningOp<DestinationStyleOpInterface>();
109 return destOp.getTiedOpOperand(opResult)->get();
117 if (!tensorType.hasStaticShape()) {
125 for (
int64_t sz : tensorType.getShape())
126 mixedSizes.push_back(
b.getIndexAttr(sz));
131 if (
auto rankedTensorType = dyn_cast<RankedTensorType>(tensorType))
132 encoding = rankedTensorType.getEncoding();
133 Value emptyTensor = tensor::EmptyOp::create(
134 b, loc, mixedSizes, tensorType.getElementType(), encoding);
142 if (llvm::isa<TensorType>(opResult.getType())) {
144 if (failed(destination))
146 result.push_back(*destination);
153 if (
auto rtp1 = llvm::dyn_cast<RankedTensorType>(tp1)) {
154 if (
auto rtp2 = llvm::dyn_cast<RankedTensorType>(tp2))
155 return rtp1.getShape() == rtp2.getShape() &&
156 rtp1.getElementType() == rtp2.getElementType();
166 llvm::SmallBitVector droppedDims(mixedSizes.size());
167 int64_t shapePos = reducedShape.size() - 1;
169 for (
const auto &size : enumerate(llvm::reverse(mixedSizes))) {
170 size_t idx = mixedSizes.size() - size.index() - 1;
172 bool isStaticUnitSize =
173 isa<Attribute>(size.value()) &&
174 llvm::cast<IntegerAttr>(cast<Attribute>(size.value())).getInt() == 1;
179 assert(isStaticUnitSize &&
"expected unit dim");
180 droppedDims.set(idx);
185 if (!isStaticUnitSize) {
191 if (reducedShape[shapePos] == 1) {
197 droppedDims.set(idx);
200 assert(shapePos < 0 &&
"dimension mismatch");
207static RankedTensorType
211 assert(type.getNumDynamicDims() == dynamicSizes.size() &&
212 "incorrect number of dynamic sizes");
216 for (
int64_t i = 0, e = type.getRank(); i < e; ++i) {
217 if (type.isDynamicDim(i)) {
218 Value dynamicSize = dynamicSizes[ctr++];
220 if (cst.has_value()) {
222 if (cst.value() < 0) {
223 foldedDynamicSizes.push_back(dynamicSize);
226 staticShape[i] = *cst;
228 foldedDynamicSizes.push_back(dynamicSize);
233 return RankedTensorType::get(staticShape, type.getElementType(),
242 if (inputs.size() != 1 || outputs.size() != 1)
244 Type a = inputs.front(),
b = outputs.front();
245 auto aT = dyn_cast<TensorType>(a);
246 auto bT = dyn_cast<TensorType>(
b);
250 if (aT.getElementTypeBitWidth() != bT.getElementTypeBitWidth())
261 using OpRewritePattern<BitcastOp>::OpRewritePattern;
263 LogicalResult matchAndRewrite(BitcastOp tensorBitcast,
264 PatternRewriter &rewriter)
const final {
265 auto tensorBitcastOperand =
266 tensorBitcast.getOperand().getDefiningOp<BitcastOp>();
267 if (!tensorBitcastOperand)
270 auto resultType = cast<TensorType>(tensorBitcast.getType());
271 rewriter.replaceOpWithNewOp<BitcastOp>(tensorBitcast, resultType,
272 tensorBitcastOperand.getOperand());
281 results.
add<ChainedTensorBitcast>(context);
289 setNameFn(getResult(),
"cast");
295 auto sourceType = llvm::dyn_cast<RankedTensorType>(source);
296 auto targetType = llvm::dyn_cast<RankedTensorType>(
target);
299 if (!sourceType || !targetType)
303 if (sourceType.getElementType() != targetType.getElementType())
307 if (sourceType.getRank() != targetType.getRank())
311 if (sourceType.getEncoding() != targetType.getEncoding())
315 for (
auto t : llvm::zip(sourceType.getShape(), targetType.getShape())) {
316 if (ShapedType::isStatic(std::get<0>(t)) &&
317 ShapedType::isDynamic(std::get<1>(t)))
353 castOp.getSource().getType());
386 if (llvm::isa<BlockArgument>(opOperand.get()))
388 auto castOp = opOperand.get().getDefiningOp<tensor::CastOp>();
389 return castOp && canFoldIntoConsumerOp(castOp);
396 newOperands.reserve(op->getNumOperands());
402 for (
OpOperand &opOperand : op->getOpOperands()) {
403 auto tensorCastOp = opOperand.get().getDefiningOp<tensor::CastOp>();
405 newOperands.push_back(fold ? tensorCastOp.getOperand() : opOperand.get());
406 if (op.isDpsInit(&opOperand) &&
407 !llvm::isa<MemRefType>(newOperands.back().getType()))
408 newResTy[dpsInitIdx++] = newOperands.back().getType();
418 auto castOp = operand.get().getDefiningOp<tensor::CastOp>();
420 operand.set(castOp.getOperand());
428 if (inputs.size() != 1 || outputs.size() != 1)
430 Type a = inputs.front(),
b = outputs.front();
431 auto aT = llvm::dyn_cast<TensorType>(a);
432 auto bT = llvm::dyn_cast<TensorType>(
b);
436 if (aT.getElementType() != bT.getElementType())
453 if (rank != two.getRank())
458 for (
int64_t i = 0; i < rank; ++i) {
459 if (one.isDynamicDim(i)) {
460 join.push_back(two.getDimSize(i));
463 if (two.isDynamicDim(i)) {
464 join.push_back(one.getDimSize(i));
467 if (one.getDimSize(i) != two.getDimSize(i))
469 join.push_back(one.getDimSize(i));
479 using OpRewritePattern<CastOp>::OpRewritePattern;
481 LogicalResult matchAndRewrite(CastOp tensorCast,
482 PatternRewriter &rewriter)
const final {
483 auto tensorCastOperand = tensorCast.getOperand().getDefiningOp<CastOp>();
485 if (!tensorCastOperand)
489 llvm::cast<TensorType>(tensorCastOperand.getOperand().getType());
490 auto intermediateType = llvm::cast<TensorType>(tensorCastOperand.getType());
491 auto resultType = llvm::cast<TensorType>(tensorCast.getType());
505 auto newJoin =
joinShapes(sourceType, resultType);
506 if (firstJoin != newJoin)
509 rewriter.replaceOpWithNewOp<CastOp>(tensorCast, resultType,
510 tensorCastOperand.getOperand());
528 using OpRewritePattern<CastOp>::OpRewritePattern;
530 LogicalResult matchAndRewrite(CastOp tensorCast,
531 PatternRewriter &rewriter)
const final {
532 auto extractOperand =
533 tensorCast.getOperand().getDefiningOp<ExtractSliceOp>();
536 auto rankedResultType =
537 llvm::dyn_cast<RankedTensorType>(tensorCast.getType());
538 if (!rankedResultType)
542 rankedResultType.getShape() ==
543 llvm::cast<RankedTensorType>(tensorCast.getSource().getType())
547 SmallVector<OpFoldResult, 4> sizes = extractOperand.getMixedSizes();
549 extractOperand.getStaticSizes(), extractOperand.getType().getShape());
551 for (
size_t i = 0, e = sizes.size(); i < e; i++) {
552 if (dimMask && dimMask->count(i))
554 int64_t dim = rankedResultType.getShape()[dimIndex++];
555 if (ShapedType::isDynamic(dim))
557 sizes[i] = rewriter.getIndexAttr(dim);
560 rewriter.replaceOpWithNewOp<ExtractSliceOp>(
561 tensorCast, rankedResultType, extractOperand.getSource(),
562 extractOperand.getMixedOffsets(), sizes,
563 extractOperand.getMixedStrides());
572 results.
add<ChainedTensorCast, TensorCastExtractSlice>(context);
579RankedTensorType ConcatOp::inferResultType(
int64_t dim,
TypeRange inputTypes) {
580 assert(!inputTypes.empty() &&
"cannot concatenate 0 tensors");
582 llvm::map_to_vector<4>(inputTypes, llvm::CastTo<RankedTensorType>);
583 int64_t concatRank = tensorTypes[0].getRank();
586 assert(dim >= 0 && dim < concatRank &&
"Invalid concatenation dim");
589 for (
int64_t i = 0, e = concatRank; i < e; ++i) {
593 for (
auto tensorType : tensorTypes)
598 for (
auto tensorType : tensorTypes)
601 sizes[dim] = concatSize.asInteger();
602 return RankedTensorType::get(sizes, tensorTypes[0].
getElementType());
607 FailureOr<RankedTensorType> resultType =
608 inferResultType(dim, inputs.
getTypes());
609 assert(succeeded(resultType) &&
"failed to infer concatenation result type");
610 build(builder,
result, *resultType, dim, inputs);
613LogicalResult ConcatOp::verify() {
614 if (getInputs().size() < 1)
615 return emitOpError(
"requires at least one input");
618 for (
auto input : getInputs())
619 inputTypes.push_back(cast<RankedTensorType>(input.getType()));
621 RankedTensorType resultType = getResultType();
622 int64_t resultRank = getRank();
623 if (llvm::any_of(inputTypes, [resultRank](RankedTensorType type) {
624 return type.getRank() != resultRank;
626 return emitOpError(
"rank of concatenated inputs must match result rank");
628 Type resultElementType = resultType.getElementType();
629 if (llvm::any_of(inputTypes, [&](RankedTensorType type) {
630 return type.getElementType() != resultElementType;
632 return emitOpError(
"inputs and result element type must match");
635 if (dim >= resultRank)
636 return emitOpError(
"concatenation dim must be less than the tensor rank");
639 for (
int64_t i = 0, e = resultRank; i < e; ++i) {
643 for (
auto tensorType : inputTypes) {
644 FailureOr<SaturatedInteger> maybeSize =
647 return emitOpError(
"static concatenation size mismatch along ")
648 <<
"non-concatenated dimension " << i;
654 for (
auto tensorType : inputTypes)
657 sizes[dim] = concatSize.asInteger();
658 auto inferredResultType =
661 for (
auto [inferredSize, actualSize] :
662 llvm::zip_equal(inferredResultType.getShape(), resultType.getShape())) {
663 bool hasDynamic = ShapedType::isDynamic(inferredSize) ||
664 ShapedType::isDynamic(actualSize);
665 if (!hasDynamic && inferredSize != actualSize)
666 return emitOpError(
"result type ")
667 << resultType <<
"does not match inferred shape "
668 << inferredResultType <<
" static sizes";
674FailureOr<SmallVector<Value>> ConcatOp::decomposeOperation(
OpBuilder &builder) {
675 size_t numInputs = getInputs().size();
676 uint64_t concatDim = getDim();
679 inputShapes.reserve(numInputs);
681 concatOffsets.reserve(numInputs);
688 for (
auto [
index, input] : llvm::enumerate(getInputs())) {
692 outputShape = inputShape;
693 concatOffsets.push_back(zero);
695 concatOffsets.push_back(outputShape[concatDim]);
697 builder, loc, addExpr,
698 {outputShape[concatDim], inputShape[concatDim]});
700 inputShapes.emplace_back(std::move(inputShape));
710 for (
auto [
index, input] : llvm::enumerate(getInputs())) {
711 offsets[concatDim] = concatOffsets[
index];
712 auto insertSlice = tensor::InsertSliceOp::create(
723ConcatOp::reifyResultShapes(
OpBuilder &builder,
727 RankedTensorType inferredResultType = inferResultType(dim, inputs.
getTypes());
729 Value init = inputs[0];
737 for (
int64_t i = 0; i < rank; ++i) {
740 if (!
getType().isDynamicDim(i)) {
742 }
else if (!inferredResultType.isDynamicDim(i)) {
745 builder.
getIndexAttr(inferredResultType.getDimSize(i)));
747 reifiedReturnShapes[0][i] =
748 tensor::DimOp::create(builder, init.
getLoc(), init, i).getResult();
752 if (
getType().isDynamicDim(dim)) {
757 for (
auto [idx, input] : llvm::enumerate(inputs.drop_front())) {
760 builder.
createOrFold<tensor::DimOp>(input.getLoc(), input, dim));
768 reifiedReturnShapes[0][dim] =
774void ConcatOp::getAsmResultNames(
776 setNameFn(getResult(),
"concat");
781 if (inputs.size() == 1 && inputs[0].
getType() == getResultType())
789 using OpRewritePattern<ConcatOp>::OpRewritePattern;
791 LogicalResult matchAndRewrite(ConcatOp concatOp,
792 PatternRewriter &rewriter)
const override {
793 if (concatOp.getInputs().size() != 1)
796 concatOp.getInputs()[0]);
821 using OpRewritePattern<ConcatOp>::OpRewritePattern;
823 LogicalResult matchAndRewrite(ConcatOp concatOp,
824 PatternRewriter &rewriter)
const override {
825 int64_t dim = concatOp.getDim();
826 RankedTensorType inferredResultType =
827 ConcatOp::inferResultType(dim, concatOp->getOperandTypes());
830 LogicalResult matched = failure();
833 SmallVector<int64_t> inferredOperandShape(inferredResultType.getShape());
834 for (
auto [operandIdx, operandType] :
835 llvm::enumerate(concatOp->getOperandTypes())) {
837 inferredOperandShape[dim] =
838 cast<RankedTensorType>(operandType).getDimSize(dim);
839 auto inferredOperandType = RankedTensorType::get(
840 inferredOperandShape, inferredResultType.getElementType());
848 CastOp::create(rewriter, concatOp->getLoc(), inferredOperandType,
849 concatOp.getOperand(operandIdx));
851 concatOp->setOperand(operandIdx, castOp->getResult(0));
875 using OpRewritePattern<ConcatOp>::OpRewritePattern;
877 LogicalResult matchAndRewrite(ConcatOp concatOp,
878 PatternRewriter &rewriter)
const override {
879 int64_t dim = concatOp.getDim();
880 RankedTensorType inferredResultType =
881 ConcatOp::inferResultType(dim, concatOp->getOperandTypes());
885 concatOp.getResultType())) {
890 ConcatOp::create(rewriter, concatOp->getLoc(), inferredResultType, dim,
891 concatOp->getOperands());
903 .
add<SingleInputConcatOp, InferConcatOperandTypes, InferConcatResultType>(
912 setNameFn(getResult(),
"dim");
917 auto loc =
result.location;
919 build(builder,
result, source, indexValue);
922std::optional<int64_t> DimOp::getConstantIndex() {
931 auto rankedSourceType = dyn_cast<RankedTensorType>(getSource().
getType());
932 if (!rankedSourceType)
935 if (rankedSourceType.getRank() <= constantIndex)
943 setResultRange(getResult(),
949 std::optional<int64_t>
index = getConstantIndex();
954 auto tensorType = llvm::dyn_cast<RankedTensorType>(getSource().
getType());
961 if (indexVal < 0 || indexVal >= tensorType.getRank())
965 if (!tensorType.isDynamicDim(indexVal)) {
967 return builder.
getIndexAttr(tensorType.getShape()[indexVal]);
970 Operation *definingOp = getSource().getDefiningOp();
973 if (
auto fromElements = dyn_cast_or_null<tensor::GenerateOp>(definingOp)) {
975 llvm::cast<RankedTensorType>(fromElements.getResult().getType());
978 assert(ShapedType::isDynamic(resultType.getShape()[indexVal]));
981 auto dynExtents = fromElements.getDynamicExtents().begin();
982 for (
auto dim : resultType.getShape().take_front(indexVal))
983 if (ShapedType::isDynamic(dim))
986 return Value{*dynExtents};
990 if (
auto sliceOp = dyn_cast_or_null<tensor::ExtractSliceOp>(definingOp)) {
993 if (sliceOp.getType().getRank() == sliceOp.getSourceType().getRank() &&
994 sliceOp.isDynamicSize(indexVal)) {
995 return {sliceOp.getDynamicSize(indexVal)};
1009 using OpRewritePattern<DimOp>::OpRewritePattern;
1011 LogicalResult matchAndRewrite(DimOp dimOp,
1012 PatternRewriter &rewriter)
const override {
1013 auto castOp = dimOp.getSource().getDefiningOp<CastOp>();
1016 Value newSource = castOp.getOperand();
1025 using OpRewritePattern<DimOp>::OpRewritePattern;
1027 LogicalResult matchAndRewrite(DimOp dimOp,
1028 PatternRewriter &rewriter)
const override {
1029 auto source = dimOp.getSource();
1030 auto destOp = source.getDefiningOp<DestinationStyleOpInterface>();
1034 auto resultIndex = cast<OpResult>(source).getResultNumber();
1035 auto *initOperand = destOp.getDpsInitOperand(resultIndex);
1038 dimOp, [&]() { dimOp.getSourceMutable().assign(initOperand->get()); });
1046 using OpRewritePattern<DimOp>::OpRewritePattern;
1048 LogicalResult matchAndRewrite(DimOp dim,
1049 PatternRewriter &rewriter)
const override {
1050 auto reshape = dim.getSource().getDefiningOp<ReshapeOp>();
1058 Location loc = dim.getLoc();
1060 ExtractOp::create(rewriter, loc, reshape.getShape(), dim.getIndex());
1061 if (extract.
getType() != dim.getType())
1063 arith::IndexCastOp::create(rewriter, loc, dim.getType(), extract);
1072 results.
add<DimOfCastOp, DimOfDestStyleOp, DimOfReshapeOp>(context);
1082 assert(none_of(staticShape, ShapedType::isDynamic) &&
1083 "expected only static sizes");
1087void EmptyOp::build(OpBuilder &builder, OperationState &
result,
1088 ArrayRef<int64_t> staticShape, Type elementType,
1089 ValueRange dynamicSizes, Attribute encoding) {
1090 auto tensorType = RankedTensorType::get(staticShape, elementType, encoding);
1091 build(builder,
result, tensorType, dynamicSizes);
1094void EmptyOp::build(OpBuilder &builder, OperationState &
result,
1095 ArrayRef<OpFoldResult> sizes, Type elementType,
1096 Attribute encoding) {
1097 SmallVector<int64_t> staticShape;
1098 SmallVector<Value> dynamicSizes;
1100 build(builder,
result, staticShape, elementType, dynamicSizes, encoding);
1103LogicalResult EmptyOp::verify() {
1109EmptyOp::reifyResultShapes(OpBuilder &builder,
1111 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
1113 for (int64_t i = 0; i <
getType().getRank(); ++i) {
1114 if (
getType().isDynamicDim(i)) {
1123Value EmptyOp::getDynamicSize(
unsigned idx) {
1124 assert(
getType().isDynamicDim(idx) &&
"expected dynamic dim");
1126 for (int64_t i = 0; i < static_cast<int64_t>(idx); ++i)
1127 if (
getType().isDynamicDim(i))
1132SmallVector<OpFoldResult> EmptyOp::getMixedSizes() {
1133 SmallVector<OpFoldResult>
result;
1137 if (ShapedType::isDynamic(dim)) {
1140 result.push_back(
b.getIndexAttr(dim));
1158struct ReplaceEmptyTensorStaticShapeDims : OpRewritePattern<EmptyOp> {
1159 using OpRewritePattern<EmptyOp>::OpRewritePattern;
1161 LogicalResult matchAndRewrite(EmptyOp op,
1162 PatternRewriter &rewriter)
const override {
1163 SmallVector<Value> foldedDynamicSizes;
1165 op.getType(), op.getDynamicSizes(), foldedDynamicSizes);
1168 if (foldedTensorType == op.getType())
1171 auto newOp = EmptyOp::create(rewriter, op.getLoc(), foldedTensorType,
1172 foldedDynamicSizes);
1178struct FoldEmptyTensorWithDimOp :
public OpRewritePattern<DimOp> {
1179 using OpRewritePattern<DimOp>::OpRewritePattern;
1181 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1182 PatternRewriter &rewriter)
const override {
1183 std::optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
1184 auto emptyTensorOp = dimOp.getSource().getDefiningOp<EmptyOp>();
1185 if (!emptyTensorOp || !maybeConstantIndex)
1187 auto emptyTensorType = emptyTensorOp.getType();
1188 if (*maybeConstantIndex < 0 ||
1189 *maybeConstantIndex >= emptyTensorType.getRank() ||
1190 !emptyTensorType.isDynamicDim(*maybeConstantIndex))
1193 emptyTensorOp.getDynamicSize(*maybeConstantIndex));
1213struct FoldEmptyTensorWithCastOp :
public OpRewritePattern<CastOp> {
1214 using OpRewritePattern<CastOp>::OpRewritePattern;
1216 LogicalResult matchAndRewrite(CastOp castOp,
1217 PatternRewriter &rewriter)
const override {
1220 auto producer = castOp.getSource().getDefiningOp<EmptyOp>();
1225 llvm::cast<RankedTensorType>(castOp->getResult(0).getType());
1226 ArrayRef<int64_t> resultShape = resultType.getShape();
1227 SmallVector<OpFoldResult> currMixedSizes = producer.getMixedSizes();
1228 SmallVector<OpFoldResult> newMixedSizes;
1229 newMixedSizes.reserve(currMixedSizes.size());
1230 assert(resultShape.size() == currMixedSizes.size() &&
1231 "mismatch in result shape and sizes of empty op");
1232 for (
auto [newDim, currDim] : llvm::zip(resultShape, currMixedSizes)) {
1235 if (
auto attr = llvm::dyn_cast_if_present<Attribute>(currDim)) {
1236 if (ShapedType::isDynamic(newDim) ||
1237 newDim != llvm::cast<IntegerAttr>(attr).getInt()) {
1242 producer,
"mismatch in static value of shape of empty tensor "
1243 "result and cast result");
1245 newMixedSizes.push_back(attr);
1251 if (ShapedType::isStatic(newDim)) {
1252 newMixedSizes.push_back(rewriter.
getIndexAttr(newDim));
1258 newMixedSizes.push_back(currDim);
1262 resultType.getElementType(),
1263 resultType.getEncoding());
1270void EmptyOp::getCanonicalizationPatterns(RewritePatternSet &results,
1271 MLIRContext *context) {
1272 results.
add<FoldEmptyTensorWithCastOp, FoldEmptyTensorWithDimOp,
1273 ReplaceEmptyTensorStaticShapeDims>(context);
1290struct ExtractFromTensorCast :
public OpRewritePattern<tensor::ExtractOp> {
1291 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
1293 LogicalResult matchAndRewrite(tensor::ExtractOp extract,
1294 PatternRewriter &rewriter)
const final {
1295 auto tensorCast = extract.getTensor().getDefiningOp<tensor::CastOp>();
1298 if (!llvm::isa<RankedTensorType>(tensorCast.getSource().getType()))
1301 extract, tensorCast.getSource(), extract.getIndices());
1316struct ExtractFromCollapseShape :
public OpRewritePattern<tensor::ExtractOp> {
1317 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
1319 LogicalResult matchAndRewrite(tensor::ExtractOp extractOp,
1320 PatternRewriter &rewriter)
const final {
1322 extractOp.getTensor().getDefiningOp<tensor::CollapseShapeOp>();
1325 if (!collapseOp.getSrcType().hasStaticShape())
1328 auto sourceSizes = collapseOp.getSrcType().getShape();
1330 SmallVector<Value>
indices(extractOp.getIndices().begin(),
1331 extractOp.getIndices().end());
1332 SmallVector<Value> sourceIndices;
1333 for (
auto [index, group] :
1334 llvm::zip(
indices, collapseOp.getReassociationIndices())) {
1335 assert(!group.empty() &&
"association indices groups cannot be empty");
1336 auto groupSize = group.size();
1338 if (groupSize == 1) {
1339 sourceIndices.push_back(index);
1343 SmallVector<int64_t> basis =
1344 llvm::map_to_vector(group, [&](int64_t d) {
return sourceSizes[d]; });
1345 auto delinearize = affine::AffineDelinearizeIndexOp::create(
1346 rewriter, extractOp.getLoc(), index, basis,
true);
1347 llvm::append_range(sourceIndices,
delinearize.getResults());
1349 if (collapseOp.getReassociationIndices().empty()) {
1352 cast<RankedTensorType>(collapseOp.getSrcType()).getRank();
1354 rewriter, extractOp.getLoc(), zeroAffineMap,
1355 ArrayRef<OpFoldResult>{});
1356 for (int64_t i = 0; i < srcRank; i++) {
1357 sourceIndices.push_back(
1363 extractOp, collapseOp.getSrc(), sourceIndices);
1370void ExtractOp::getAsmResultNames(
1372 setNameFn(getResult(),
"extracted");
1375LogicalResult ExtractOp::verify() {
1377 auto tensorType = llvm::cast<RankedTensorType>(getTensor().
getType());
1378 if (tensorType.getRank() !=
static_cast<int64_t
>(
getIndices().size()))
1379 return emitOpError(
"incorrect number of indices for extract_element");
1388 auto insertOp = extractOp.getTensor().
getDefiningOp<InsertOp>();
1393 if (insertOp && insertOp.getScalar().getType() == extractOp.getType() &&
1394 llvm::equal(insertOp.getIndices(), extractOp.getIndices(), isSame))
1395 return insertOp.getScalar();
1400OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
1401 if (Attribute tensor = adaptor.getTensor()) {
1404 if (
auto splatTensor = llvm::dyn_cast<SplatElementsAttr>(tensor))
1405 return splatTensor.getSplatValue<Attribute>();
1408 if (isa<DenseResourceElementsAttr>(tensor))
1413 SmallVector<uint64_t, 8>
indices;
1414 for (Attribute indice : adaptor.getIndices()) {
1415 if (!indice || !llvm::isa<IntegerAttr>(indice))
1417 indices.push_back(llvm::cast<IntegerAttr>(indice).getInt());
1421 if (
auto fromElementsOp = getTensor().getDefiningOp<FromElementsOp>()) {
1422 auto tensorType = llvm::cast<RankedTensorType>(fromElementsOp.getType());
1423 auto rank = tensorType.getRank();
1424 assert(
static_cast<int64_t
>(
indices.size()) == tensorType.getRank() &&
1428 for (
int i = rank - 1; i >= 0; --i) {
1429 flatIndex +=
indices[i] * stride;
1430 stride *= tensorType.getDimSize(i);
1434 if (
static_cast<int>(fromElementsOp.getElements().size()) <= flatIndex ||
1437 return fromElementsOp.getElements()[flatIndex];
1441 if (Attribute tensor = adaptor.getTensor()) {
1442 auto elementsAttr = llvm::dyn_cast<ElementsAttr>(tensor);
1443 if (elementsAttr && elementsAttr.isValidIndex(
indices))
1444 return elementsAttr.getValues<Attribute>()[
indices];
1453void ExtractOp::getCanonicalizationPatterns(RewritePatternSet &results,
1454 MLIRContext *context) {
1455 results.
add<ExtractFromTensorCast>(context);
1460 patterns.
add<ExtractFromCollapseShape>(patterns.
getContext());
1467void FromElementsOp::getAsmResultNames(
1469 setNameFn(getResult(),
"from_elements");
1474 assert(!elements.empty() &&
"expected at least one element");
1475 Type resultType = RankedTensorType::get(
1476 {
static_cast<int64_t>(elements.size())}, elements.front().
getType());
1477 build(builder,
result, resultType, elements);
1480OpFoldResult FromElementsOp::fold(FoldAdaptor adaptor) {
1485 Type eltType =
getType().getElementType();
1488 if (!llvm::is_contained(adaptor.getElements(),
nullptr))
1511struct ExtractElementFromIndexCast
1512 :
public OpRewritePattern<tensor::ExtractOp> {
1513 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
1515 LogicalResult matchAndRewrite(tensor::ExtractOp extract,
1516 PatternRewriter &rewriter)
const final {
1517 Location loc = extract.getLoc();
1518 auto indexCast = extract.getTensor().getDefiningOp<arith::IndexCastOp>();
1524 auto newExtract = tensor::ExtractOp::create(
1525 rewriter, loc, elementTy, indexCast.getIn(), extract.getIndices());
1536void FromElementsOp::getCanonicalizationPatterns(RewritePatternSet &results,
1537 MLIRContext *context) {
1538 results.
add<ExtractElementFromIndexCast>(context);
1545void GatherOp::getAsmResultNames(
1547 setNameFn(getResult(),
"gather");
1562RankedTensorType GatherOp::inferResultType(RankedTensorType sourceType,
1563 RankedTensorType indicesType,
1564 ArrayRef<int64_t> gatherDims,
1566 SmallVector<int64_t> resultShape(indicesType.getShape().drop_back());
1567 resultShape.reserve(resultShape.size() + sourceType.getRank());
1568 for (int64_t idx : llvm::seq<int64_t>(0, sourceType.getRank())) {
1569 if (llvm::binary_search(gatherDims, idx)) {
1571 resultShape.push_back(1);
1574 resultShape.push_back(sourceType.getDimSize(idx));
1576 return RankedTensorType::Builder(sourceType).setShape(resultShape);
1582 StringRef gatherOrScatter, StringRef sourceOrDest) {
1584 return op->
emitOpError(gatherOrScatter) <<
"_dims must be non-empty";
1586 int64_t numGatherDims = dims.size();
1587 if (numGatherDims > rank)
1589 <<
"_dims overflow " << sourceOrDest <<
" rank";
1592 <<
"_dims length must match the size of last dimension of indices";
1596 <<
"_dims value must be non-negative";
1599 <<
"_dims value must be smaller than " << sourceOrDest <<
" rank";
1601 for (
int64_t i = 1; i < numGatherDims; ++i) {
1602 if (dims[i - 1] >= dims[i])
1604 <<
"_dims values must be strictly increasing";
1609LogicalResult GatherOp::verify() {
1610 int64_t sourceRank = getSourceType().getRank();
1611 ArrayRef<int64_t> gatherDims = getGatherDims();
1613 getIndicesType().
getShape(), sourceRank,
1614 "gather",
"source")))
1617 RankedTensorType expectedResultType = GatherOp::inferResultType(
1618 getSourceType(), getIndicesType(), gatherDims,
false);
1619 RankedTensorType expectedRankReducedResultType = GatherOp::inferResultType(
1620 getSourceType(), getIndicesType(), gatherDims,
true);
1621 if (getResultType() != expectedResultType &&
1622 getResultType() != expectedRankReducedResultType) {
1623 return emitOpError(
"result type "
1626 << expectedResultType <<
" or its rank-reduced variant "
1627 << expectedRankReducedResultType <<
" (got: " << getResultType()
1634OpFoldResult GatherOp::fold(FoldAdaptor adaptor) {
1635 if (OpFoldResult reshapedSource = reshapeConstantSource(
1636 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
1638 return reshapedSource;
1646void InsertOp::getAsmResultNames(
1648 setNameFn(getResult(),
"inserted");
1651LogicalResult InsertOp::verify() {
1653 auto destType = llvm::cast<RankedTensorType>(getDest().
getType());
1654 if (destType.getRank() !=
static_cast<int64_t
>(
getIndices().size()))
1655 return emitOpError(
"incorrect number of indices");
1659OpFoldResult InsertOp::fold(FoldAdaptor adaptor) {
1660 Attribute scalar = adaptor.getScalar();
1661 Attribute dest = adaptor.getDest();
1663 if (
auto splatDest = llvm::dyn_cast<SplatElementsAttr>(dest))
1664 if (scalar == splatDest.getSplatValue<Attribute>())
1673void GenerateOp::getAsmResultNames(
1675 setNameFn(getResult(),
"generated");
1678LogicalResult GenerateOp::reifyResultShapes(
1680 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
1682 for (
auto dim : llvm::seq<int64_t>(0,
getType().getRank())) {
1683 if (
getType().isDynamicDim(dim)) {
1684 reifiedReturnShapes[0][dim] = getOperand(idx++);
1686 reifiedReturnShapes[0][dim] =
1693LogicalResult GenerateOp::verify() {
1696 RankedTensorType resultType = llvm::cast<RankedTensorType>(
getType());
1703LogicalResult GenerateOp::verifyRegions() {
1704 RankedTensorType resultTy = llvm::cast<RankedTensorType>(
getType());
1706 if (!llvm::all_of(getBody().getArgumentTypes(),
1707 [](Type ty) {
return ty.
isIndex(); }))
1708 return emitError(
"all body arguments must be index");
1709 if (getBody().getNumArguments() != resultTy.getRank())
1710 return emitError(
"must have one body argument per input dimension");
1713 auto yieldOp = cast<YieldOp>(getBody().getBlocks().front().getTerminator());
1715 if (yieldOp.getValue().getType() != resultTy.getElementType())
1717 "body must be terminated with a `yield` operation of the tensor "
1723void GenerateOp::build(
1724 OpBuilder &
b, OperationState &
result, Type resultTy,
1727 build(
b,
result, resultTy, dynamicExtents);
1730 OpBuilder::InsertionGuard guard(
b);
1731 Region *bodyRegion =
result.regions.front().get();
1732 auto rank = llvm::cast<RankedTensorType>(resultTy).getRank();
1733 SmallVector<Type, 2> argumentTypes(rank,
b.getIndexType());
1734 SmallVector<Location, 2> argumentLocs(rank,
result.location);
1736 b.createBlock(bodyRegion, bodyRegion->
end(), argumentTypes, argumentLocs);
1746struct StaticTensorGenerate :
public OpRewritePattern<GenerateOp> {
1747 using OpRewritePattern<GenerateOp>::OpRewritePattern;
1749 LogicalResult matchAndRewrite(GenerateOp generateOp,
1750 PatternRewriter &rewriter)
const final {
1751 SmallVector<Value> foldedDynamicSizes;
1753 generateOp.getType(), generateOp.getDynamicExtents(),
1754 foldedDynamicSizes);
1757 if (foldedTensorType == generateOp.getType())
1760 auto loc = generateOp.getLoc();
1762 GenerateOp::create(rewriter, loc, foldedTensorType, foldedDynamicSizes);
1764 newOp.getBody().begin());
1766 generateOp.getType(), newOp);
1782struct ExtractFromTensorGenerate :
public OpRewritePattern<tensor::ExtractOp> {
1783 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
1785 LogicalResult matchAndRewrite(tensor::ExtractOp extract,
1786 PatternRewriter &rewriter)
const final {
1787 auto tensorFromElements = extract.getTensor().getDefiningOp<GenerateOp>();
1792 Block *body = &tensorFromElements.getBody().front();
1795 rewriter.
clone(op, mapping);
1806void GenerateOp::getCanonicalizationPatterns(RewritePatternSet &results,
1807 MLIRContext *context) {
1809 results.
add<ExtractFromTensorGenerate, StaticTensorGenerate>(context);
1816void RankOp::getAsmResultNames(
function_ref<
void(Value, StringRef)> setNameFn) {
1817 setNameFn(getResult(),
"rank");
1820OpFoldResult RankOp::fold(FoldAdaptor adaptor) {
1822 auto type = getOperand().getType();
1823 auto shapedType = llvm::dyn_cast<ShapedType>(type);
1824 if (shapedType && shapedType.hasRank())
1825 return IntegerAttr::get(IndexType::get(
getContext()), shapedType.getRank());
1826 return IntegerAttr();
1833void ReshapeOp::getAsmResultNames(
1835 setNameFn(getResult(),
"reshape");
1840 for (
auto dim : type.getShape())
1845LogicalResult ReshapeOp::verify() {
1846 TensorType operandType = llvm::cast<TensorType>(getSource().
getType());
1847 TensorType resultType = llvm::cast<TensorType>(getResult().
getType());
1850 return emitOpError(
"element types of source and destination tensor "
1851 "types should be the same");
1855 auto resultRankedType = llvm::dyn_cast<RankedTensorType>(resultType);
1856 auto operandRankedType = llvm::dyn_cast<RankedTensorType>(operandType);
1858 if (resultRankedType) {
1859 if (operandRankedType && resultRankedType.hasStaticShape() &&
1860 operandRankedType.hasStaticShape()) {
1862 return emitOpError(
"source and destination tensor should have the "
1863 "same number of elements");
1865 if (ShapedType::isDynamic(shapeSize))
1866 return emitOpError(
"cannot use shape operand with dynamic length to "
1867 "reshape to statically-ranked tensor type");
1868 if (shapeSize != resultRankedType.getRank())
1870 "length of shape operand differs from the result's tensor rank");
1875OpFoldResult ReshapeOp::fold(FoldAdaptor adaptor) {
1876 if (OpFoldResult reshapedSource = reshapeConstantSource(
1877 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
1879 return reshapedSource;
1884 if (
auto reshapeOpProducer = getSource().getDefiningOp<ReshapeOp>()) {
1885 getSourceMutable().assign(reshapeOpProducer.getSource());
1889 auto source = getSource();
1890 auto sourceTy = dyn_cast<RankedTensorType>(source.getType());
1891 auto resultTy = dyn_cast<RankedTensorType>(
getType());
1892 if (!sourceTy || !resultTy || sourceTy != resultTy)
1897 if (sourceTy.getRank() <= 1)
1900 if (
auto fromElements =
getShape().getDefiningOp<tensor::FromElementsOp>()) {
1901 auto elements = fromElements.getElements();
1903 sourceTy.getRank() ==
static_cast<int64_t
>(elements.size());
1904 for (
int id = 0, s = elements.size();
id < s && dynamicNoop; ++
id) {
1905 auto element = elements[id];
1908 dynamicNoop &= cst.value() == sourceTy.getDimSize(
id);
1912 if (
auto dimOp = element.getDefiningOp<tensor::DimOp>()) {
1913 dynamicNoop &= dimOp.getSource() == source;
1917 cst.has_value() && cst.value() ==
static_cast<int64_t
>(id);
1921 dynamicNoop =
false;
1936void CollapseShapeOp::getAsmResultNames(
1938 setNameFn(getResult(),
"collapsed");
1941void ExpandShapeOp::getAsmResultNames(
1943 setNameFn(getResult(),
"expanded");
1946int64_t ExpandShapeOp::getCorrespondingSourceDim(int64_t resultDim) {
1947 assert(resultDim >= 0 && resultDim < getResultType().getRank() &&
1948 "invalid resultDim");
1949 for (
const auto &it : llvm::enumerate(getReassociationIndices()))
1950 if (llvm::is_contained(it.value(), resultDim))
1952 llvm_unreachable(
"could not find reassociation group");
1955FailureOr<SmallVector<OpFoldResult>>
1956ExpandShapeOp::inferOutputShape(OpBuilder &
b, Location loc,
1957 RankedTensorType expandedType,
1958 ArrayRef<ReassociationIndices> reassociation,
1959 ArrayRef<OpFoldResult> inputShape) {
1960 std::optional<SmallVector<OpFoldResult>> outputShape =
1965 return *outputShape;
1968SmallVector<OpFoldResult> ExpandShapeOp::getMixedOutputShape() {
1972void ExpandShapeOp::build(OpBuilder &builder, OperationState &
result,
1973 Type resultType, Value src,
1974 ArrayRef<ReassociationIndices> reassociation,
1975 ArrayRef<OpFoldResult> outputShape) {
1976 auto [staticOutputShape, dynamicOutputShape] =
1978 build(builder,
result, cast<RankedTensorType>(resultType), src,
1980 dynamicOutputShape, staticOutputShape);
1983void ExpandShapeOp::build(OpBuilder &builder, OperationState &
result,
1984 Type resultType, Value src,
1985 ArrayRef<ReassociationIndices> reassociation) {
1986 SmallVector<OpFoldResult> inputShape =
1988 auto tensorResultTy = cast<RankedTensorType>(resultType);
1989 FailureOr<SmallVector<OpFoldResult>> outputShape = inferOutputShape(
1990 builder,
result.location, tensorResultTy, reassociation, inputShape);
1991 SmallVector<OpFoldResult> outputShapeOrEmpty;
1992 if (succeeded(outputShape)) {
1993 outputShapeOrEmpty = *outputShape;
1995 build(builder,
result, tensorResultTy, src, reassociation,
1996 outputShapeOrEmpty);
1999SmallVector<AffineMap, 4> CollapseShapeOp::getReassociationMaps() {
2002SmallVector<ReassociationExprs, 4> CollapseShapeOp::getReassociationExprs() {
2004 getReassociationIndices());
2007SmallVector<AffineMap, 4> ExpandShapeOp::getReassociationMaps() {
2010SmallVector<ReassociationExprs, 4> ExpandShapeOp::getReassociationExprs() {
2012 getReassociationIndices());
2015RankedTensorType CollapseShapeOp::inferCollapsedType(
2016 RankedTensorType type, ArrayRef<ReassociationIndices> reassociation) {
2017 return inferCollapsedType(
2019 type.getContext(), reassociation)));
2025CollapseShapeOp::inferCollapsedType(RankedTensorType type,
2026 ArrayRef<AffineMap> reassociation) {
2027 auto shape = type.getShape();
2028 SmallVector<int64_t, 4> newShape;
2029 newShape.reserve(reassociation.size());
2034 unsigned currentDim = 0;
2035 for (AffineMap m : reassociation) {
2036 unsigned dim = m.getNumResults();
2037 auto band = shape.slice(currentDim, dim);
2039 if (llvm::is_contained(band, ShapedType::kDynamic))
2040 size = ShapedType::kDynamic;
2042 for (
unsigned d = 0; d < dim; ++d)
2043 size *= shape[currentDim + d];
2044 newShape.push_back(size);
2048 return RankedTensorType::get(newShape, type.getElementType());
2051void CollapseShapeOp::build(OpBuilder &
b, OperationState &
result, Value src,
2052 ArrayRef<ReassociationIndices> reassociation,
2053 ArrayRef<NamedAttribute> attrs) {
2054 auto srcType = llvm::cast<RankedTensorType>(src.
getType());
2055 RankedTensorType collapsedType = inferCollapsedType(srcType, reassociation);
2057 RankedTensorType::get(collapsedType.getShape(), srcType.getElementType(),
2058 srcType.getEncoding());
2059 buildPropertiesAndDiscardableAttributes(
result, attrs);
2060 result.getOrAddProperties<Properties>().reassociation =
2063 result.addTypes(resultType);
2066template <
typename TensorReshapeOp,
bool isExpansion = std::is_same<
2067 TensorReshapeOp, ExpandShapeOp>::value>
2069 RankedTensorType expandedType,
2070 RankedTensorType collapsedType) {
2072 verifyReshapeLikeTypes(op, expandedType, collapsedType, isExpansion)))
2076 if (expandedType.hasStaticShape() && collapsedType.hasStaticShape()) {
2077 int64_t expandedNumElements = expandedType.getNumElements();
2078 int64_t collapsedNumElements = collapsedType.getNumElements();
2079 if (expandedNumElements != collapsedNumElements) {
2080 return op.emitOpError(
"number of elements must be preserved: ")
2081 << expandedNumElements <<
" != " << collapsedNumElements;
2085 auto maps = op.getReassociationMaps();
2086 RankedTensorType expectedType =
2087 CollapseShapeOp::inferCollapsedType(expandedType, maps);
2089 return op.emitOpError(
"expected collapsed type to be ")
2090 << expectedType <<
", but got " << collapsedType;
2094LogicalResult ExpandShapeOp::verify() {
2095 RankedTensorType srcType = getSrc().getType();
2096 RankedTensorType resultType = getResult().getType();
2098 if ((int64_t)getStaticOutputShape().size() != resultType.getRank())
2099 return emitOpError(
"expected number of static shape dims to be equal to "
2100 "the output rank (")
2101 << resultType.getRank() <<
") but found "
2102 << getStaticOutputShape().size() <<
" inputs instead";
2104 if ((int64_t)getOutputShape().size() !=
2105 llvm::count(getStaticOutputShape(), ShapedType::kDynamic))
2106 return emitOpError(
"mismatch in dynamic dims in output_shape and "
2107 "static_output_shape: static_output_shape has ")
2108 << llvm::count(getStaticOutputShape(), ShapedType::kDynamic)
2109 <<
" dynamic dims while output_shape has " << getOutputShape().size()
2120 ArrayRef<int64_t> resShape = getResult().getType().getShape();
2121 for (
auto [pos, shape] : llvm::enumerate(resShape))
2122 if (ShapedType::isStatic(shape) && shape != staticOutputShapes[pos])
2123 return emitOpError(
"invalid output shape provided at pos ") << pos;
2128LogicalResult CollapseShapeOp::verify() {
2129 CollapseShapeOp op = *
this;
2130 if (llvm::any_of(op.getReassociationIndices(),
2132 return op.emitOpError(
"reassociation indices must not be empty");
2134 RankedTensorType srcType = op.getSrc().getType();
2135 RankedTensorType resultType = op.getResult().getType();
2143template <
typename TensorReshapeOp>
2144struct FoldReshapeWithConstant : OpRewritePattern<TensorReshapeOp> {
2145 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2146 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2147 PatternRewriter &rewriter)
const override {
2148 DenseElementsAttr attr;
2155 if (!reshapeOp.getResultType().hasStaticShape())
2158 reshapeOp.getResultType(), attr.
getRawData());
2165template <
typename TensorReshapeOp>
2166class FoldReshapeWithSplat :
public OpRewritePattern<TensorReshapeOp> {
2168 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2170 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2171 PatternRewriter &rewriter)
const override {
2172 auto splatOp = reshapeOp.getSrc().template getDefiningOp<tensor::SplatOp>();
2173 if (!splatOp || !splatOp.getAggregate().getType().hasStaticShape())
2177 reshapeOp, reshapeOp.getResultType(), splatOp.getInput());
2184template <
typename TensorReshapeOp>
2185struct FoldReshapeWithFromElements : OpRewritePattern<TensorReshapeOp> {
2186 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2187 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2188 PatternRewriter &rewriter)
const override {
2190 reshapeOp.getSrc().template getDefiningOp<FromElementsOp>();
2194 auto shapedTy = llvm::cast<ShapedType>(reshapeOp.getType());
2196 if (!shapedTy.hasStaticShape())
2200 fromElements.getElements());
2206struct FoldCollapseOfCastOp :
public OpRewritePattern<CollapseShapeOp> {
2207 using OpRewritePattern<CollapseShapeOp>::OpRewritePattern;
2209 LogicalResult matchAndRewrite(CollapseShapeOp collapseShapeOp,
2210 PatternRewriter &rewriter)
const override {
2211 auto castOp = collapseShapeOp.getSrc().getDefiningOp<tensor::CastOp>();
2215 RankedTensorType srcType =
2216 llvm::cast<RankedTensorType>(castOp.getSource().getType());
2217 RankedTensorType newResultType = CollapseShapeOp::inferCollapsedType(
2218 srcType, collapseShapeOp.getReassociationMaps());
2220 if (newResultType == collapseShapeOp.getResultType()) {
2222 collapseShapeOp.getSrcMutable().assign(castOp.getSource());
2225 auto newOp = CollapseShapeOp::create(rewriter, collapseShapeOp.getLoc(),
2226 newResultType, castOp.getSource(),
2227 collapseShapeOp.getReassociation());
2229 collapseShapeOp, collapseShapeOp.getResultType(), newOp);
2239struct ConvertToStaticExpandShape :
public OpRewritePattern<ExpandShapeOp> {
2240 using OpRewritePattern<ExpandShapeOp>::OpRewritePattern;
2242 LogicalResult matchAndRewrite(ExpandShapeOp expandOp,
2243 PatternRewriter &rewriter)
const override {
2244 auto castOp = expandOp.getSrc().getDefiningOp<CastOp>();
2248 ArrayRef<int64_t> castSrcShape = castOp.getSource().getType().getShape();
2249 SmallVector<ReassociationIndices, 4> reassoc =
2250 expandOp.getReassociationIndices();
2252 SmallVector<int64_t> newOutputShape(expandOp.getResultType().getShape());
2253 SmallVector<Value> dynamicOutputShape;
2254 auto outputIt = expandOp.getOutputShape().begin();
2256 for (
const auto &[inputDim, innerReassoc] : llvm::enumerate(reassoc)) {
2257 for (uint64_t outDim : innerReassoc) {
2258 if (ShapedType::isStatic(newOutputShape[outDim]))
2265 Value val = *outputIt;
2267 if (ShapedType::isDynamic(castSrcShape[inputDim])) {
2268 dynamicOutputShape.push_back(val);
2274 newOutputShape[outDim] = cst.getSExtValue();
2276 dynamicOutputShape.push_back(val);
2282 if (expandOp.getOutputShape().size() == dynamicOutputShape.size())
2286 SmallVector<int64_t> newInputShape(expandOp.getSrcType().getRank(), 1l);
2287 for (
auto inDim : llvm::seq<int>(0, newInputShape.size())) {
2288 for (
auto outDim : reassoc[inDim]) {
2289 auto ofr = newOutputShape[outDim];
2290 if (ShapedType::isDynamic(ofr)) {
2291 newInputShape[inDim] = ShapedType::kDynamic;
2294 newInputShape[inDim] *= ofr;
2298 SmallVector<OpFoldResult> outputOfr =
2303 Type elementType = expandOp.getSrcType().getElementType();
2304 auto inputType = RankedTensorType::get(
2305 newInputShape, elementType,
2308 auto outputType = RankedTensorType::get(
2309 newOutputShape, elementType,
2311 newOutputShape, elementType));
2312 auto inputCast = CastOp::create(rewriter, expandOp.getLoc(), inputType,
2314 auto newExpand = ExpandShapeOp::create(
2315 rewriter, expandOp.getLoc(), outputType, inputCast.getResult(),
2316 expandOp.getReassociationIndices(), outputOfr);
2318 newExpand.getResult());
2324void ExpandShapeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2325 MLIRContext *context) {
2327 ComposeReassociativeReshapeOps<ExpandShapeOp, ReshapeOpKind::kExpand>,
2328 ComposeExpandOfCollapseOp<ExpandShapeOp, CollapseShapeOp, CastOp>,
2329 ConvertToStaticExpandShape, FoldReshapeWithConstant<ExpandShapeOp>,
2330 FoldReshapeWithSplat<ExpandShapeOp>,
2331 FoldReshapeWithFromElements<ExpandShapeOp>>(context);
2334void CollapseShapeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2335 MLIRContext *context) {
2337 ComposeReassociativeReshapeOps<CollapseShapeOp, ReshapeOpKind::kCollapse>,
2338 ComposeCollapseOfExpandOp<CollapseShapeOp, ExpandShapeOp, CastOp,
2339 tensor::DimOp, RankedTensorType>,
2340 FoldReshapeWithConstant<CollapseShapeOp>,
2341 FoldReshapeWithSplat<CollapseShapeOp>,
2342 FoldReshapeWithFromElements<CollapseShapeOp>, FoldCollapseOfCastOp>(
2346OpFoldResult ExpandShapeOp::fold(FoldAdaptor adaptor) {
2348 adaptor.getOperands());
2351OpFoldResult CollapseShapeOp::fold(FoldAdaptor adaptor) {
2353 adaptor.getOperands());
2360void ExtractSliceOp::getAsmResultNames(
2362 setNameFn(getResult(),
"extracted_slice");
2369ExtractSliceOp::inferResultType(RankedTensorType sourceTensorType,
2370 ArrayRef<int64_t> staticSizes) {
2374 assert(
static_cast<int64_t
>(staticSizes.size()) ==
2375 sourceTensorType.getRank() &&
2376 "unexpected staticSizes not equal to rank of source");
2377 return RankedTensorType::get(staticSizes, sourceTensorType.getElementType(),
2378 sourceTensorType.getEncoding());
2382ExtractSliceOp::inferResultType(RankedTensorType sourceTensorType,
2383 ArrayRef<OpFoldResult> sizes) {
2384 SmallVector<int64_t> staticSizes;
2387 assert(
static_cast<int64_t
>(staticSizes.size()) ==
2388 sourceTensorType.getRank() &&
2389 "unexpected staticSizes not equal to rank of source");
2390 return RankedTensorType::get(staticSizes, sourceTensorType.getElementType(),
2391 sourceTensorType.getEncoding());
2397 const llvm::SmallBitVector &droppedDims) {
2398 assert(staticSizes.size() == droppedDims.size() &&
2399 "expected one dropped-dimension bit per size");
2402 resultShape.reserve(staticSizes.size() - droppedDims.count());
2403 for (
auto [idx, size] : llvm::enumerate(staticSizes))
2404 if (!droppedDims.test(idx))
2405 resultShape.push_back(size);
2407 Type elementType = sourceTensorType.getElementType();
2408 return RankedTensorType::get(resultShape, elementType,
2410 resultShape, elementType));
2416 const llvm::SmallBitVector &droppedDims) {
2419 return inferSliceType(sourceTensorType, staticSizes, droppedDims);
2425 RankedTensorType resultType,
Value source,
2435 auto sourceRankedTensorType = llvm::cast<RankedTensorType>(source.
getType());
2438 resultType = llvm::cast<RankedTensorType>(
2439 ExtractSliceOp::inferResultType(sourceRankedTensorType, staticSizes));
2441 result.addAttributes(attrs);
2442 build(
b,
result, resultType, source, dynamicOffsets, dynamicSizes,
2443 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
2444 b.getDenseI64ArrayAttr(staticSizes),
2445 b.getDenseI64ArrayAttr(staticStrides));
2450void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2451 ArrayRef<OpFoldResult> offsets,
2452 ArrayRef<OpFoldResult> sizes,
2453 ArrayRef<OpFoldResult> strides,
2454 ArrayRef<NamedAttribute> attrs) {
2455 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2460void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2461 ArrayRef<Range> ranges,
2462 ArrayRef<NamedAttribute> attrs) {
2464 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2469void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result,
2470 RankedTensorType resultType, Value source,
2472 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2473 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
2474 offsets, [](Value v) -> OpFoldResult {
return v; });
2475 SmallVector<OpFoldResult> sizeValues =
2476 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
2477 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
2478 strides, [](Value v) -> OpFoldResult {
return v; });
2479 build(
b,
result, resultType, source, offsetValues, sizeValues, strideValues);
2483void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2485 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2486 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2491 RankedTensorType expectedType) {
2496 return op->
emitError(
"expected rank to be smaller or equal to ")
2497 <<
"the other rank. ";
2499 return op->
emitError(
"expected type to be ")
2500 << expectedType <<
" or a rank-reduced version. (size mismatch) ";
2502 return op->
emitError(
"expected element type to be ")
2503 << expectedType.getElementType();
2505 llvm_unreachable(
"unexpected extract_slice op verification result");
2511void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result,
2512 RankedTensorType resultType, Value source,
2513 ArrayRef<OpFoldResult> sizes,
2514 ArrayRef<NamedAttribute> attrs) {
2515 Attribute zeroIdxAttr =
b.getIndexAttr(0);
2516 Attribute oneIdxAttr =
b.getIndexAttr(1);
2517 SmallVector<OpFoldResult> readStrides(sizes.size(), oneIdxAttr);
2518 SmallVector<OpFoldResult> readOffsets(sizes.size(), zeroIdxAttr);
2519 build(
b,
result, resultType, source, readOffsets, sizes, readStrides, attrs);
2523LogicalResult ExtractSliceOp::verify() {
2524 RankedTensorType sourceType = getSourceType();
2527 RankedTensorType expectedType =
2528 ExtractSliceOp::inferResultType(sourceType,
getMixedSizes());
2536 sourceType.getShape(), getStaticOffsets(), getStaticSizes(),
2537 getStaticStrides(),
true);
2539 return getOperation()->emitError(boundsResult.
errorMessage);
2544llvm::SmallBitVector ExtractSliceOp::getDroppedDims() {
2549ExtractSliceOp::rankReduceIfNeeded(OpBuilder &
b, Location loc, Value value,
2550 ArrayRef<int64_t> desiredShape) {
2551 auto sourceTensorType = llvm::dyn_cast<RankedTensorType>(value.
getType());
2552 assert(sourceTensorType &&
"not a ranked tensor type");
2553 auto sourceShape = sourceTensorType.getShape();
2554 if (sourceShape.equals(desiredShape))
2556 auto maybeRankReductionMask =
2558 if (!maybeRankReductionMask)
2562 RankedTensorType::Builder(sourceTensorType).setShape(desiredShape));
2565LogicalResult ExtractSliceOp::reifyResultShapes(
2567 reifiedReturnShapes.resize(1);
2568 reifiedReturnShapes[0].reserve(
getType().getRank());
2571 for (
const auto &size :
enumerate(mixedSizes)) {
2572 if (droppedDims.test(size.index()))
2574 reifiedReturnShapes[0].push_back(size.value());
2595class ExtractSliceOpCastFolder final :
public OpRewritePattern<ExtractSliceOp> {
2597 using OpRewritePattern<ExtractSliceOp>::OpRewritePattern;
2599 LogicalResult matchAndRewrite(ExtractSliceOp sliceOp,
2600 PatternRewriter &rewriter)
const override {
2602 if (llvm::any_of(sliceOp.getOperands(), [](Value operand) {
2603 return matchPattern(operand, matchConstantIndex());
2607 auto castOp = sliceOp.getSource().getDefiningOp<CastOp>();
2616 cast<RankedTensorType>(castOp.getSource().getType()).getShape(),
2617 sliceOp.getStaticOffsets(), sliceOp.getStaticSizes(),
2618 sliceOp.getStaticStrides());
2623 Location loc = sliceOp.getLoc();
2624 Value newResult = ExtractSliceOp::create(
2625 rewriter, loc, sliceOp.getType(), castOp.getSource(),
2626 sliceOp.getOffsets(), sliceOp.getSizes(), sliceOp.getStrides(),
2627 sliceOp.getStaticOffsets(), sliceOp.getStaticSizes(),
2628 sliceOp.getStaticStrides());
2637template <
typename IterTy,
typename ElemTy>
2638static void sliceElements(IterTy values, ArrayRef<int64_t> counts,
2639 ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes,
2640 ArrayRef<int64_t> strides,
2641 llvm::SmallVectorImpl<ElemTy> *outValues) {
2642 assert(offsets.size() == sizes.size());
2643 assert(offsets.size() == strides.size());
2644 if (offsets.empty())
2647 int64_t offset = offsets.front();
2648 int64_t size = sizes.front();
2649 int64_t stride = strides.front();
2650 if (offsets.size() == 1) {
2651 for (int64_t i = 0; i < size; ++i, offset += stride)
2652 outValues->push_back(*(values + offset));
2657 for (int64_t i = 0; i < size; ++i, offset += stride) {
2658 auto begin = values + offset * counts.front();
2659 sliceElements<IterTy, ElemTy>(begin, counts.drop_front(),
2660 offsets.drop_front(), sizes.drop_front(),
2661 strides.drop_front(), outValues);
2668class ConstantOpExtractSliceFolder final
2669 :
public OpRewritePattern<ExtractSliceOp> {
2671 using OpRewritePattern<ExtractSliceOp>::OpRewritePattern;
2673 ConstantOpExtractSliceFolder(MLIRContext *context,
2675 : OpRewritePattern<ExtractSliceOp>(context),
2676 controlFn(std::move(controlFn)) {}
2678 LogicalResult matchAndRewrite(ExtractSliceOp op,
2679 PatternRewriter &rewriter)
const override {
2680 DenseElementsAttr attr;
2689 auto sourceType = llvm::cast<ShapedType>(op.getSource().getType());
2690 auto resultType = llvm::cast<ShapedType>(op.getResult().getType());
2691 if (!sourceType.hasStaticShape() || !resultType.hasStaticShape())
2698 int64_t count = sourceType.getNumElements();
2703 auto offsets = op.getStaticOffsets();
2704 if (llvm::is_contained(offsets, ShapedType::kDynamic))
2706 auto sizes = op.getStaticSizes();
2707 if (llvm::is_contained(sizes, ShapedType::kDynamic))
2709 auto strides = op.getStaticStrides();
2710 if (llvm::is_contained(strides, ShapedType::kDynamic))
2714 SmallVector<int64_t> counts;
2715 ArrayRef<int64_t> shape = sourceType.getShape();
2716 counts.reserve(shape.size());
2717 for (int64_t v : shape) {
2719 counts.push_back(count);
2723 SmallVector<Attribute> outValues;
2724 outValues.reserve(resultType.getNumElements());
2725 sliceElements(attr.
value_begin<Attribute>(), counts, offsets, sizes,
2726 strides, &outValues);
2743 patterns.
add<ConstantOpExtractSliceFolder>(patterns.
getContext(), controlFn);
2755 return inferSliceType(op.getSourceType(), mixedSizes, op.getDroppedDims());
2762 ExtractSliceOp newOp) {
2765 replacement = tensor::CastOp::create(rewriter, op.getLoc(), op.getType(),
2771void ExtractSliceOp::getCanonicalizationPatterns(RewritePatternSet &results,
2772 MLIRContext *context) {
2774 OpWithOffsetSizesAndStridesConstantArgumentFolder<
2775 ExtractSliceOp, SliceReturnTypeCanonicalizer, SliceCanonicalizer>,
2776 ExtractSliceOpCastFolder>(context);
2782 ShapedType shapedType) {
2789 auto shape = shapedType.getShape();
2790 for (
auto it : llvm::zip(op.getMixedSizes(),
shape))
2804 auto insertOp = extractOp.getSource().getDefiningOp<InsertSliceOp>();
2807 if (insertOp && insertOp.getSource().getType() == extractOp.getType() &&
2808 insertOp.isSameAs(extractOp, isSame))
2809 return insertOp.getSource();
2814OpFoldResult ExtractSliceOp::fold(FoldAdaptor adaptor) {
2815 if (OpFoldResult reshapedSource = reshapeConstantSource(
2816 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getSource()),
2818 return reshapedSource;
2819 if (getSourceType() ==
getType() &&
2821 return this->getSource();
2825 return OpFoldResult();
2830 auto rankedTensorType = llvm::cast<RankedTensorType>(
tensor.getType());
2831 unsigned rank = rankedTensorType.getRank();
2835 return b.createOrFold<tensor::ExtractSliceOp>(loc, targetType,
tensor,
2836 offsets, sizes, strides);
2843void InsertSliceOp::getAsmResultNames(
2845 setNameFn(getResult(),
"inserted_slice");
2859 result.addAttributes(attrs);
2860 build(
b,
result, dest.
getType(), source, dest, dynamicOffsets, dynamicSizes,
2861 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
2862 b.getDenseI64ArrayAttr(staticSizes),
2863 b.getDenseI64ArrayAttr(staticStrides));
2868void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2869 Value dest, ArrayRef<Range> ranges,
2870 ArrayRef<NamedAttribute> attrs) {
2872 build(
b,
result, source, dest, offsets, sizes, strides, attrs);
2876void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2878 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2879 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
2880 offsets, [](Value v) -> OpFoldResult {
return v; });
2881 SmallVector<OpFoldResult> sizeValues =
2882 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
2883 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
2884 strides, [](Value v) -> OpFoldResult {
return v; });
2885 build(
b,
result, source, dest, offsetValues, sizeValues, strideValues);
2891 RankedTensorType srcType, RankedTensorType dstType,
2896 RankedTensorType expected =
2897 ExtractSliceOp::inferResultType(dstType, staticSizes);
2899 *expectedType = expected;
2904LogicalResult InsertSliceOp::verify() {
2906 RankedTensorType expectedType;
2909 getStaticSizes(), getStaticStrides(), &expectedType);
2916 getDestType().
getShape(), getStaticOffsets(), getStaticSizes(),
2917 getStaticStrides(),
true);
2919 return getOperation()->emitError(boundsResult.
errorMessage);
2942 auto prevInsertOp = insertOp.getDest().getDefiningOp<InsertSliceOp>();
2945 if (!prevInsertOp ||
2946 prevInsertOp.getSource().getType() != insertOp.getSource().getType() ||
2947 !prevInsertOp.isSameAs(insertOp, isSame))
2950 insertOp.getDestMutable().assign(prevInsertOp.getDest());
2962 auto extractOp = insertOp.getSource().
getDefiningOp<ExtractSliceOp>();
2965 if (!extractOp || extractOp.getSource() != insertOp.getDest() ||
2966 !extractOp.isSameAs(insertOp, isSame))
2969 return extractOp.getSource();
2972OpFoldResult InsertSliceOp::fold(FoldAdaptor) {
2973 if (getSourceType().hasStaticShape() &&
getType().hasStaticShape() &&
2974 getSourceType() ==
getType() &&
2976 return this->getSource();
2983 return OpFoldResult();
2986LogicalResult InsertSliceOp::reifyResultShapes(
2988 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
2997template <
typename InsertOpTy>
2998class InsertSliceOpConstantArgumentFolder final
2999 :
public OpRewritePattern<InsertOpTy> {
3001 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3003 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3004 PatternRewriter &rewriter)
const override {
3005 SmallVector<OpFoldResult> mixedOffsets(insertSliceOp.getMixedOffsets());
3006 SmallVector<OpFoldResult> mixedSizes(insertSliceOp.getMixedSizes());
3007 SmallVector<OpFoldResult> mixedStrides(insertSliceOp.getMixedStrides());
3016 SliceBoundsVerificationResult sliceResult =
3018 mixedOffsets, mixedSizes, mixedStrides);
3022 auto sourceType =
inferSliceType(insertSliceOp.getSourceType(), mixedSizes,
3023 insertSliceOp.getDroppedDims());
3024 Value toInsert = insertSliceOp.getSource();
3025 if (sourceType != insertSliceOp.getSourceType()) {
3026 OpBuilder::InsertionGuard g(rewriter);
3030 if (isa<InParallelOpInterface>(insertSliceOp->getParentOp()))
3032 toInsert = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3033 sourceType, toInsert);
3036 insertSliceOp, toInsert, insertSliceOp.getDest(), mixedOffsets,
3037 mixedSizes, mixedStrides);
3062template <
typename InsertOpTy>
3063struct InsertSliceOpCastFolder final :
public OpRewritePattern<InsertOpTy> {
3064 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3066 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3067 PatternRewriter &rewriter)
const override {
3068 if (llvm::any_of(insertSliceOp.getOperands(), [](Value operand) {
3069 return matchPattern(operand, matchConstantIndex());
3073 auto getSourceOfCastOp = [](Value v) -> std::optional<Value> {
3076 return std::nullopt;
3077 return castOp.getSource();
3079 std::optional<Value> sourceCastSource =
3080 getSourceOfCastOp(insertSliceOp.getSource());
3081 std::optional<Value> destCastSource =
3082 getSourceOfCastOp(insertSliceOp.getDest());
3083 if (!sourceCastSource && !destCastSource)
3087 (sourceCastSource ? *sourceCastSource : insertSliceOp.getSource());
3088 auto dst = (destCastSource ? *destCastSource : insertSliceOp.getDest());
3089 auto srcType = llvm::dyn_cast<RankedTensorType>(src.
getType());
3090 auto dstType = llvm::dyn_cast<RankedTensorType>(dst.getType());
3091 if (!srcType || !dstType)
3097 SmallVector<int64_t> staticSizes(insertSliceOp.getStaticSizes());
3099 staticSizes, srcType.getShape(),
true);
3100 if (!rankReductionMask.has_value())
3107 SmallVector<OpFoldResult> mixedSizes(insertSliceOp.getMixedSizes());
3108 int64_t rankReducedIdx = 0;
3109 for (
auto [idx, size] :
enumerate(staticSizes)) {
3110 if (!rankReductionMask.value().contains(idx) &&
3111 !srcType.isDynamicDim(rankReducedIdx)) {
3113 rewriter.
getContext(), srcType.getDimSize(rankReducedIdx));
3114 size = srcType.getDimSize(rankReducedIdx++);
3120 staticSizes, insertSliceOp.getStaticStrides()) !=
3121 SliceVerificationResult::Success)
3123 SliceBoundsVerificationResult sliceResult =
3125 mixedSizes, insertSliceOp.getMixedStrides());
3130 InsertOpTy::create(rewriter, insertSliceOp.getLoc(), src, dst,
3131 insertSliceOp.getMixedOffsets(), mixedSizes,
3132 insertSliceOp.getMixedStrides());
3135 bool isParallelInsert =
3136 std::is_same<InsertOpTy, ParallelInsertSliceOp>::value;
3137 if (!isParallelInsert && dst.getType() != insertSliceOp.getDestType()) {
3138 replacement = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3139 insertSliceOp.getDestType(),
3168template <
typename InsertOpTy>
3169struct InsertSliceOpSourceCastInserter final
3170 :
public OpRewritePattern<InsertOpTy> {
3171 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3173 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3174 PatternRewriter &rewriter)
const override {
3175 RankedTensorType srcType = insertSliceOp.getSourceType();
3176 if (srcType.getRank() != insertSliceOp.getDestType().getRank())
3178 SmallVector<int64_t> newSrcShape(srcType.getShape());
3179 for (int64_t i = 0; i < srcType.getRank(); ++i) {
3180 if (std::optional<int64_t> constInt =
3185 newSrcShape[i] = *constInt;
3191 RankedTensorType newSrcType = RankedTensorType::get(
3192 newSrcShape, srcType.getElementType(), srcType.getEncoding());
3193 if (srcType == newSrcType ||
3195 !tensor::CastOp::areCastCompatible(srcType, newSrcType))
3203 OpBuilder::InsertionGuard g(rewriter);
3207 if (isa<ParallelCombiningOpInterface>(insertSliceOp->getParentOp()))
3209 Value cast = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3210 newSrcType, insertSliceOp.getSource());
3212 insertSliceOp, cast, insertSliceOp.getDest(),
3213 insertSliceOp.getMixedOffsets(), insertSliceOp.getMixedSizes(),
3214 insertSliceOp.getMixedStrides());
3220llvm::SmallBitVector InsertSliceOp::getDroppedDims() {
3224void InsertSliceOp::getCanonicalizationPatterns(RewritePatternSet &results,
3225 MLIRContext *context) {
3226 results.
add<InsertSliceOpConstantArgumentFolder<InsertSliceOp>,
3227 InsertSliceOpCastFolder<InsertSliceOp>,
3228 InsertSliceOpSourceCastInserter<InsertSliceOp>>(context);
3235 auto rankedTensorType = llvm::cast<RankedTensorType>(dest.
getType());
3236 unsigned rank = rankedTensorType.getRank();
3240 return b.createOrFold<tensor::InsertSliceOp>(loc,
tensor, dest, offsets,
3249 setNameFn(getResult(),
"padded");
3252LogicalResult PadOp::verify() {
3253 auto sourceType = llvm::cast<RankedTensorType>(getSource().
getType());
3254 auto resultType = llvm::cast<RankedTensorType>(getResult().
getType());
3256 PadOp::inferResultType(sourceType, getStaticLow(), getStaticHigh());
3257 if (!expectedType) {
3258 return emitError(
"failed to infer expectedType from sourceType ")
3259 << sourceType <<
", specified resultType is " << resultType;
3261 if (resultType.getRank() != expectedType.getRank()) {
3263 << resultType <<
" does not match the inferred type "
3266 for (
int i = 0, e = sourceType.getRank(); i < e; ++i) {
3267 if (resultType.getDimSize(i) == expectedType.getDimSize(i))
3269 if (expectedType.isDynamicDim(i))
3272 << resultType <<
" does not match the inferred type "
3279LogicalResult PadOp::verifyRegions() {
3280 auto ®ion = getRegion();
3281 unsigned rank = llvm::cast<RankedTensorType>(getResult().
getType()).getRank();
3282 Block &block = region.front();
3284 return emitError(
"expected the block to have ") << rank <<
" arguments";
3288 if (!en.value().isIndex())
3289 return emitOpError(
"expected block argument ")
3290 << (en.index() + 1) <<
" to be an index";
3295 if (yieldOp.getValue().getType() !=
3297 return emitOpError(
"expected yield type to match shape element type");
3302RankedTensorType PadOp::inferResultType(RankedTensorType sourceType,
3303 ArrayRef<int64_t> staticLow,
3304 ArrayRef<int64_t> staticHigh,
3305 ArrayRef<int64_t> resultShape) {
3306 unsigned rank = sourceType.getRank();
3307 if (staticLow.size() != rank)
3308 return RankedTensorType();
3309 if (staticHigh.size() != rank)
3310 return RankedTensorType();
3311 if (!resultShape.empty() && resultShape.size() != rank)
3312 return RankedTensorType();
3314 SmallVector<int64_t, 4> inferredShape;
3315 for (
auto i : llvm::seq<unsigned>(0, rank)) {
3316 if (sourceType.isDynamicDim(i) || staticLow[i] == ShapedType::kDynamic ||
3317 staticHigh[i] == ShapedType::kDynamic) {
3318 inferredShape.push_back(resultShape.empty() ? ShapedType::kDynamic
3321 int64_t size = sourceType.getDimSize(i) + staticLow[i] + staticHigh[i];
3322 assert((resultShape.empty() || size == resultShape[i] ||
3323 resultShape[i] == ShapedType::kDynamic) &&
3324 "mismatch between inferred shape and result shape");
3325 inferredShape.push_back(size);
3329 Type elementType = sourceType.getElementType();
3330 return RankedTensorType::get(
3331 inferredShape, elementType,
3335void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3336 Value source, ArrayRef<int64_t> staticLow,
3338 bool nofold, ArrayRef<NamedAttribute> attrs) {
3339 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3341 resultType = inferResultType(sourceType, staticLow, staticHigh);
3342 result.addAttributes(attrs);
3343 build(
b,
result, resultType, source, low, high,
3344 b.getDenseI64ArrayAttr(staticLow),
b.getDenseI64ArrayAttr(staticHigh),
3345 nofold ?
b.getUnitAttr() : UnitAttr());
3348void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3350 ArrayRef<NamedAttribute> attrs) {
3351 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3352 unsigned rank = sourceType.getRank();
3353 SmallVector<int64_t, 4> staticVector(rank, ShapedType::kDynamic);
3354 build(
b,
result, resultType, source, staticVector, staticVector, low, high,
3358void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3359 Value source, ArrayRef<OpFoldResult> low,
3360 ArrayRef<OpFoldResult> high,
bool nofold,
3361 ArrayRef<NamedAttribute> attrs) {
3362 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3363 SmallVector<Value, 4> dynamicLow, dynamicHigh;
3364 SmallVector<int64_t, 4> staticLow, staticHigh;
3372 resultType = PadOp::inferResultType(sourceType, staticLow, staticHigh);
3374 assert(llvm::isa<RankedTensorType>(resultType));
3375 result.addAttributes(attrs);
3376 build(
b,
result, resultType, source, dynamicLow, dynamicHigh,
3377 b.getDenseI64ArrayAttr(staticLow),
b.getDenseI64ArrayAttr(staticHigh),
3378 nofold ?
b.getUnitAttr() : UnitAttr());
3381void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3382 Value source, ArrayRef<OpFoldResult> low,
3383 ArrayRef<OpFoldResult> high, Value constantPadValue,
3384 bool nofold, ArrayRef<NamedAttribute> attrs) {
3385 build(
b,
result, resultType, source, low, high, nofold, attrs);
3388 Region *region =
result.regions[0].get();
3389 int sourceRank = llvm::cast<RankedTensorType>(source.
getType()).getRank();
3390 Repeated<Type> blockArgTypes(sourceRank,
b.getIndexType());
3391 SmallVector<Location> blockArgLocs(sourceRank,
result.location);
3395 OpBuilder::InsertionGuard guard(
b);
3396 b.createBlock(region, region->
end(), blockArgTypes, blockArgLocs);
3397 tensor::YieldOp::create(
b,
result.location, constantPadValue);
3400llvm::SmallBitVector PadOp::getPaddedDims() {
3401 llvm::SmallBitVector paddedDims(getSourceType().getRank());
3402 auto extractPaddedDims = [&](ArrayRef<OpFoldResult> paddingWidths) {
3403 for (
const auto &en :
enumerate(paddingWidths))
3405 paddedDims.set(en.index());
3407 extractPaddedDims(getMixedLowPad());
3408 extractPaddedDims(getMixedHighPad());
3415struct FoldStaticZeroPadding :
public OpRewritePattern<PadOp> {
3416 using OpRewritePattern<PadOp>::OpRewritePattern;
3418 LogicalResult matchAndRewrite(PadOp padTensorOp,
3419 PatternRewriter &rewriter)
const override {
3420 if (!padTensorOp.hasZeroLowPad() || !padTensorOp.hasZeroHighPad())
3422 if (padTensorOp.getNofold())
3425 padTensorOp, padTensorOp.getResult().
getType(),
3426 padTensorOp.getSource());
3432struct FoldSourceTensorCast :
public OpRewritePattern<PadOp> {
3433 using OpRewritePattern<PadOp>::OpRewritePattern;
3435 LogicalResult matchAndRewrite(PadOp padTensorOp,
3436 PatternRewriter &rewriter)
const override {
3437 auto castOp = padTensorOp.getSource().getDefiningOp<tensor::CastOp>();
3441 auto newResultType = PadOp::inferResultType(
3442 llvm::cast<RankedTensorType>(castOp.getSource().getType()),
3443 padTensorOp.getStaticLow(), padTensorOp.getStaticHigh(),
3444 padTensorOp.getResultType().getShape());
3446 if (newResultType == padTensorOp.getResultType()) {
3448 padTensorOp.getSourceMutable().assign(castOp.getSource());
3451 auto newOp = PadOp::create(
3452 rewriter, padTensorOp->getLoc(), newResultType,
3453 padTensorOp.getSource(), padTensorOp.getStaticLow(),
3454 padTensorOp.getStaticHigh(), padTensorOp.getLow(),
3455 padTensorOp.getHigh(), padTensorOp.getNofold(),
3458 padTensorOp.getRegion().cloneInto(&newOp.getRegion(), mapper);
3461 padTensorOp, padTensorOp.getResultType(), newOp);
3469struct FoldTargetTensorCast :
public OpRewritePattern<PadOp> {
3470 using OpRewritePattern<PadOp>::OpRewritePattern;
3472 LogicalResult matchAndRewrite(PadOp padTensorOp,
3473 PatternRewriter &rewriter)
const override {
3474 if (!padTensorOp.getResult().hasOneUse())
3477 dyn_cast<tensor::CastOp>(*padTensorOp->getUsers().begin());
3481 tensorCastOp.getDest().getType()))
3484 auto replacementOp = PadOp::create(
3485 rewriter, padTensorOp.getLoc(), tensorCastOp.getDest().getType(),
3486 padTensorOp.getSource(), padTensorOp.getStaticLow(),
3487 padTensorOp.getStaticHigh(), padTensorOp.getLow(),
3488 padTensorOp.getHigh(), padTensorOp.getNofold(),
3490 replacementOp.getRegion().takeBody(padTensorOp.getRegion());
3492 rewriter.
replaceOp(padTensorOp, replacementOp.getResult());
3493 rewriter.
replaceOp(tensorCastOp, replacementOp.getResult());
3533struct FoldOrthogonalPaddings :
public OpRewritePattern<PadOp> {
3534 using OpRewritePattern<PadOp>::OpRewritePattern;
3536 LogicalResult matchAndRewrite(PadOp padOp,
3537 PatternRewriter &rewriter)
const override {
3538 auto innerSliceOp = padOp.getSource().getDefiningOp<ExtractSliceOp>();
3541 auto outerPadOp = innerSliceOp.getSource().getDefiningOp<PadOp>();
3542 if (!outerPadOp || outerPadOp.getNofold())
3544 auto outerSliceOp = outerPadOp.getSource().getDefiningOp<ExtractSliceOp>();
3549 int64_t rank = padOp.getSourceType().getRank();
3550 if (outerSliceOp.getSourceType().getRank() != rank) {
3552 "cannot fold rank-reducing chain");
3556 if (!innerSliceOp.hasUnitStride() || !outerSliceOp.hasUnitStride()) {
3558 padOp,
"cannot fold non-unit stride ExtractSliceOps");
3562 if (!padOp.hasZeroLowPad() || !outerPadOp.hasZeroLowPad()) {
3564 "cannot fold PadOps with low padding");
3568 Attribute innerAttr, outerAttr;
3569 Value innerValue = padOp.getConstantPaddingValue();
3570 Value outerValue = outerPadOp.getConstantPaddingValue();
3571 if (!innerValue || !outerValue ||
3574 innerAttr != outerAttr) {
3576 padOp,
"cannot fold PadOps with different padding values");
3580 llvm::SmallBitVector innerDims = padOp.getPaddedDims();
3581 llvm::SmallBitVector outerDims = outerPadOp.getPaddedDims();
3582 if (innerDims.anyCommon(outerDims)) {
3584 padOp,
"cannot fold PadOps with common padding dimensions");
3592 SmallVector<OpFoldResult> newOffsets(rank, rewriter.
getIndexAttr(0));
3594 OpFoldResult innerOffset = innerSliceOp.getMixedOffsets()[en.index()];
3595 OpFoldResult outerOffset = outerSliceOp.getMixedOffsets()[en.index()];
3596 if (!innerDims.test(en.index()) &&
3598 en.value() = outerOffset;
3601 if (!outerDims.test(en.index()) &&
3603 en.value() = innerOffset;
3607 padOp,
"cannot find zero-offset and zero-padding pair");
3615 SmallVector<OpFoldResult> newSizes = innerSliceOp.getMixedSizes();
3617 if (!outerDims.test(en.index()))
3619 OpFoldResult sliceSize = innerSliceOp.getMixedSizes()[en.index()];
3620 int64_t sourceSize = innerSliceOp.getSourceType().getShape()[en.index()];
3621 assert(ShapedType::isStatic(sourceSize) &&
3622 "expected padded dimension to have a static size");
3625 padOp,
"cannot fold since the inner ExtractSliceOp size does not "
3626 "match the size of the outer padding");
3628 en.value() = outerSliceOp.getMixedSizes()[en.index()];
3632 SmallVector<OpFoldResult> newHighPad(rank, rewriter.
getIndexAttr(0));
3634 if (innerDims.test(en.index()))
3635 newHighPad[en.index()] = padOp.getMixedHighPad()[en.index()];
3636 if (outerDims.test(en.index()))
3637 newHighPad[en.index()] = outerPadOp.getMixedHighPad()[en.index()];
3642 auto newSliceOp = ExtractSliceOp::create(
3643 rewriter, padOp.getLoc(), outerSliceOp.getSource(), newOffsets,
3644 newSizes, innerSliceOp.getMixedStrides());
3645 auto newPadOp = PadOp::create(
3646 rewriter, padOp.getLoc(), padOp.getResultType(), newSliceOp.getResult(),
3647 padOp.getMixedLowPad(), newHighPad, padOp.getNofold(),
3650 newPadOp.getRegion().begin());
3651 rewriter.
replaceOp(padOp, newPadOp.getResult());
3656struct FoldStaticPadding :
public OpRewritePattern<PadOp> {
3657 using OpRewritePattern<PadOp>::OpRewritePattern;
3659 LogicalResult matchAndRewrite(PadOp padTensorOp,
3660 PatternRewriter &rewriter)
const override {
3661 Value input = padTensorOp.getSource();
3662 if (!llvm::isa<RankedTensorType>(input.
getType()))
3664 auto inputDims = llvm::cast<RankedTensorType>(input.
getType()).getShape();
3665 auto inputRank = inputDims.size();
3667 auto oldResultType =
3668 dyn_cast<RankedTensorType>(padTensorOp.getResult().getType());
3672 auto outputDims = oldResultType.getShape();
3675 SmallVector<int64_t> constOperandsLow;
3676 SmallVector<Value> newLows;
3677 for (
auto operand : padTensorOp.getLow()) {
3680 constOperandsLow.push_back(ShapedType::kDynamic);
3681 newLows.push_back(operand);
3684 constOperandsLow.push_back(intOp.getExtValue());
3686 SmallVector<int64_t> constOperandsHigh;
3687 SmallVector<Value> newHighs;
3688 for (
auto operand : padTensorOp.getHigh()) {
3691 constOperandsHigh.push_back(ShapedType::kDynamic);
3692 newHighs.push_back(operand);
3695 constOperandsHigh.push_back(intOp.getExtValue());
3698 SmallVector<int64_t> constLow(padTensorOp.getStaticLow());
3699 SmallVector<int64_t> constHigh(padTensorOp.getStaticHigh());
3702 if (inputDims.size() != outputDims.size() ||
3703 inputDims.size() != constLow.size() ||
3704 inputDims.size() != constHigh.size())
3709 for (
size_t i = 0; i < inputRank; i++) {
3710 if (constLow[i] == ShapedType::kDynamic)
3711 constLow[i] = constOperandsLow[lowCount++];
3712 if (constHigh[i] == ShapedType::kDynamic)
3713 constHigh[i] = constOperandsHigh[highCount++];
3716 auto staticLow = ArrayRef<int64_t>(constLow);
3717 auto staticHigh = ArrayRef<int64_t>(constHigh);
3720 SmallVector<int64_t> newOutDims;
3721 for (
size_t i = 0; i < inputRank; i++) {
3722 if (outputDims[i] == ShapedType::kDynamic) {
3723 newOutDims.push_back(
3724 (staticLow[i] == ShapedType::kDynamic ||
3725 staticHigh[i] == ShapedType::kDynamic ||
3726 inputDims[i] == ShapedType::kDynamic
3727 ? ShapedType::kDynamic
3728 : inputDims[i] + staticLow[i] + staticHigh[i]));
3730 newOutDims.push_back(outputDims[i]);
3734 if (SmallVector<int64_t>(outputDims) == newOutDims ||
3735 llvm::all_of(newOutDims,
3736 [&](int64_t x) {
return x == ShapedType::kDynamic; }))
3739 Type elementType = padTensorOp.getType().getElementType();
3740 auto newResultType = RankedTensorType::get(
3741 newOutDims, elementType,
3744 auto newOp = PadOp::create(
3745 rewriter, padTensorOp->getLoc(), newResultType, input, staticLow,
3746 staticHigh, newLows, newHighs, padTensorOp.getNofold(),
3750 padTensorOp.getRegion().cloneInto(&newOp.getRegion(), mapper);
3778struct FoldConsecutiveConstantPadding :
public OpRewritePattern<tensor::PadOp> {
3779 using OpRewritePattern<tensor::PadOp>::OpRewritePattern;
3781 LogicalResult matchAndRewrite(tensor::PadOp padOp,
3782 PatternRewriter &rewriter)
const override {
3783 if (padOp.getNofold()) {
3787 auto producerPad = padOp.getSource().getDefiningOp<tensor::PadOp>();
3788 if (!producerPad || producerPad.getNofold()) {
3790 padOp,
"producer is not a foldable tensor.pad op");
3794 Value consumerPadValue = padOp.getConstantPaddingValue();
3795 Value producerPadValue = producerPad.getConstantPaddingValue();
3796 if (!consumerPadValue || !producerPadValue ||
3797 consumerPadValue != producerPadValue) {
3800 "cannot fold PadOps with different or non-constant padding values");
3803 Location loc = padOp.getLoc();
3808 auto addPaddings = [&](ArrayRef<OpFoldResult> consumerPaddings,
3809 ArrayRef<OpFoldResult> producerPaddings) {
3810 SmallVector<OpFoldResult> sumPaddings;
3811 for (
auto [consumerIndex, producerIndex] :
3812 llvm::zip_equal(consumerPaddings, producerPaddings)) {
3814 rewriter, loc, d0 + d1, {consumerIndex, producerIndex}));
3819 SmallVector<OpFoldResult> newHighPad =
3820 addPaddings(padOp.getMixedHighPad(), producerPad.getMixedHighPad());
3821 SmallVector<OpFoldResult> newLowPad =
3822 addPaddings(padOp.getMixedLowPad(), producerPad.getMixedLowPad());
3824 auto newPadOp = tensor::PadOp::create(
3825 rewriter, padOp.getLoc(), padOp.getResultType(),
3826 producerPad.getSource(), newLowPad, newHighPad, padOp.getNofold(),
3829 newPadOp.getRegion().begin());
3830 rewriter.
replaceOp(padOp, newPadOp.getResult());
3838PadOp::reifyResultShapes(OpBuilder &
b,
3840 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
3841 SmallVector<OpFoldResult> lp = getMixedLowPad();
3842 SmallVector<OpFoldResult> hp = getMixedHighPad();
3843 for (int64_t i = 0; i < getResultType().getRank(); ++i) {
3844 if (!
getType().isDynamicDim(i)) {
3845 reifiedReturnShapes[0][i] =
b.getIndexAttr(
getType().getDimSize(i));
3848 Location loc = getLoc();
3849 Value dim =
b.createOrFold<tensor::DimOp>(
3852 AffineExpr d0, d1, d2;
3855 b, loc, {d0 + d1 + d2}, {dim, lp[i], hp[i]});
3860void PadOp::getCanonicalizationPatterns(RewritePatternSet &results,
3861 MLIRContext *context) {
3862 results.
add<FoldStaticZeroPadding, FoldSourceTensorCast, FoldTargetTensorCast,
3863 FoldOrthogonalPaddings, FoldStaticPadding,
3864 FoldConsecutiveConstantPadding>(context);
3876Value PadOp::getConstantPaddingValue() {
3877 auto yieldOp = dyn_cast<YieldOp>(getRegion().front().getTerminator());
3880 Value padValue = yieldOp.getValue();
3891OpFoldResult PadOp::fold(FoldAdaptor) {
3892 if (getResultType().hasStaticShape() && getResultType() == getSourceType() &&
3902OpResult ParallelInsertSliceOp::getTiedOpResult() {
3903 InParallelOpInterface parallelCombiningParent = getParallelCombiningParent();
3904 for (
const auto &it :
3905 llvm::enumerate(parallelCombiningParent.getYieldingOps())) {
3906 Operation &nextOp = it.value();
3907 if (&nextOp == getOperation())
3908 return parallelCombiningParent.getParentResult(it.index());
3910 llvm_unreachable(
"ParallelInsertSliceOp no tied OpResult found");
3914void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3915 Value source, Value dest,
3916 ArrayRef<OpFoldResult> offsets,
3917 ArrayRef<OpFoldResult> sizes,
3918 ArrayRef<OpFoldResult> strides,
3919 ArrayRef<NamedAttribute> attrs) {
3920 SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;
3921 SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;
3925 result.addAttributes(attrs);
3926 build(
b,
result, {}, source, dest, dynamicOffsets, dynamicSizes,
3927 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
3928 b.getDenseI64ArrayAttr(staticSizes),
3929 b.getDenseI64ArrayAttr(staticStrides));
3934void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3935 Value source, Value dest,
3936 ArrayRef<Range> ranges,
3937 ArrayRef<NamedAttribute> attrs) {
3939 build(
b,
result, source, dest, offsets, sizes, strides, attrs);
3943void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3944 Value source, Value dest,
ValueRange offsets,
3946 ArrayRef<NamedAttribute> attrs) {
3947 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
3948 offsets, [](Value v) -> OpFoldResult {
return v; });
3949 SmallVector<OpFoldResult> sizeValues =
3950 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
3951 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
3952 strides, [](Value v) -> OpFoldResult {
return v; });
3953 build(
b,
result, source, dest, offsetValues, sizeValues, strideValues);
3958void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
3959 Value dest, ArrayRef<OpFoldResult> sizes,
3960 ArrayRef<NamedAttribute> attrs) {
3961 Attribute zeroIdxAttr =
b.getIndexAttr(0);
3962 Attribute oneIdxAttr =
b.getIndexAttr(1);
3963 SmallVector<OpFoldResult> writeStrides(sizes.size(), oneIdxAttr);
3964 SmallVector<OpFoldResult> writeOffsets(sizes.size(), zeroIdxAttr);
3965 build(
b,
result, source, dest, writeOffsets, sizes, writeStrides, attrs);
3968LogicalResult ParallelInsertSliceOp::verify() {
3969 if (!isa<InParallelOpInterface>(getOperation()->getParentOp()))
3970 return this->
emitError(
"expected InParallelOpInterface parent, got:")
3971 << *(getOperation()->getParentOp());
3974 RankedTensorType expectedType;
3977 getStaticSizes(), getStaticStrides(), &expectedType);
3984 getDestType().
getShape(), getStaticOffsets(), getStaticSizes(),
3985 getStaticStrides(),
true);
3987 return getOperation()->emitError(boundsResult.
errorMessage);
3992void ParallelInsertSliceOp::getCanonicalizationPatterns(
3993 RewritePatternSet &results, MLIRContext *context) {
3994 results.
add<InsertSliceOpConstantArgumentFolder<ParallelInsertSliceOp>,
3995 InsertSliceOpCastFolder<ParallelInsertSliceOp>,
3996 InsertSliceOpSourceCastInserter<ParallelInsertSliceOp>>(context);
3999llvm::SmallBitVector ParallelInsertSliceOp::getDroppedDims() {
4004MutableOperandRange ParallelInsertSliceOp::getUpdatedDestinations() {
4005 return getDestMutable();
4008Operation *ParallelInsertSliceOp::getIteratingParent() {
4010 if (
auto combiningOp =
4011 dyn_cast<InParallelOpInterface>(getOperation()->getParentOp()))
4012 return combiningOp->getParentOp();
4020void ScatterOp::getAsmResultNames(
4022 setNameFn(getResult(),
"scatter");
4025LogicalResult ScatterOp::verify() {
4026 int64_t destRank = getDestType().getRank();
4027 ArrayRef<int64_t> scatterDims = getScatterDims();
4029 getIndicesType().
getShape(), destRank,
4030 "scatter",
"dest")))
4034 return emitOpError(
"requires 'unique' attribute to be set");
4041 RankedTensorType expectedSourceType = GatherOp::inferResultType(
4042 getDestType(), getIndicesType(), scatterDims,
false);
4043 RankedTensorType expectedRankReducedSourceType = GatherOp::inferResultType(
4044 getDestType(), getIndicesType(), scatterDims,
true);
4045 if (getSourceType() != expectedSourceType &&
4046 getSourceType() != expectedRankReducedSourceType) {
4047 return emitOpError(
"source type "
4050 << expectedSourceType <<
" or its rank-reduced variant "
4051 << expectedRankReducedSourceType <<
" (got: " << getSourceType()
4062void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4063 Type aggregateType,
ValueRange dynamicSizes) {
4064 build(builder,
result, aggregateType, element, dynamicSizes);
4067void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4068 ArrayRef<int64_t> staticShape,
ValueRange dynamicSizes) {
4069 auto aggregateType = RankedTensorType::get(staticShape, element.
getType());
4070 build(builder,
result, aggregateType, element, dynamicSizes);
4073void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4074 ArrayRef<OpFoldResult> sizes) {
4075 SmallVector<int64_t> staticShape;
4076 SmallVector<Value> dynamicSizes;
4078 build(builder,
result, element, staticShape, dynamicSizes);
4081void SplatOp::getAsmResultNames(
4083 setNameFn(getResult(),
"splat");
4086LogicalResult SplatOp::verify() {
4092SplatOp::reifyResultShapes(OpBuilder &builder,
4094 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
4096 for (int64_t i = 0; i <
getType().getRank(); ++i) {
4097 if (
getType().isDynamicDim(i)) {
4106OpFoldResult SplatOp::fold(FoldAdaptor adaptor) {
4107 auto constOperand = adaptor.getInput();
4108 if (!isa_and_nonnull<IntegerAttr, FloatAttr>(constOperand))
4112 if (!
getType().hasStaticShape())
4127 if (isa<InsertSliceOp>(op.getOperation()) ||
4128 isa<LoopLikeOpInterface>(op.getOperation()))
4161 isa<linalg::RelayoutOpInterface>(*op))
4169 auto newOp =
clone(rewriter, op, newResultTypes, newOperands);
4172 replacements.reserve(newOp->getNumResults());
4173 for (
auto [oldResult, newResult] :
4174 llvm::zip(op->getResults(), newOp->getResults())) {
4175 if (newResult.getType() != oldResult.getType()) {
4176 replacements.push_back(tensor::CastOp::create(
4177 rewriter, op->getLoc(), oldResult.
getType(), newResult));
4179 replacements.push_back(newResult);
4192void TensorDialect::getCanonicalizationPatterns(
4193 RewritePatternSet &results)
const {
4201#define GET_OP_CLASSES
4202#include "mlir/Dialect/Tensor/IR/TensorOps.cpp.inc"
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
static int64_t getNumElements(Type t)
Compute the total number of elements in the given type, also taking into account nested types.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
static void getDynamicSizes(RankedTensorType tp, ValueRange sizes, SmallVectorImpl< Value > &dynSizes)
Collects the dynamic dimension sizes for tp with the assumption that sizes are the dimension sizes fo...
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
static TensorType joinShapes(TensorType one, TensorType two)
Compute a TensorType that has the joined shape knowledge of the two given TensorTypes.
static Value foldExtractAfterInsert(ExtractOp extractOp)
If we have an ExtractOp consuming an InsertOp with the same indices, we can return the InsertOp's sca...
static LogicalResult verifyGatherOrScatterDims(Operation *op, ArrayRef< int64_t > dims, ArrayRef< int64_t > indices, int64_t rank, StringRef gatherOrScatter, StringRef sourceOrDest)
static LogicalResult produceSliceErrorMsg(SliceVerificationResult result, Operation *op, RankedTensorType expectedType)
static bool foldTensorCastPrecondition(DestinationStyleOpInterface op)
static LogicalResult foldInsertAfterInsertSlice(InsertSliceOp insertOp)
If we have two consecutive InsertSliceOp writing to the same slice, we can mutate the second InsertSl...
static Attribute propagateEncoding(Attribute encoding, ArrayRef< int64_t > shape, Type elementType)
Implements the VerifiableTensorEncoding contract documented in TensorEncoding.td for patterns that re...
static LogicalResult foldIdentityOffsetSizeAndStrideOpInterface(OffsetSizeAndStrideOpInterface op, ShapedType shapedType)
static Value foldExtractAfterInsertSlice(ExtractSliceOp extractOp)
If we have an ExtractSliceOp consuming an InsertSliceOp with the same slice, we can return the Insert...
static SliceVerificationResult verifyInsertSliceOp(RankedTensorType srcType, RankedTensorType dstType, ArrayRef< int64_t > staticOffsets, ArrayRef< int64_t > staticSizes, ArrayRef< int64_t > staticStrides, RankedTensorType *expectedType=nullptr)
Rank-reducing type verification for both InsertSliceOp and ParallelInsertSliceOp.
static RankedTensorType foldDynamicToStaticDimSizes(RankedTensorType type, ValueRange dynamicSizes, SmallVector< Value > &foldedDynamicSizes)
Given a ranked tensor type and a range of values that defines its dynamic dimension sizes,...
static llvm::SmallBitVector getDroppedDims(ArrayRef< int64_t > reducedShape, ArrayRef< OpFoldResult > mixedSizes)
Compute the dropped dimensions of a rank-reducing tensor.extract_slice op or rank-extending tensor....
static Value foldInsertAfterExtractSlice(InsertSliceOp insertOp)
Folds round-trip extract/insert slice op pairs.
static LogicalResult verifyTensorReshapeOp(TensorReshapeOp op, RankedTensorType expandedType, RankedTensorType collapsedType)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Base type for affine expression.
Attributes are known-constant values of operations.
MLIRContext * getContext() const
Return the context this attribute belongs to.
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
unsigned getNumArguments()
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgListType getArguments()
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
AffineExpr getAffineSymbolExpr(unsigned position)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
AffineExpr getAffineDimExpr(unsigned position)
AffineMap getConstantAffineMap(int64_t val)
Returns a single constant result affine map with 0 dimensions and 0 symbols.
MLIRContext * getContext() const
auto value_begin() const
Get an iterator of the given type to the start of the held element values.
static DenseElementsAttr getFromRawBuffer(ShapedType type, ArrayRef< char > rawBuffer)
Construct a dense elements attribute from a raw buffer representing the data for this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
ArrayRef< char > getRawData() const
Return the raw storage data held by this attribute.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
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.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
This class represents an operand of an operation.
This is a value defined by a result of an operation.
unsigned getResultNumber() const
Returns the number of this result.
Operation is the basic unit of execution within MLIR.
MutableArrayRef< OpOperand > getOpOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
result_range getResults()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
void inlineRegionBefore(Region ®ion, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
bool hasRank() const
Returns if this type is ranked, i.e. it has a known number of dimensions.
Type getElementType() const
Returns the element type of this tensor type.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
This class provides an abstraction over the different types of ranges over Values.
type_range getType() const
type_range getTypes() const
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.
Block * getParentBlock()
Return the Block in which this Value is defined.
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.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto Speculatable
constexpr auto NotSpeculatable
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
ConstantIntRanges inferShapedDimOpInterface(ShapedDimOpInterface op, const IntegerValueRange &maybeDim)
Returns the integer range for the result of a ShapedDimOpInterface given the optional inferred ranges...
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
DynamicAPInt getIndex(const ConeV &cone)
Get the index of a cone, i.e., the volume of the parallelepiped spanned by its generators,...
Value constantIndex(OpBuilder &builder, Location loc, int64_t i)
Generates a constant of index type.
LogicalResult foldTensorCast(Operation *op)
Performs folding of any operand of op if it comes from a tensor::CastOp that can be folded.
bool hasFoldableTensorCastOperand(Operation *op)
Return true if any of the operands of op is a CastOp that can be folded into its consumer,...
void populateFoldConstantExtractSlicePatterns(RewritePatternSet &patterns, const ControlConstantExtractSliceFusionFn &controlFn=[](ExtractSliceOp op) { return false;})
Patterns to fold the extract slice op with its constant operand.
bool canFoldIntoProducerOp(CastOp castOp)
Determines whether the tensor::CastOp casts to a more static version of the source tensor.
SmallVector< Value > getUpdatedOperandsAfterCastOpFolding(DestinationStyleOpInterface op, SmallVector< Type > &newResTy)
Assuming that op contains at least one operand that is a foldable CastOp (i.e.
bool canFoldIntoConsumerOp(CastOp castOp)
Determines whether tensor::CastOp casts to a more dynamic version of the source tensor.
Value createCanonicalRankReducingInsertSliceOp(OpBuilder &b, Location loc, Value tensor, Value dest)
Create a rank-reducing InsertSliceOp @[0 .
Value createCanonicalRankReducingExtractSliceOp(OpBuilder &b, Location loc, Value tensor, RankedTensorType targetType)
Create a rank-reducing ExtractSliceOp @[0 .
bool isSameTypeWithoutEncoding(Type tp1, Type tp2)
Tests if types are the same when ignoring encoding on ranked tensors.
RankedTensorType inferSliceType(RankedTensorType sourceTensorType, ArrayRef< int64_t > staticSizes, const llvm::SmallBitVector &droppedDims)
Infer a slice type for the given sizes and exact dropped-dimension mask.
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
void populateFoldCollapseExtractPatterns(RewritePatternSet &patterns)
Patterns to fold extracts of a collapse_shaped tensor to an extract of the source tensor.
FailureOr< Value > getOrCreateDestination(OpBuilder &b, Location loc, OpResult opResult)
This is a helper function for DestinationStyleOpInterface.
bool preservesStaticInformation(Type source, Type target)
Returns true if target is a ranked tensor type that preserves static information available in the sou...
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
LogicalResult getOrCreateDestinations(OpBuilder &b, Location loc, Operation *op, SmallVector< Value > &result)
This is a helper function for DestinationStyleOpInterface.
std::function< bool(ExtractSliceOp)> ControlConstantExtractSliceFusionFn
Function to control the folding of constant and extract slice.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
std::tuple< SmallVector< OpFoldResult >, SmallVector< OpFoldResult >, SmallVector< OpFoldResult > > getOffsetsSizesAndStrides(ArrayRef< Range > ranges)
Given an array of Range values, return a tuple of (offset vector, sizes vector, and strides vector) f...
SliceVerificationResult
Enum that captures information related to verifier error conditions on slice insert/extract type of o...
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
LogicalResult reifyResultShapes(OpBuilder &b, Operation *op, ReifiedRankedShapedTypeDims &reifiedReturnShapes)
Reify the shape of the result of an operation (typically in terms of the shape of its operands).
LogicalResult foldDynamicStrideList(SmallVectorImpl< OpFoldResult > &strides)
Returns "success" when any of the elements in strides is a constant value.
llvm::function_ref< void(Value, const IntegerValueRange &)> SetIntLatticeFn
Similar to SetIntRangeFn, but operating on IntegerValueRange lattice values.
SliceBoundsVerificationResult verifyInBoundsSlice(ArrayRef< int64_t > shape, ArrayRef< int64_t > staticOffsets, ArrayRef< int64_t > staticSizes, ArrayRef< int64_t > staticStrides, bool generateErrorMessage=false)
Verify that the offsets/sizes/strides-style access into the given shape is in-bounds.
LogicalResult verifyDynamicDimensionCount(Operation *op, ShapedType type, ValueRange dynamicSizes)
Verify that the number of dynamic size operands matches the number of dynamic dimensions in the shape...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
SmallVector< AffineMap, 4 > getSymbolLessAffineMaps(ArrayRef< ReassociationExprs > reassociation)
Constructs affine maps out of Array<Array<AffineExpr>>.
OpFoldResult foldReshapeOp(ReshapeOpTy reshapeOp, ArrayRef< Attribute > operands)
bool hasValidSizesOffsets(SmallVector< int64_t > sizesOrOffsets)
Helper function to check whether the passed in sizes or offsets are valid.
bool wouldOpBeTriviallyDead(Operation *op)
Return true if the given operation would be dead if unused, and has no side effects on memory that wo...
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
SmallVector< SmallVector< AffineExpr, 2 >, 2 > convertReassociationIndicesToExprs(MLIRContext *context, ArrayRef< ReassociationIndices > reassociationIndices)
Convert reassociation indices to affine expressions.
bool isReassociationValid(ArrayRef< AffineMap > reassociation, int *invalidIndex=nullptr)
Return true if the reassociation specification is valid, false otherwise.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
std::optional< SmallVector< OpFoldResult > > inferExpandShapeOutputShape(OpBuilder &b, Location loc, ShapedType expandedType, ArrayRef< ReassociationIndices > reassociation, ArrayRef< OpFoldResult > inputShape)
Infer the output shape for a {memref|tensor}.expand_shape when it is possible to do so.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
std::optional< llvm::SmallDenseSet< unsigned > > computeRankReductionMask(ArrayRef< int64_t > originalShape, ArrayRef< int64_t > reducedShape, bool matchDynamic=false)
Given an originalShape and a reducedShape assumed to be a subset of originalShape with some 1 entries...
LogicalResult verifyCompatibleShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2)
Returns success if the given two shapes are compatible.
SmallVector< int64_t, 2 > ReassociationIndices
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
SliceVerificationResult isRankReducedType(ShapedType originalType, ShapedType candidateReducedType)
Check if originalType can be rank reduced to candidateReducedType type by dropping some dimensions wi...
ArrayAttr getReassociationIndicesAttribute(Builder &b, ArrayRef< ReassociationIndices > reassociation)
Wraps a list of reassociations in an ArrayAttr.
llvm::function_ref< Fn > function_ref
SmallVector< NamedAttribute > getPrunedAttributeList(Operation *op, ArrayRef< StringRef > elidedAttrs)
LogicalResult foldDynamicOffsetSizeList(SmallVectorImpl< OpFoldResult > &offsetsOrSizes)
Returns "success" when any of the elements in offsetsOrSizes is a constant value.
std::pair< SmallVector< int64_t >, SmallVector< Value > > decomposeMixedValues(ArrayRef< OpFoldResult > mixedValues)
Decompose a vector of mixed static or dynamic values into the corresponding pair of arrays.
Folds a tensor.cast op into a consuming DestinationStyleOpInterface op if the tensor....
LogicalResult matchAndRewrite(DestinationStyleOpInterface op, PatternRewriter &rewriter) const override
A canonicalizer wrapper to replace ExtractSliceOps.
void operator()(PatternRewriter &rewriter, ExtractSliceOp op, ExtractSliceOp newOp)
Return the canonical type of the result of an extract_slice op.
RankedTensorType operator()(ExtractSliceOp op, ArrayRef< OpFoldResult > mixedOffsets, ArrayRef< OpFoldResult > mixedSizes, ArrayRef< OpFoldResult > mixedStrides)
OpInterfaceRewritePattern(MLIRContext *context, PatternBenefit benefit=1)
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Idiomatic saturated operations on values like offsets, sizes, and strides.
static SaturatedInteger wrap(int64_t v)
FailureOr< SaturatedInteger > desaturate(SaturatedInteger other)
bool isValid
If set to "true", the slice bounds verification was successful.
std::string errorMessage
An error message that can be printed during op verification.