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() {
2098 RankedTensorType srcType = getSrc().getType();
2099 RankedTensorType resultType = getResult().getType();
2101 if ((int64_t)getStaticOutputShape().size() != resultType.getRank())
2102 return emitOpError(
"expected number of static shape dims to be equal to "
2103 "the output rank (")
2104 << resultType.getRank() <<
") but found "
2105 << getStaticOutputShape().size() <<
" inputs instead";
2107 if ((int64_t)getOutputShape().size() !=
2108 llvm::count(getStaticOutputShape(), ShapedType::kDynamic))
2109 return emitOpError(
"mismatch in dynamic dims in output_shape and "
2110 "static_output_shape: static_output_shape has ")
2111 << llvm::count(getStaticOutputShape(), ShapedType::kDynamic)
2112 <<
" dynamic dims while output_shape has " << getOutputShape().size()
2123 ArrayRef<int64_t> resShape = getResult().getType().getShape();
2124 for (
auto [pos, shape] : llvm::enumerate(resShape))
2125 if (ShapedType::isStatic(shape) && shape != staticOutputShapes[pos])
2126 return emitOpError(
"invalid output shape provided at pos ") << pos;
2131LogicalResult CollapseShapeOp::verify() {
2132 CollapseShapeOp op = *
this;
2136 RankedTensorType srcType = op.getSrc().getType();
2137 RankedTensorType resultType = op.getResult().getType();
2145template <
typename TensorReshapeOp>
2146struct FoldReshapeWithConstant : OpRewritePattern<TensorReshapeOp> {
2147 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2148 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2149 PatternRewriter &rewriter)
const override {
2150 DenseElementsAttr attr;
2157 if (!reshapeOp.getResultType().hasStaticShape())
2160 reshapeOp.getResultType(), attr.
getRawData());
2167template <
typename TensorReshapeOp>
2168class FoldReshapeWithSplat :
public OpRewritePattern<TensorReshapeOp> {
2170 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2172 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2173 PatternRewriter &rewriter)
const override {
2174 auto splatOp = reshapeOp.getSrc().template getDefiningOp<tensor::SplatOp>();
2175 if (!splatOp || !splatOp.getAggregate().getType().hasStaticShape())
2179 reshapeOp, reshapeOp.getResultType(), splatOp.getInput());
2186template <
typename TensorReshapeOp>
2187struct FoldReshapeWithFromElements : OpRewritePattern<TensorReshapeOp> {
2188 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2189 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2190 PatternRewriter &rewriter)
const override {
2192 reshapeOp.getSrc().template getDefiningOp<FromElementsOp>();
2196 auto shapedTy = llvm::cast<ShapedType>(reshapeOp.getType());
2198 if (!shapedTy.hasStaticShape())
2202 fromElements.getElements());
2208struct FoldCollapseOfCastOp :
public OpRewritePattern<CollapseShapeOp> {
2209 using OpRewritePattern<CollapseShapeOp>::OpRewritePattern;
2211 LogicalResult matchAndRewrite(CollapseShapeOp collapseShapeOp,
2212 PatternRewriter &rewriter)
const override {
2213 auto castOp = collapseShapeOp.getSrc().getDefiningOp<tensor::CastOp>();
2217 RankedTensorType srcType =
2218 llvm::cast<RankedTensorType>(castOp.getSource().getType());
2219 RankedTensorType newResultType = CollapseShapeOp::inferCollapsedType(
2220 srcType, collapseShapeOp.getReassociationMaps());
2222 if (newResultType == collapseShapeOp.getResultType()) {
2224 collapseShapeOp.getSrcMutable().assign(castOp.getSource());
2227 auto newOp = CollapseShapeOp::create(rewriter, collapseShapeOp.getLoc(),
2228 newResultType, castOp.getSource(),
2229 collapseShapeOp.getReassociation());
2231 collapseShapeOp, collapseShapeOp.getResultType(), newOp);
2241struct ConvertToStaticExpandShape :
public OpRewritePattern<ExpandShapeOp> {
2242 using OpRewritePattern<ExpandShapeOp>::OpRewritePattern;
2244 LogicalResult matchAndRewrite(ExpandShapeOp expandOp,
2245 PatternRewriter &rewriter)
const override {
2246 auto castOp = expandOp.getSrc().getDefiningOp<CastOp>();
2250 ArrayRef<int64_t> castSrcShape = castOp.getSource().getType().getShape();
2251 SmallVector<ReassociationIndices, 4> reassoc =
2252 expandOp.getReassociationIndices();
2254 SmallVector<int64_t> newOutputShape(expandOp.getResultType().getShape());
2255 SmallVector<Value> dynamicOutputShape;
2256 auto outputIt = expandOp.getOutputShape().begin();
2258 for (
const auto &[inputDim, innerReassoc] : llvm::enumerate(reassoc)) {
2259 for (uint64_t outDim : innerReassoc) {
2260 if (ShapedType::isStatic(newOutputShape[outDim]))
2267 Value val = *outputIt;
2269 if (ShapedType::isDynamic(castSrcShape[inputDim])) {
2270 dynamicOutputShape.push_back(val);
2276 newOutputShape[outDim] = cst.getSExtValue();
2278 dynamicOutputShape.push_back(val);
2284 if (expandOp.getOutputShape().size() == dynamicOutputShape.size())
2288 SmallVector<int64_t> newInputShape(expandOp.getSrcType().getRank(), 1l);
2289 for (
auto inDim : llvm::seq<int>(0, newInputShape.size())) {
2290 for (
auto outDim : reassoc[inDim]) {
2291 auto ofr = newOutputShape[outDim];
2292 if (ShapedType::isDynamic(ofr)) {
2293 newInputShape[inDim] = ShapedType::kDynamic;
2296 newInputShape[inDim] *= ofr;
2300 SmallVector<OpFoldResult> outputOfr =
2305 Type elementType = expandOp.getSrcType().getElementType();
2306 auto inputType = RankedTensorType::get(
2307 newInputShape, elementType,
2310 auto outputType = RankedTensorType::get(
2311 newOutputShape, elementType,
2313 newOutputShape, elementType));
2314 auto inputCast = CastOp::create(rewriter, expandOp.getLoc(), inputType,
2316 auto newExpand = ExpandShapeOp::create(
2317 rewriter, expandOp.getLoc(), outputType, inputCast.getResult(),
2318 expandOp.getReassociationIndices(), outputOfr);
2320 newExpand.getResult());
2326void ExpandShapeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2327 MLIRContext *context) {
2329 ComposeReassociativeReshapeOps<ExpandShapeOp, ReshapeOpKind::kExpand>,
2330 ComposeExpandOfCollapseOp<ExpandShapeOp, CollapseShapeOp, CastOp>,
2331 ConvertToStaticExpandShape, FoldReshapeWithConstant<ExpandShapeOp>,
2332 FoldReshapeWithSplat<ExpandShapeOp>,
2333 FoldReshapeWithFromElements<ExpandShapeOp>>(context);
2336void CollapseShapeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2337 MLIRContext *context) {
2339 ComposeReassociativeReshapeOps<CollapseShapeOp, ReshapeOpKind::kCollapse>,
2340 ComposeCollapseOfExpandOp<CollapseShapeOp, ExpandShapeOp, CastOp,
2341 tensor::DimOp, RankedTensorType>,
2342 FoldReshapeWithConstant<CollapseShapeOp>,
2343 FoldReshapeWithSplat<CollapseShapeOp>,
2344 FoldReshapeWithFromElements<CollapseShapeOp>, FoldCollapseOfCastOp>(
2348OpFoldResult ExpandShapeOp::fold(FoldAdaptor adaptor) {
2350 adaptor.getOperands());
2353OpFoldResult CollapseShapeOp::fold(FoldAdaptor adaptor) {
2355 adaptor.getOperands());
2362void ExtractSliceOp::getAsmResultNames(
2364 setNameFn(getResult(),
"extracted_slice");
2371ExtractSliceOp::inferResultType(RankedTensorType sourceTensorType,
2372 ArrayRef<int64_t> staticSizes) {
2376 assert(
static_cast<int64_t
>(staticSizes.size()) ==
2377 sourceTensorType.getRank() &&
2378 "unexpected staticSizes not equal to rank of source");
2379 return RankedTensorType::get(staticSizes, sourceTensorType.getElementType(),
2380 sourceTensorType.getEncoding());
2384ExtractSliceOp::inferResultType(RankedTensorType sourceTensorType,
2385 ArrayRef<OpFoldResult> sizes) {
2386 SmallVector<int64_t> staticSizes;
2389 assert(
static_cast<int64_t
>(staticSizes.size()) ==
2390 sourceTensorType.getRank() &&
2391 "unexpected staticSizes not equal to rank of source");
2392 return RankedTensorType::get(staticSizes, sourceTensorType.getElementType(),
2393 sourceTensorType.getEncoding());
2399 const llvm::SmallBitVector &droppedDims) {
2400 assert(staticSizes.size() == droppedDims.size() &&
2401 "expected one dropped-dimension bit per size");
2404 resultShape.reserve(staticSizes.size() - droppedDims.count());
2405 for (
auto [idx, size] : llvm::enumerate(staticSizes))
2406 if (!droppedDims.test(idx))
2407 resultShape.push_back(size);
2409 Type elementType = sourceTensorType.getElementType();
2410 return RankedTensorType::get(resultShape, elementType,
2412 resultShape, elementType));
2418 const llvm::SmallBitVector &droppedDims) {
2421 return inferSliceType(sourceTensorType, staticSizes, droppedDims);
2427 RankedTensorType resultType,
Value source,
2437 auto sourceRankedTensorType = llvm::cast<RankedTensorType>(source.
getType());
2440 resultType = llvm::cast<RankedTensorType>(
2441 ExtractSliceOp::inferResultType(sourceRankedTensorType, staticSizes));
2443 result.addAttributes(attrs);
2444 build(
b,
result, resultType, source, dynamicOffsets, dynamicSizes,
2445 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
2446 b.getDenseI64ArrayAttr(staticSizes),
2447 b.getDenseI64ArrayAttr(staticStrides));
2452void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2453 ArrayRef<OpFoldResult> offsets,
2454 ArrayRef<OpFoldResult> sizes,
2455 ArrayRef<OpFoldResult> strides,
2456 ArrayRef<NamedAttribute> attrs) {
2457 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2462void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2463 ArrayRef<Range> ranges,
2464 ArrayRef<NamedAttribute> attrs) {
2466 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2471void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result,
2472 RankedTensorType resultType, Value source,
2474 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2475 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
2476 offsets, [](Value v) -> OpFoldResult {
return v; });
2477 SmallVector<OpFoldResult> sizeValues =
2478 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
2479 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
2480 strides, [](Value v) -> OpFoldResult {
return v; });
2481 build(
b,
result, resultType, source, offsetValues, sizeValues, strideValues);
2485void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2487 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2488 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2493 RankedTensorType expectedType) {
2498 return op->
emitError(
"expected rank to be smaller or equal to ")
2499 <<
"the other rank. ";
2501 return op->
emitError(
"expected type to be ")
2502 << expectedType <<
" or a rank-reduced version. (size mismatch) ";
2504 return op->
emitError(
"expected element type to be ")
2505 << expectedType.getElementType();
2507 llvm_unreachable(
"unexpected extract_slice op verification result");
2513void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result,
2514 RankedTensorType resultType, Value source,
2515 ArrayRef<OpFoldResult> sizes,
2516 ArrayRef<NamedAttribute> attrs) {
2517 Attribute zeroIdxAttr =
b.getIndexAttr(0);
2518 Attribute oneIdxAttr =
b.getIndexAttr(1);
2519 SmallVector<OpFoldResult> readStrides(sizes.size(), oneIdxAttr);
2520 SmallVector<OpFoldResult> readOffsets(sizes.size(), zeroIdxAttr);
2521 build(
b,
result, resultType, source, readOffsets, sizes, readStrides, attrs);
2525LogicalResult ExtractSliceOp::verify() {
2526 RankedTensorType sourceType = getSourceType();
2529 RankedTensorType expectedType =
2530 ExtractSliceOp::inferResultType(sourceType,
getMixedSizes());
2538 sourceType.getShape(), getStaticOffsets(), getStaticSizes(),
2539 getStaticStrides(),
true);
2541 return getOperation()->emitError(boundsResult.
errorMessage);
2546llvm::SmallBitVector ExtractSliceOp::getDroppedDims() {
2551ExtractSliceOp::rankReduceIfNeeded(OpBuilder &
b, Location loc, Value value,
2552 ArrayRef<int64_t> desiredShape) {
2553 auto sourceTensorType = llvm::dyn_cast<RankedTensorType>(value.
getType());
2554 assert(sourceTensorType &&
"not a ranked tensor type");
2555 auto sourceShape = sourceTensorType.getShape();
2556 if (sourceShape.equals(desiredShape))
2558 auto maybeRankReductionMask =
2560 if (!maybeRankReductionMask)
2564 RankedTensorType::Builder(sourceTensorType).setShape(desiredShape));
2567LogicalResult ExtractSliceOp::reifyResultShapes(
2569 reifiedReturnShapes.resize(1);
2570 reifiedReturnShapes[0].reserve(
getType().getRank());
2573 for (
const auto &size :
enumerate(mixedSizes)) {
2574 if (droppedDims.test(size.index()))
2576 reifiedReturnShapes[0].push_back(size.value());
2597class ExtractSliceOpCastFolder final :
public OpRewritePattern<ExtractSliceOp> {
2599 using OpRewritePattern<ExtractSliceOp>::OpRewritePattern;
2601 LogicalResult matchAndRewrite(ExtractSliceOp sliceOp,
2602 PatternRewriter &rewriter)
const override {
2604 if (llvm::any_of(sliceOp.getOperands(), [](Value operand) {
2605 return matchPattern(operand, matchConstantIndex());
2609 auto castOp = sliceOp.getSource().getDefiningOp<CastOp>();
2618 cast<RankedTensorType>(castOp.getSource().getType()).getShape(),
2619 sliceOp.getStaticOffsets(), sliceOp.getStaticSizes(),
2620 sliceOp.getStaticStrides());
2625 Location loc = sliceOp.getLoc();
2626 Value newResult = ExtractSliceOp::create(
2627 rewriter, loc, sliceOp.getType(), castOp.getSource(),
2628 sliceOp.getOffsets(), sliceOp.getSizes(), sliceOp.getStrides(),
2629 sliceOp.getStaticOffsets(), sliceOp.getStaticSizes(),
2630 sliceOp.getStaticStrides());
2639template <
typename IterTy,
typename ElemTy>
2640static void sliceElements(IterTy values, ArrayRef<int64_t> counts,
2641 ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes,
2642 ArrayRef<int64_t> strides,
2643 llvm::SmallVectorImpl<ElemTy> *outValues) {
2644 assert(offsets.size() == sizes.size());
2645 assert(offsets.size() == strides.size());
2646 if (offsets.empty())
2649 int64_t offset = offsets.front();
2650 int64_t size = sizes.front();
2651 int64_t stride = strides.front();
2652 if (offsets.size() == 1) {
2653 for (int64_t i = 0; i < size; ++i, offset += stride)
2654 outValues->push_back(*(values + offset));
2659 for (int64_t i = 0; i < size; ++i, offset += stride) {
2660 auto begin = values + offset * counts.front();
2661 sliceElements<IterTy, ElemTy>(begin, counts.drop_front(),
2662 offsets.drop_front(), sizes.drop_front(),
2663 strides.drop_front(), outValues);
2670class ConstantOpExtractSliceFolder final
2671 :
public OpRewritePattern<ExtractSliceOp> {
2673 using OpRewritePattern<ExtractSliceOp>::OpRewritePattern;
2675 ConstantOpExtractSliceFolder(MLIRContext *context,
2677 : OpRewritePattern<ExtractSliceOp>(context),
2678 controlFn(std::move(controlFn)) {}
2680 LogicalResult matchAndRewrite(ExtractSliceOp op,
2681 PatternRewriter &rewriter)
const override {
2682 DenseElementsAttr attr;
2691 auto sourceType = llvm::cast<ShapedType>(op.getSource().getType());
2692 auto resultType = llvm::cast<ShapedType>(op.getResult().getType());
2693 if (!sourceType.hasStaticShape() || !resultType.hasStaticShape())
2700 int64_t count = sourceType.getNumElements();
2705 auto offsets = op.getStaticOffsets();
2706 if (llvm::is_contained(offsets, ShapedType::kDynamic))
2708 auto sizes = op.getStaticSizes();
2709 if (llvm::is_contained(sizes, ShapedType::kDynamic))
2711 auto strides = op.getStaticStrides();
2712 if (llvm::is_contained(strides, ShapedType::kDynamic))
2716 SmallVector<int64_t> counts;
2717 ArrayRef<int64_t> shape = sourceType.getShape();
2718 counts.reserve(shape.size());
2719 for (int64_t v : shape) {
2721 counts.push_back(count);
2725 SmallVector<Attribute> outValues;
2726 outValues.reserve(resultType.getNumElements());
2727 sliceElements(attr.
value_begin<Attribute>(), counts, offsets, sizes,
2728 strides, &outValues);
2745 patterns.
add<ConstantOpExtractSliceFolder>(patterns.
getContext(), controlFn);
2757 return inferSliceType(op.getSourceType(), mixedSizes, op.getDroppedDims());
2764 ExtractSliceOp newOp) {
2767 replacement = tensor::CastOp::create(rewriter, op.getLoc(), op.getType(),
2773void ExtractSliceOp::getCanonicalizationPatterns(RewritePatternSet &results,
2774 MLIRContext *context) {
2776 OpWithOffsetSizesAndStridesConstantArgumentFolder<
2777 ExtractSliceOp, SliceReturnTypeCanonicalizer, SliceCanonicalizer>,
2778 ExtractSliceOpCastFolder>(context);
2784 ShapedType shapedType) {
2791 auto shape = shapedType.getShape();
2792 for (
auto it : llvm::zip(op.getMixedSizes(),
shape))
2806 auto insertOp = extractOp.getSource().getDefiningOp<InsertSliceOp>();
2809 if (insertOp && insertOp.getSource().getType() == extractOp.getType() &&
2810 insertOp.isSameAs(extractOp, isSame))
2811 return insertOp.getSource();
2816OpFoldResult ExtractSliceOp::fold(FoldAdaptor adaptor) {
2817 if (OpFoldResult reshapedSource = reshapeConstantSource(
2818 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getSource()),
2820 return reshapedSource;
2821 if (getSourceType() ==
getType() &&
2823 return this->getSource();
2827 return OpFoldResult();
2832 auto rankedTensorType = llvm::cast<RankedTensorType>(
tensor.getType());
2833 unsigned rank = rankedTensorType.getRank();
2837 return b.createOrFold<tensor::ExtractSliceOp>(loc, targetType,
tensor,
2838 offsets, sizes, strides);
2845void InsertSliceOp::getAsmResultNames(
2847 setNameFn(getResult(),
"inserted_slice");
2861 result.addAttributes(attrs);
2862 build(
b,
result, dest.
getType(), source, dest, dynamicOffsets, dynamicSizes,
2863 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
2864 b.getDenseI64ArrayAttr(staticSizes),
2865 b.getDenseI64ArrayAttr(staticStrides));
2870void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2871 Value dest, ArrayRef<Range> ranges,
2872 ArrayRef<NamedAttribute> attrs) {
2874 build(
b,
result, source, dest, offsets, sizes, strides, attrs);
2878void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2880 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2881 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
2882 offsets, [](Value v) -> OpFoldResult {
return v; });
2883 SmallVector<OpFoldResult> sizeValues =
2884 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
2885 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
2886 strides, [](Value v) -> OpFoldResult {
return v; });
2887 build(
b,
result, source, dest, offsetValues, sizeValues, strideValues);
2893 RankedTensorType srcType, RankedTensorType dstType,
2898 RankedTensorType expected =
2899 ExtractSliceOp::inferResultType(dstType, staticSizes);
2901 *expectedType = expected;
2906LogicalResult InsertSliceOp::verify() {
2908 RankedTensorType expectedType;
2911 getStaticSizes(), getStaticStrides(), &expectedType);
2918 getDestType().
getShape(), getStaticOffsets(), getStaticSizes(),
2919 getStaticStrides(),
true);
2921 return getOperation()->emitError(boundsResult.
errorMessage);
2944 auto prevInsertOp = insertOp.getDest().getDefiningOp<InsertSliceOp>();
2947 if (!prevInsertOp ||
2948 prevInsertOp.getSource().getType() != insertOp.getSource().getType() ||
2949 !prevInsertOp.isSameAs(insertOp, isSame))
2952 insertOp.getDestMutable().assign(prevInsertOp.getDest());
2964 auto extractOp = insertOp.getSource().
getDefiningOp<ExtractSliceOp>();
2967 if (!extractOp || extractOp.getSource() != insertOp.getDest() ||
2968 !extractOp.isSameAs(insertOp, isSame))
2971 return extractOp.getSource();
2974OpFoldResult InsertSliceOp::fold(FoldAdaptor) {
2975 if (getSourceType().hasStaticShape() &&
getType().hasStaticShape() &&
2976 getSourceType() ==
getType() &&
2978 return this->getSource();
2985 return OpFoldResult();
2988LogicalResult InsertSliceOp::reifyResultShapes(
2990 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
2999template <
typename InsertOpTy>
3000class InsertSliceOpConstantArgumentFolder final
3001 :
public OpRewritePattern<InsertOpTy> {
3003 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3005 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3006 PatternRewriter &rewriter)
const override {
3007 SmallVector<OpFoldResult> mixedOffsets(insertSliceOp.getMixedOffsets());
3008 SmallVector<OpFoldResult> mixedSizes(insertSliceOp.getMixedSizes());
3009 SmallVector<OpFoldResult> mixedStrides(insertSliceOp.getMixedStrides());
3018 SliceBoundsVerificationResult sliceResult =
3020 mixedOffsets, mixedSizes, mixedStrides);
3024 auto sourceType =
inferSliceType(insertSliceOp.getSourceType(), mixedSizes,
3025 insertSliceOp.getDroppedDims());
3026 Value toInsert = insertSliceOp.getSource();
3027 if (sourceType != insertSliceOp.getSourceType()) {
3028 OpBuilder::InsertionGuard g(rewriter);
3032 if (isa<InParallelOpInterface>(insertSliceOp->getParentOp()))
3034 toInsert = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3035 sourceType, toInsert);
3038 insertSliceOp, toInsert, insertSliceOp.getDest(), mixedOffsets,
3039 mixedSizes, mixedStrides);
3064template <
typename InsertOpTy>
3065struct InsertSliceOpCastFolder final :
public OpRewritePattern<InsertOpTy> {
3066 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3068 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3069 PatternRewriter &rewriter)
const override {
3070 if (llvm::any_of(insertSliceOp.getOperands(), [](Value operand) {
3071 return matchPattern(operand, matchConstantIndex());
3075 auto getSourceOfCastOp = [](Value v) -> std::optional<Value> {
3078 return std::nullopt;
3079 return castOp.getSource();
3081 std::optional<Value> sourceCastSource =
3082 getSourceOfCastOp(insertSliceOp.getSource());
3083 std::optional<Value> destCastSource =
3084 getSourceOfCastOp(insertSliceOp.getDest());
3085 if (!sourceCastSource && !destCastSource)
3089 (sourceCastSource ? *sourceCastSource : insertSliceOp.getSource());
3090 auto dst = (destCastSource ? *destCastSource : insertSliceOp.getDest());
3091 auto srcType = llvm::dyn_cast<RankedTensorType>(src.
getType());
3092 auto dstType = llvm::dyn_cast<RankedTensorType>(dst.getType());
3093 if (!srcType || !dstType)
3099 SmallVector<int64_t> staticSizes(insertSliceOp.getStaticSizes());
3101 staticSizes, srcType.getShape(),
true);
3102 if (!rankReductionMask.has_value())
3109 SmallVector<OpFoldResult> mixedSizes(insertSliceOp.getMixedSizes());
3110 int64_t rankReducedIdx = 0;
3111 for (
auto [idx, size] :
enumerate(staticSizes)) {
3112 if (!rankReductionMask.value().contains(idx) &&
3113 !srcType.isDynamicDim(rankReducedIdx)) {
3115 rewriter.
getContext(), srcType.getDimSize(rankReducedIdx));
3116 size = srcType.getDimSize(rankReducedIdx++);
3122 staticSizes, insertSliceOp.getStaticStrides()) !=
3123 SliceVerificationResult::Success)
3125 SliceBoundsVerificationResult sliceResult =
3127 mixedSizes, insertSliceOp.getMixedStrides());
3132 InsertOpTy::create(rewriter, insertSliceOp.getLoc(), src, dst,
3133 insertSliceOp.getMixedOffsets(), mixedSizes,
3134 insertSliceOp.getMixedStrides());
3137 bool isParallelInsert =
3138 std::is_same<InsertOpTy, ParallelInsertSliceOp>::value;
3139 if (!isParallelInsert && dst.getType() != insertSliceOp.getDestType()) {
3140 replacement = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3141 insertSliceOp.getDestType(),
3170template <
typename InsertOpTy>
3171struct InsertSliceOpSourceCastInserter final
3172 :
public OpRewritePattern<InsertOpTy> {
3173 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3175 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3176 PatternRewriter &rewriter)
const override {
3177 RankedTensorType srcType = insertSliceOp.getSourceType();
3178 if (srcType.getRank() != insertSliceOp.getDestType().getRank())
3180 SmallVector<int64_t> newSrcShape(srcType.getShape());
3181 for (int64_t i = 0; i < srcType.getRank(); ++i) {
3182 if (std::optional<int64_t> constInt =
3187 newSrcShape[i] = *constInt;
3193 RankedTensorType newSrcType = RankedTensorType::get(
3194 newSrcShape, srcType.getElementType(), srcType.getEncoding());
3195 if (srcType == newSrcType ||
3197 !tensor::CastOp::areCastCompatible(srcType, newSrcType))
3205 OpBuilder::InsertionGuard g(rewriter);
3209 if (isa<ParallelCombiningOpInterface>(insertSliceOp->getParentOp()))
3211 Value cast = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3212 newSrcType, insertSliceOp.getSource());
3214 insertSliceOp, cast, insertSliceOp.getDest(),
3215 insertSliceOp.getMixedOffsets(), insertSliceOp.getMixedSizes(),
3216 insertSliceOp.getMixedStrides());
3222llvm::SmallBitVector InsertSliceOp::getDroppedDims() {
3226void InsertSliceOp::getCanonicalizationPatterns(RewritePatternSet &results,
3227 MLIRContext *context) {
3228 results.
add<InsertSliceOpConstantArgumentFolder<InsertSliceOp>,
3229 InsertSliceOpCastFolder<InsertSliceOp>,
3230 InsertSliceOpSourceCastInserter<InsertSliceOp>>(context);
3237 auto rankedTensorType = llvm::cast<RankedTensorType>(dest.
getType());
3238 unsigned rank = rankedTensorType.getRank();
3242 return b.createOrFold<tensor::InsertSliceOp>(loc,
tensor, dest, offsets,
3251 setNameFn(getResult(),
"padded");
3254LogicalResult PadOp::verify() {
3255 auto sourceType = llvm::cast<RankedTensorType>(getSource().
getType());
3256 auto resultType = llvm::cast<RankedTensorType>(getResult().
getType());
3258 PadOp::inferResultType(sourceType, getStaticLow(), getStaticHigh());
3259 if (!expectedType) {
3260 return emitError(
"failed to infer expectedType from sourceType ")
3261 << sourceType <<
", specified resultType is " << resultType;
3263 if (resultType.getRank() != expectedType.getRank()) {
3265 << resultType <<
" does not match the inferred type "
3268 for (
int i = 0, e = sourceType.getRank(); i < e; ++i) {
3269 if (resultType.getDimSize(i) == expectedType.getDimSize(i))
3271 if (expectedType.isDynamicDim(i))
3274 << resultType <<
" does not match the inferred type "
3281LogicalResult PadOp::verifyRegions() {
3282 auto ®ion = getRegion();
3283 unsigned rank = llvm::cast<RankedTensorType>(getResult().
getType()).getRank();
3284 Block &block = region.front();
3286 return emitError(
"expected the block to have ") << rank <<
" arguments";
3290 if (!en.value().isIndex())
3291 return emitOpError(
"expected block argument ")
3292 << (en.index() + 1) <<
" to be an index";
3297 if (yieldOp.getValue().getType() !=
3299 return emitOpError(
"expected yield type to match shape element type");
3304RankedTensorType PadOp::inferResultType(RankedTensorType sourceType,
3305 ArrayRef<int64_t> staticLow,
3306 ArrayRef<int64_t> staticHigh,
3307 ArrayRef<int64_t> resultShape) {
3308 unsigned rank = sourceType.getRank();
3309 if (staticLow.size() != rank)
3310 return RankedTensorType();
3311 if (staticHigh.size() != rank)
3312 return RankedTensorType();
3313 if (!resultShape.empty() && resultShape.size() != rank)
3314 return RankedTensorType();
3316 SmallVector<int64_t, 4> inferredShape;
3317 for (
auto i : llvm::seq<unsigned>(0, rank)) {
3318 if (sourceType.isDynamicDim(i) || staticLow[i] == ShapedType::kDynamic ||
3319 staticHigh[i] == ShapedType::kDynamic) {
3320 inferredShape.push_back(resultShape.empty() ? ShapedType::kDynamic
3323 int64_t size = sourceType.getDimSize(i) + staticLow[i] + staticHigh[i];
3324 assert((resultShape.empty() || size == resultShape[i] ||
3325 resultShape[i] == ShapedType::kDynamic) &&
3326 "mismatch between inferred shape and result shape");
3327 inferredShape.push_back(size);
3331 Type elementType = sourceType.getElementType();
3332 return RankedTensorType::get(
3333 inferredShape, elementType,
3337void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3338 Value source, ArrayRef<int64_t> staticLow,
3340 bool nofold, ArrayRef<NamedAttribute> attrs) {
3341 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3343 resultType = inferResultType(sourceType, staticLow, staticHigh);
3344 result.addAttributes(attrs);
3345 build(
b,
result, resultType, source, low, high,
3346 b.getDenseI64ArrayAttr(staticLow),
b.getDenseI64ArrayAttr(staticHigh),
3347 nofold ?
b.getUnitAttr() : UnitAttr());
3350void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3352 ArrayRef<NamedAttribute> attrs) {
3353 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3354 unsigned rank = sourceType.getRank();
3355 SmallVector<int64_t, 4> staticVector(rank, ShapedType::kDynamic);
3356 build(
b,
result, resultType, source, staticVector, staticVector, low, high,
3360void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3361 Value source, ArrayRef<OpFoldResult> low,
3362 ArrayRef<OpFoldResult> high,
bool nofold,
3363 ArrayRef<NamedAttribute> attrs) {
3364 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3365 SmallVector<Value, 4> dynamicLow, dynamicHigh;
3366 SmallVector<int64_t, 4> staticLow, staticHigh;
3374 resultType = PadOp::inferResultType(sourceType, staticLow, staticHigh);
3376 assert(llvm::isa<RankedTensorType>(resultType));
3377 result.addAttributes(attrs);
3378 build(
b,
result, resultType, source, dynamicLow, dynamicHigh,
3379 b.getDenseI64ArrayAttr(staticLow),
b.getDenseI64ArrayAttr(staticHigh),
3380 nofold ?
b.getUnitAttr() : UnitAttr());
3383void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3384 Value source, ArrayRef<OpFoldResult> low,
3385 ArrayRef<OpFoldResult> high, Value constantPadValue,
3386 bool nofold, ArrayRef<NamedAttribute> attrs) {
3387 build(
b,
result, resultType, source, low, high, nofold, attrs);
3390 Region *region =
result.regions[0].get();
3391 int sourceRank = llvm::cast<RankedTensorType>(source.
getType()).getRank();
3392 Repeated<Type> blockArgTypes(sourceRank,
b.getIndexType());
3393 SmallVector<Location> blockArgLocs(sourceRank,
result.location);
3397 OpBuilder::InsertionGuard guard(
b);
3398 b.createBlock(region, region->
end(), blockArgTypes, blockArgLocs);
3399 tensor::YieldOp::create(
b,
result.location, constantPadValue);
3402llvm::SmallBitVector PadOp::getPaddedDims() {
3403 llvm::SmallBitVector paddedDims(getSourceType().getRank());
3404 auto extractPaddedDims = [&](ArrayRef<OpFoldResult> paddingWidths) {
3405 for (
const auto &en :
enumerate(paddingWidths))
3407 paddedDims.set(en.index());
3409 extractPaddedDims(getMixedLowPad());
3410 extractPaddedDims(getMixedHighPad());
3417struct FoldStaticZeroPadding :
public OpRewritePattern<PadOp> {
3418 using OpRewritePattern<PadOp>::OpRewritePattern;
3420 LogicalResult matchAndRewrite(PadOp padTensorOp,
3421 PatternRewriter &rewriter)
const override {
3422 if (!padTensorOp.hasZeroLowPad() || !padTensorOp.hasZeroHighPad())
3424 if (padTensorOp.getNofold())
3427 padTensorOp, padTensorOp.getResult().
getType(),
3428 padTensorOp.getSource());
3434struct FoldSourceTensorCast :
public OpRewritePattern<PadOp> {
3435 using OpRewritePattern<PadOp>::OpRewritePattern;
3437 LogicalResult matchAndRewrite(PadOp padTensorOp,
3438 PatternRewriter &rewriter)
const override {
3439 auto castOp = padTensorOp.getSource().getDefiningOp<tensor::CastOp>();
3443 auto newResultType = PadOp::inferResultType(
3444 llvm::cast<RankedTensorType>(castOp.getSource().getType()),
3445 padTensorOp.getStaticLow(), padTensorOp.getStaticHigh(),
3446 padTensorOp.getResultType().getShape());
3448 if (newResultType == padTensorOp.getResultType()) {
3450 padTensorOp.getSourceMutable().assign(castOp.getSource());
3453 auto newOp = PadOp::create(
3454 rewriter, padTensorOp->getLoc(), newResultType,
3455 padTensorOp.getSource(), padTensorOp.getStaticLow(),
3456 padTensorOp.getStaticHigh(), padTensorOp.getLow(),
3457 padTensorOp.getHigh(), padTensorOp.getNofold(),
3460 padTensorOp.getRegion().cloneInto(&newOp.getRegion(), mapper);
3463 padTensorOp, padTensorOp.getResultType(), newOp);
3471struct FoldTargetTensorCast :
public OpRewritePattern<PadOp> {
3472 using OpRewritePattern<PadOp>::OpRewritePattern;
3474 LogicalResult matchAndRewrite(PadOp padTensorOp,
3475 PatternRewriter &rewriter)
const override {
3476 if (!padTensorOp.getResult().hasOneUse())
3479 dyn_cast<tensor::CastOp>(*padTensorOp->getUsers().begin());
3483 tensorCastOp.getDest().getType()))
3486 auto replacementOp = PadOp::create(
3487 rewriter, padTensorOp.getLoc(), tensorCastOp.getDest().getType(),
3488 padTensorOp.getSource(), padTensorOp.getStaticLow(),
3489 padTensorOp.getStaticHigh(), padTensorOp.getLow(),
3490 padTensorOp.getHigh(), padTensorOp.getNofold(),
3492 replacementOp.getRegion().takeBody(padTensorOp.getRegion());
3494 rewriter.
replaceOp(padTensorOp, replacementOp.getResult());
3495 rewriter.
replaceOp(tensorCastOp, replacementOp.getResult());
3535struct FoldOrthogonalPaddings :
public OpRewritePattern<PadOp> {
3536 using OpRewritePattern<PadOp>::OpRewritePattern;
3538 LogicalResult matchAndRewrite(PadOp padOp,
3539 PatternRewriter &rewriter)
const override {
3540 auto innerSliceOp = padOp.getSource().getDefiningOp<ExtractSliceOp>();
3543 auto outerPadOp = innerSliceOp.getSource().getDefiningOp<PadOp>();
3544 if (!outerPadOp || outerPadOp.getNofold())
3546 auto outerSliceOp = outerPadOp.getSource().getDefiningOp<ExtractSliceOp>();
3551 int64_t rank = padOp.getSourceType().getRank();
3552 if (outerSliceOp.getSourceType().getRank() != rank) {
3554 "cannot fold rank-reducing chain");
3558 if (!innerSliceOp.hasUnitStride() || !outerSliceOp.hasUnitStride()) {
3560 padOp,
"cannot fold non-unit stride ExtractSliceOps");
3564 if (!padOp.hasZeroLowPad() || !outerPadOp.hasZeroLowPad()) {
3566 "cannot fold PadOps with low padding");
3570 Attribute innerAttr, outerAttr;
3571 Value innerValue = padOp.getConstantPaddingValue();
3572 Value outerValue = outerPadOp.getConstantPaddingValue();
3573 if (!innerValue || !outerValue ||
3576 innerAttr != outerAttr) {
3578 padOp,
"cannot fold PadOps with different padding values");
3582 llvm::SmallBitVector innerDims = padOp.getPaddedDims();
3583 llvm::SmallBitVector outerDims = outerPadOp.getPaddedDims();
3584 if (innerDims.anyCommon(outerDims)) {
3586 padOp,
"cannot fold PadOps with common padding dimensions");
3594 SmallVector<OpFoldResult> newOffsets(rank, rewriter.
getIndexAttr(0));
3596 OpFoldResult innerOffset = innerSliceOp.getMixedOffsets()[en.index()];
3597 OpFoldResult outerOffset = outerSliceOp.getMixedOffsets()[en.index()];
3598 if (!innerDims.test(en.index()) &&
3600 en.value() = outerOffset;
3603 if (!outerDims.test(en.index()) &&
3605 en.value() = innerOffset;
3609 padOp,
"cannot find zero-offset and zero-padding pair");
3617 SmallVector<OpFoldResult> newSizes = innerSliceOp.getMixedSizes();
3619 if (!outerDims.test(en.index()))
3621 OpFoldResult sliceSize = innerSliceOp.getMixedSizes()[en.index()];
3622 int64_t sourceSize = innerSliceOp.getSourceType().getShape()[en.index()];
3623 assert(ShapedType::isStatic(sourceSize) &&
3624 "expected padded dimension to have a static size");
3627 padOp,
"cannot fold since the inner ExtractSliceOp size does not "
3628 "match the size of the outer padding");
3630 en.value() = outerSliceOp.getMixedSizes()[en.index()];
3634 SmallVector<OpFoldResult> newHighPad(rank, rewriter.
getIndexAttr(0));
3636 if (innerDims.test(en.index()))
3637 newHighPad[en.index()] = padOp.getMixedHighPad()[en.index()];
3638 if (outerDims.test(en.index()))
3639 newHighPad[en.index()] = outerPadOp.getMixedHighPad()[en.index()];
3644 auto newSliceOp = ExtractSliceOp::create(
3645 rewriter, padOp.getLoc(), outerSliceOp.getSource(), newOffsets,
3646 newSizes, innerSliceOp.getMixedStrides());
3647 auto newPadOp = PadOp::create(
3648 rewriter, padOp.getLoc(), padOp.getResultType(), newSliceOp.getResult(),
3649 padOp.getMixedLowPad(), newHighPad, padOp.getNofold(),
3652 newPadOp.getRegion().begin());
3653 rewriter.
replaceOp(padOp, newPadOp.getResult());
3658struct FoldStaticPadding :
public OpRewritePattern<PadOp> {
3659 using OpRewritePattern<PadOp>::OpRewritePattern;
3661 LogicalResult matchAndRewrite(PadOp padTensorOp,
3662 PatternRewriter &rewriter)
const override {
3663 Value input = padTensorOp.getSource();
3664 if (!llvm::isa<RankedTensorType>(input.
getType()))
3666 auto inputDims = llvm::cast<RankedTensorType>(input.
getType()).getShape();
3667 auto inputRank = inputDims.size();
3669 auto oldResultType =
3670 dyn_cast<RankedTensorType>(padTensorOp.getResult().getType());
3674 auto outputDims = oldResultType.getShape();
3677 SmallVector<int64_t> constOperandsLow;
3678 SmallVector<Value> newLows;
3679 for (
auto operand : padTensorOp.getLow()) {
3682 constOperandsLow.push_back(ShapedType::kDynamic);
3683 newLows.push_back(operand);
3686 constOperandsLow.push_back(intOp.getExtValue());
3688 SmallVector<int64_t> constOperandsHigh;
3689 SmallVector<Value> newHighs;
3690 for (
auto operand : padTensorOp.getHigh()) {
3693 constOperandsHigh.push_back(ShapedType::kDynamic);
3694 newHighs.push_back(operand);
3697 constOperandsHigh.push_back(intOp.getExtValue());
3700 SmallVector<int64_t> constLow(padTensorOp.getStaticLow());
3701 SmallVector<int64_t> constHigh(padTensorOp.getStaticHigh());
3704 if (inputDims.size() != outputDims.size() ||
3705 inputDims.size() != constLow.size() ||
3706 inputDims.size() != constHigh.size())
3711 for (
size_t i = 0; i < inputRank; i++) {
3712 if (constLow[i] == ShapedType::kDynamic)
3713 constLow[i] = constOperandsLow[lowCount++];
3714 if (constHigh[i] == ShapedType::kDynamic)
3715 constHigh[i] = constOperandsHigh[highCount++];
3718 auto staticLow = ArrayRef<int64_t>(constLow);
3719 auto staticHigh = ArrayRef<int64_t>(constHigh);
3722 SmallVector<int64_t> newOutDims;
3723 for (
size_t i = 0; i < inputRank; i++) {
3724 if (outputDims[i] == ShapedType::kDynamic) {
3725 newOutDims.push_back(
3726 (staticLow[i] == ShapedType::kDynamic ||
3727 staticHigh[i] == ShapedType::kDynamic ||
3728 inputDims[i] == ShapedType::kDynamic
3729 ? ShapedType::kDynamic
3730 : inputDims[i] + staticLow[i] + staticHigh[i]));
3732 newOutDims.push_back(outputDims[i]);
3736 if (SmallVector<int64_t>(outputDims) == newOutDims ||
3737 llvm::all_of(newOutDims,
3738 [&](int64_t x) {
return x == ShapedType::kDynamic; }))
3741 Type elementType = padTensorOp.getType().getElementType();
3742 auto newResultType = RankedTensorType::get(
3743 newOutDims, elementType,
3746 auto newOp = PadOp::create(
3747 rewriter, padTensorOp->getLoc(), newResultType, input, staticLow,
3748 staticHigh, newLows, newHighs, padTensorOp.getNofold(),
3752 padTensorOp.getRegion().cloneInto(&newOp.getRegion(), mapper);
3780struct FoldConsecutiveConstantPadding :
public OpRewritePattern<tensor::PadOp> {
3781 using OpRewritePattern<tensor::PadOp>::OpRewritePattern;
3783 LogicalResult matchAndRewrite(tensor::PadOp padOp,
3784 PatternRewriter &rewriter)
const override {
3785 if (padOp.getNofold()) {
3789 auto producerPad = padOp.getSource().getDefiningOp<tensor::PadOp>();
3790 if (!producerPad || producerPad.getNofold()) {
3792 padOp,
"producer is not a foldable tensor.pad op");
3796 Value consumerPadValue = padOp.getConstantPaddingValue();
3797 Value producerPadValue = producerPad.getConstantPaddingValue();
3798 if (!consumerPadValue || !producerPadValue ||
3799 consumerPadValue != producerPadValue) {
3802 "cannot fold PadOps with different or non-constant padding values");
3805 Location loc = padOp.getLoc();
3810 auto addPaddings = [&](ArrayRef<OpFoldResult> consumerPaddings,
3811 ArrayRef<OpFoldResult> producerPaddings) {
3812 SmallVector<OpFoldResult> sumPaddings;
3813 for (
auto [consumerIndex, producerIndex] :
3814 llvm::zip_equal(consumerPaddings, producerPaddings)) {
3816 rewriter, loc, d0 + d1, {consumerIndex, producerIndex}));
3821 SmallVector<OpFoldResult> newHighPad =
3822 addPaddings(padOp.getMixedHighPad(), producerPad.getMixedHighPad());
3823 SmallVector<OpFoldResult> newLowPad =
3824 addPaddings(padOp.getMixedLowPad(), producerPad.getMixedLowPad());
3826 auto newPadOp = tensor::PadOp::create(
3827 rewriter, padOp.getLoc(), padOp.getResultType(),
3828 producerPad.getSource(), newLowPad, newHighPad, padOp.getNofold(),
3831 newPadOp.getRegion().begin());
3832 rewriter.
replaceOp(padOp, newPadOp.getResult());
3840PadOp::reifyResultShapes(OpBuilder &
b,
3842 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
3843 SmallVector<OpFoldResult> lp = getMixedLowPad();
3844 SmallVector<OpFoldResult> hp = getMixedHighPad();
3845 for (int64_t i = 0; i < getResultType().getRank(); ++i) {
3846 if (!
getType().isDynamicDim(i)) {
3847 reifiedReturnShapes[0][i] =
b.getIndexAttr(
getType().getDimSize(i));
3850 Location loc = getLoc();
3851 Value dim =
b.createOrFold<tensor::DimOp>(
3854 AffineExpr d0, d1, d2;
3857 b, loc, {d0 + d1 + d2}, {dim, lp[i], hp[i]});
3862void PadOp::getCanonicalizationPatterns(RewritePatternSet &results,
3863 MLIRContext *context) {
3864 results.
add<FoldStaticZeroPadding, FoldSourceTensorCast, FoldTargetTensorCast,
3865 FoldOrthogonalPaddings, FoldStaticPadding,
3866 FoldConsecutiveConstantPadding>(context);
3878Value PadOp::getConstantPaddingValue() {
3879 auto yieldOp = dyn_cast<YieldOp>(getRegion().front().getTerminator());
3882 Value padValue = yieldOp.getValue();
3893OpFoldResult PadOp::fold(FoldAdaptor) {
3894 if (getResultType().hasStaticShape() && getResultType() == getSourceType() &&
3904OpResult ParallelInsertSliceOp::getTiedOpResult() {
3905 InParallelOpInterface parallelCombiningParent = getParallelCombiningParent();
3906 for (
const auto &it :
3907 llvm::enumerate(parallelCombiningParent.getYieldingOps())) {
3908 Operation &nextOp = it.value();
3909 if (&nextOp == getOperation())
3910 return parallelCombiningParent.getParentResult(it.index());
3912 llvm_unreachable(
"ParallelInsertSliceOp no tied OpResult found");
3916void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3917 Value source, Value dest,
3918 ArrayRef<OpFoldResult> offsets,
3919 ArrayRef<OpFoldResult> sizes,
3920 ArrayRef<OpFoldResult> strides,
3921 ArrayRef<NamedAttribute> attrs) {
3922 SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;
3923 SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;
3927 result.addAttributes(attrs);
3928 build(
b,
result, {}, source, dest, dynamicOffsets, dynamicSizes,
3929 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
3930 b.getDenseI64ArrayAttr(staticSizes),
3931 b.getDenseI64ArrayAttr(staticStrides));
3936void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3937 Value source, Value dest,
3938 ArrayRef<Range> ranges,
3939 ArrayRef<NamedAttribute> attrs) {
3941 build(
b,
result, source, dest, offsets, sizes, strides, attrs);
3945void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3946 Value source, Value dest,
ValueRange offsets,
3948 ArrayRef<NamedAttribute> attrs) {
3949 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
3950 offsets, [](Value v) -> OpFoldResult {
return v; });
3951 SmallVector<OpFoldResult> sizeValues =
3952 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
3953 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
3954 strides, [](Value v) -> OpFoldResult {
return v; });
3955 build(
b,
result, source, dest, offsetValues, sizeValues, strideValues);
3960void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
3961 Value dest, ArrayRef<OpFoldResult> sizes,
3962 ArrayRef<NamedAttribute> attrs) {
3963 Attribute zeroIdxAttr =
b.getIndexAttr(0);
3964 Attribute oneIdxAttr =
b.getIndexAttr(1);
3965 SmallVector<OpFoldResult> writeStrides(sizes.size(), oneIdxAttr);
3966 SmallVector<OpFoldResult> writeOffsets(sizes.size(), zeroIdxAttr);
3967 build(
b,
result, source, dest, writeOffsets, sizes, writeStrides, attrs);
3970LogicalResult ParallelInsertSliceOp::verify() {
3971 if (!isa<InParallelOpInterface>(getOperation()->getParentOp()))
3972 return this->
emitError(
"expected InParallelOpInterface parent, got:")
3973 << *(getOperation()->getParentOp());
3976 RankedTensorType expectedType;
3979 getStaticSizes(), getStaticStrides(), &expectedType);
3986 getDestType().
getShape(), getStaticOffsets(), getStaticSizes(),
3987 getStaticStrides(),
true);
3989 return getOperation()->emitError(boundsResult.
errorMessage);
3994void ParallelInsertSliceOp::getCanonicalizationPatterns(
3995 RewritePatternSet &results, MLIRContext *context) {
3996 results.
add<InsertSliceOpConstantArgumentFolder<ParallelInsertSliceOp>,
3997 InsertSliceOpCastFolder<ParallelInsertSliceOp>,
3998 InsertSliceOpSourceCastInserter<ParallelInsertSliceOp>>(context);
4001llvm::SmallBitVector ParallelInsertSliceOp::getDroppedDims() {
4006MutableOperandRange ParallelInsertSliceOp::getUpdatedDestinations() {
4007 return getDestMutable();
4010Operation *ParallelInsertSliceOp::getIteratingParent() {
4012 if (
auto combiningOp =
4013 dyn_cast<InParallelOpInterface>(getOperation()->getParentOp()))
4014 return combiningOp->getParentOp();
4022void ScatterOp::getAsmResultNames(
4024 setNameFn(getResult(),
"scatter");
4027LogicalResult ScatterOp::verify() {
4028 int64_t destRank = getDestType().getRank();
4029 ArrayRef<int64_t> scatterDims = getScatterDims();
4031 getIndicesType().
getShape(), destRank,
4032 "scatter",
"dest")))
4036 return emitOpError(
"requires 'unique' attribute to be set");
4043 RankedTensorType expectedSourceType = GatherOp::inferResultType(
4044 getDestType(), getIndicesType(), scatterDims,
false);
4045 RankedTensorType expectedRankReducedSourceType = GatherOp::inferResultType(
4046 getDestType(), getIndicesType(), scatterDims,
true);
4047 if (getSourceType() != expectedSourceType &&
4048 getSourceType() != expectedRankReducedSourceType) {
4049 return emitOpError(
"source type "
4052 << expectedSourceType <<
" or its rank-reduced variant "
4053 << expectedRankReducedSourceType <<
" (got: " << getSourceType()
4064void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4065 Type aggregateType,
ValueRange dynamicSizes) {
4066 build(builder,
result, aggregateType, element, dynamicSizes);
4069void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4070 ArrayRef<int64_t> staticShape,
ValueRange dynamicSizes) {
4071 auto aggregateType = RankedTensorType::get(staticShape, element.
getType());
4072 build(builder,
result, aggregateType, element, dynamicSizes);
4075void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4076 ArrayRef<OpFoldResult> sizes) {
4077 SmallVector<int64_t> staticShape;
4078 SmallVector<Value> dynamicSizes;
4080 build(builder,
result, element, staticShape, dynamicSizes);
4083void SplatOp::getAsmResultNames(
4085 setNameFn(getResult(),
"splat");
4088LogicalResult SplatOp::verify() {
4094SplatOp::reifyResultShapes(OpBuilder &builder,
4096 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
4098 for (int64_t i = 0; i <
getType().getRank(); ++i) {
4099 if (
getType().isDynamicDim(i)) {
4108OpFoldResult SplatOp::fold(FoldAdaptor adaptor) {
4109 auto constOperand = adaptor.getInput();
4110 if (!isa_and_nonnull<IntegerAttr, FloatAttr>(constOperand))
4114 if (!
getType().hasStaticShape())
4129 if (isa<InsertSliceOp>(op.getOperation()) ||
4130 isa<LoopLikeOpInterface>(op.getOperation()))
4163 isa<linalg::RelayoutOpInterface>(*op))
4171 auto newOp =
clone(rewriter, op, newResultTypes, newOperands);
4174 replacements.reserve(newOp->getNumResults());
4175 for (
auto [oldResult, newResult] :
4176 llvm::zip(op->getResults(), newOp->getResults())) {
4177 if (newResult.getType() != oldResult.getType()) {
4178 replacements.push_back(tensor::CastOp::create(
4179 rewriter, op->getLoc(), oldResult.
getType(), newResult));
4181 replacements.push_back(newResult);
4194void TensorDialect::getCanonicalizationPatterns(
4195 RewritePatternSet &results)
const {
4203#define GET_OP_CLASSES
4204#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.
static SmallVector< Value > slice(OpBuilder &b, Location loc, Value value, VectorType nativeType)
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.
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.
LogicalResult verifyReassociationIndicesNotEmpty(ReshapeOpTy op)
Verify that none of the reassociation groups is empty.
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.