35#include "llvm/ADT/DenseSet.h"
36#include "llvm/ADT/Repeated.h"
37#include "llvm/ADT/STLExtras.h"
38#include "llvm/ADT/SmallBitVector.h"
39#include "llvm/ADT/SmallVectorExtras.h"
40#include "llvm/ADT/StringRef.h"
41#include "llvm/Support/Casting.h"
42#include "llvm/Support/MathExtras.h"
55 auto verifiable = dyn_cast_or_null<VerifiableTensorEncoding>(encoding);
63 return succeeded(verifiable.verifyEncoding(
shape, elementType,
emit))
73 if (
auto op = arith::ConstantOp::materialize(builder, value, type, loc))
75 if (complex::ConstantOp::isBuildableWith(value, type))
76 return complex::ConstantOp::create(builder, loc, type,
77 llvm::cast<ArrayAttr>(value));
83 auto tensorType = llvm::cast<RankedTensorType>(value.
getType());
84 if (tensorType.isDynamicDim(dim))
85 return builder.
createOrFold<tensor::DimOp>(loc, value, dim);
92 auto tensorType = llvm::cast<RankedTensorType>(value.
getType());
94 for (
int64_t i = 0; i < tensorType.getRank(); ++i)
101 auto tensorType = llvm::dyn_cast<TensorType>(opResult.
getType());
102 assert(tensorType &&
"expected tensor type");
106 auto destOp = opResult.
getDefiningOp<DestinationStyleOpInterface>();
108 return destOp.getTiedOpOperand(opResult)->get();
116 if (!tensorType.hasStaticShape()) {
124 for (
int64_t sz : tensorType.getShape())
125 mixedSizes.push_back(
b.getIndexAttr(sz));
130 if (
auto rankedTensorType = dyn_cast<RankedTensorType>(tensorType))
131 encoding = rankedTensorType.getEncoding();
132 Value emptyTensor = tensor::EmptyOp::create(
133 b, loc, mixedSizes, tensorType.getElementType(), encoding);
141 if (llvm::isa<TensorType>(opResult.getType())) {
143 if (failed(destination))
145 result.push_back(*destination);
152 if (
auto rtp1 = llvm::dyn_cast<RankedTensorType>(tp1)) {
153 if (
auto rtp2 = llvm::dyn_cast<RankedTensorType>(tp2))
154 return rtp1.getShape() == rtp2.getShape() &&
155 rtp1.getElementType() == rtp2.getElementType();
165 llvm::SmallBitVector droppedDims(mixedSizes.size());
166 int64_t shapePos = reducedShape.size() - 1;
168 for (
const auto &size : enumerate(llvm::reverse(mixedSizes))) {
169 size_t idx = mixedSizes.size() - size.index() - 1;
171 bool isStaticUnitSize =
172 isa<Attribute>(size.value()) &&
173 llvm::cast<IntegerAttr>(cast<Attribute>(size.value())).getInt() == 1;
178 assert(isStaticUnitSize &&
"expected unit dim");
179 droppedDims.set(idx);
184 if (!isStaticUnitSize) {
190 if (reducedShape[shapePos] == 1) {
196 droppedDims.set(idx);
199 assert(shapePos < 0 &&
"dimension mismatch");
206static RankedTensorType
210 assert(type.getNumDynamicDims() == dynamicSizes.size() &&
211 "incorrect number of dynamic sizes");
215 for (
int64_t i = 0, e = type.getRank(); i < e; ++i) {
216 if (type.isDynamicDim(i)) {
217 Value dynamicSize = dynamicSizes[ctr++];
219 if (cst.has_value()) {
221 if (cst.value() < 0) {
222 foldedDynamicSizes.push_back(dynamicSize);
225 staticShape[i] = *cst;
227 foldedDynamicSizes.push_back(dynamicSize);
232 return RankedTensorType::get(staticShape, type.getElementType(),
241 if (inputs.size() != 1 || outputs.size() != 1)
243 Type a = inputs.front(),
b = outputs.front();
244 auto aT = dyn_cast<TensorType>(a);
245 auto bT = dyn_cast<TensorType>(
b);
249 if (aT.getElementTypeBitWidth() != bT.getElementTypeBitWidth())
260 using OpRewritePattern<BitcastOp>::OpRewritePattern;
262 LogicalResult matchAndRewrite(BitcastOp tensorBitcast,
263 PatternRewriter &rewriter)
const final {
264 auto tensorBitcastOperand =
265 tensorBitcast.getOperand().getDefiningOp<BitcastOp>();
266 if (!tensorBitcastOperand)
269 auto resultType = cast<TensorType>(tensorBitcast.getType());
270 rewriter.replaceOpWithNewOp<BitcastOp>(tensorBitcast, resultType,
271 tensorBitcastOperand.getOperand());
280 results.
add<ChainedTensorBitcast>(context);
288 setNameFn(getResult(),
"cast");
294 auto sourceType = llvm::dyn_cast<RankedTensorType>(source);
295 auto targetType = llvm::dyn_cast<RankedTensorType>(
target);
298 if (!sourceType || !targetType)
302 if (sourceType.getElementType() != targetType.getElementType())
306 if (sourceType.getRank() != targetType.getRank())
310 if (sourceType.getEncoding() != targetType.getEncoding())
314 for (
auto t : llvm::zip(sourceType.getShape(), targetType.getShape())) {
315 if (ShapedType::isStatic(std::get<0>(t)) &&
316 ShapedType::isDynamic(std::get<1>(t)))
352 castOp.getSource().getType());
385 if (llvm::isa<BlockArgument>(opOperand.get()))
387 auto castOp = opOperand.get().getDefiningOp<tensor::CastOp>();
388 return castOp && canFoldIntoConsumerOp(castOp);
395 newOperands.reserve(op->getNumOperands());
401 for (
OpOperand &opOperand : op->getOpOperands()) {
402 auto tensorCastOp = opOperand.get().getDefiningOp<tensor::CastOp>();
404 newOperands.push_back(fold ? tensorCastOp.getOperand() : opOperand.get());
405 if (op.isDpsInit(&opOperand) &&
406 !llvm::isa<MemRefType>(newOperands.back().getType()))
407 newResTy[dpsInitIdx++] = newOperands.back().getType();
417 auto castOp = operand.get().getDefiningOp<tensor::CastOp>();
419 operand.set(castOp.getOperand());
427 if (inputs.size() != 1 || outputs.size() != 1)
429 Type a = inputs.front(),
b = outputs.front();
430 auto aT = llvm::dyn_cast<TensorType>(a);
431 auto bT = llvm::dyn_cast<TensorType>(
b);
435 if (aT.getElementType() != bT.getElementType())
452 if (rank != two.getRank())
457 for (
int64_t i = 0; i < rank; ++i) {
458 if (one.isDynamicDim(i)) {
459 join.push_back(two.getDimSize(i));
462 if (two.isDynamicDim(i)) {
463 join.push_back(one.getDimSize(i));
466 if (one.getDimSize(i) != two.getDimSize(i))
468 join.push_back(one.getDimSize(i));
478 using OpRewritePattern<CastOp>::OpRewritePattern;
480 LogicalResult matchAndRewrite(CastOp tensorCast,
481 PatternRewriter &rewriter)
const final {
482 auto tensorCastOperand = tensorCast.getOperand().getDefiningOp<CastOp>();
484 if (!tensorCastOperand)
488 llvm::cast<TensorType>(tensorCastOperand.getOperand().getType());
489 auto intermediateType = llvm::cast<TensorType>(tensorCastOperand.getType());
490 auto resultType = llvm::cast<TensorType>(tensorCast.getType());
504 auto newJoin =
joinShapes(sourceType, resultType);
505 if (firstJoin != newJoin)
508 rewriter.replaceOpWithNewOp<CastOp>(tensorCast, resultType,
509 tensorCastOperand.getOperand());
527 using OpRewritePattern<CastOp>::OpRewritePattern;
529 LogicalResult matchAndRewrite(CastOp tensorCast,
530 PatternRewriter &rewriter)
const final {
531 auto extractOperand =
532 tensorCast.getOperand().getDefiningOp<ExtractSliceOp>();
535 auto rankedResultType =
536 llvm::dyn_cast<RankedTensorType>(tensorCast.getType());
537 if (!rankedResultType)
541 rankedResultType.getShape() ==
542 llvm::cast<RankedTensorType>(tensorCast.getSource().getType())
546 SmallVector<OpFoldResult, 4> sizes = extractOperand.getMixedSizes();
548 extractOperand.getStaticSizes(), extractOperand.getType().getShape());
550 for (
size_t i = 0, e = sizes.size(); i < e; i++) {
551 if (dimMask && dimMask->count(i))
553 int64_t dim = rankedResultType.getShape()[dimIndex++];
554 if (ShapedType::isDynamic(dim))
556 sizes[i] = rewriter.getIndexAttr(dim);
559 rewriter.replaceOpWithNewOp<ExtractSliceOp>(
560 tensorCast, rankedResultType, extractOperand.getSource(),
561 extractOperand.getMixedOffsets(), sizes,
562 extractOperand.getMixedStrides());
571 results.
add<ChainedTensorCast, TensorCastExtractSlice>(context);
578RankedTensorType ConcatOp::inferResultType(
int64_t dim,
TypeRange inputTypes) {
579 assert(!inputTypes.empty() &&
"cannot concatenate 0 tensors");
581 llvm::map_to_vector<4>(inputTypes, llvm::CastTo<RankedTensorType>);
582 int64_t concatRank = tensorTypes[0].getRank();
585 assert(dim >= 0 && dim < concatRank &&
"Invalid concatenation dim");
588 for (
int64_t i = 0, e = concatRank; i < e; ++i) {
592 for (
auto tensorType : tensorTypes)
597 for (
auto tensorType : tensorTypes)
600 sizes[dim] = concatSize.asInteger();
601 return RankedTensorType::get(sizes, tensorTypes[0].
getElementType());
606 FailureOr<RankedTensorType> resultType =
607 inferResultType(dim, inputs.
getTypes());
608 assert(succeeded(resultType) &&
"failed to infer concatenation result type");
609 build(builder,
result, *resultType, dim, inputs);
612LogicalResult ConcatOp::verify() {
613 if (getInputs().size() < 1)
617 for (
auto input : getInputs())
618 inputTypes.push_back(cast<RankedTensorType>(input.getType()));
620 RankedTensorType resultType = getResultType();
621 int64_t resultRank = getRank();
622 if (llvm::any_of(inputTypes, [resultRank](RankedTensorType type) {
623 return type.getRank() != resultRank;
625 return emitOpError(
"rank of concatenated inputs must match result rank");
627 Type resultElementType = resultType.getElementType();
628 if (llvm::any_of(inputTypes, [&](RankedTensorType type) {
629 return type.getElementType() != resultElementType;
631 return emitOpError(
"inputs and result element type must match");
634 if (dim >= resultRank)
635 return emitOpError(
"concatenation dim must be less than the tensor rank");
638 for (
int64_t i = 0, e = resultRank; i < e; ++i) {
642 for (
auto tensorType : inputTypes) {
643 FailureOr<SaturatedInteger> maybeSize =
646 return emitOpError(
"static concatenation size mismatch along ")
647 <<
"non-concatenated dimension " << i;
653 for (
auto tensorType : inputTypes)
656 sizes[dim] = concatSize.asInteger();
657 auto inferredResultType =
660 for (
auto [inferredSize, actualSize] :
661 llvm::zip_equal(inferredResultType.getShape(), resultType.getShape())) {
662 bool hasDynamic = ShapedType::isDynamic(inferredSize) ||
663 ShapedType::isDynamic(actualSize);
664 if (!hasDynamic && inferredSize != actualSize)
666 << resultType <<
"does not match inferred shape "
667 << inferredResultType <<
" static sizes";
673FailureOr<SmallVector<Value>> ConcatOp::decomposeOperation(
OpBuilder &builder) {
674 size_t numInputs = getInputs().size();
675 uint64_t concatDim = getDim();
678 inputShapes.reserve(numInputs);
680 concatOffsets.reserve(numInputs);
687 for (
auto [
index, input] : llvm::enumerate(getInputs())) {
691 outputShape = inputShape;
692 concatOffsets.push_back(zero);
694 concatOffsets.push_back(outputShape[concatDim]);
696 builder, loc, addExpr,
697 {outputShape[concatDim], inputShape[concatDim]});
699 inputShapes.emplace_back(std::move(inputShape));
709 for (
auto [
index, input] : llvm::enumerate(getInputs())) {
710 offsets[concatDim] = concatOffsets[
index];
711 auto insertSlice = tensor::InsertSliceOp::create(
722ConcatOp::reifyResultShapes(
OpBuilder &builder,
726 RankedTensorType inferredResultType = inferResultType(dim, inputs.
getTypes());
728 Value init = inputs[0];
736 for (
int64_t i = 0; i < rank; ++i) {
739 if (!
getType().isDynamicDim(i)) {
741 }
else if (!inferredResultType.isDynamicDim(i)) {
744 builder.
getIndexAttr(inferredResultType.getDimSize(i)));
746 reifiedReturnShapes[0][i] =
747 tensor::DimOp::create(builder, init.
getLoc(), init, i).getResult();
751 if (
getType().isDynamicDim(dim)) {
756 for (
auto [idx, input] : llvm::enumerate(inputs.drop_front())) {
759 builder.
createOrFold<tensor::DimOp>(input.getLoc(), input, dim));
767 reifiedReturnShapes[0][dim] =
773void ConcatOp::getAsmResultNames(
775 setNameFn(getResult(),
"concat");
780 if (inputs.size() == 1 && inputs[0].
getType() == getResultType())
788 using OpRewritePattern<ConcatOp>::OpRewritePattern;
790 LogicalResult matchAndRewrite(ConcatOp concatOp,
791 PatternRewriter &rewriter)
const override {
792 if (concatOp.getInputs().size() != 1)
795 concatOp.getInputs()[0]);
820 using OpRewritePattern<ConcatOp>::OpRewritePattern;
822 LogicalResult matchAndRewrite(ConcatOp concatOp,
823 PatternRewriter &rewriter)
const override {
824 int64_t dim = concatOp.getDim();
825 RankedTensorType inferredResultType =
826 ConcatOp::inferResultType(dim, concatOp->getOperandTypes());
829 LogicalResult matched = failure();
832 SmallVector<int64_t> inferredOperandShape(inferredResultType.getShape());
833 for (
auto [operandIdx, operandType] :
834 llvm::enumerate(concatOp->getOperandTypes())) {
836 inferredOperandShape[dim] =
837 cast<RankedTensorType>(operandType).getDimSize(dim);
838 auto inferredOperandType = RankedTensorType::get(
839 inferredOperandShape, inferredResultType.getElementType());
847 CastOp::create(rewriter, concatOp->getLoc(), inferredOperandType,
848 concatOp.getOperand(operandIdx));
850 concatOp->setOperand(operandIdx, castOp->getResult(0));
874 using OpRewritePattern<ConcatOp>::OpRewritePattern;
876 LogicalResult matchAndRewrite(ConcatOp concatOp,
877 PatternRewriter &rewriter)
const override {
878 int64_t dim = concatOp.getDim();
879 RankedTensorType inferredResultType =
880 ConcatOp::inferResultType(dim, concatOp->getOperandTypes());
884 concatOp.getResultType())) {
889 ConcatOp::create(rewriter, concatOp->getLoc(), inferredResultType, dim,
890 concatOp->getOperands());
902 .
add<SingleInputConcatOp, InferConcatOperandTypes, InferConcatResultType>(
911 setNameFn(getResult(),
"dim");
916 auto loc =
result.location;
918 build(builder,
result, source, indexValue);
921std::optional<int64_t> DimOp::getConstantIndex() {
930 auto rankedSourceType = dyn_cast<RankedTensorType>(getSource().
getType());
931 if (!rankedSourceType)
934 if (rankedSourceType.getRank() <= constantIndex)
942 setResultRange(getResult(),
948 std::optional<int64_t>
index = getConstantIndex();
953 auto tensorType = llvm::dyn_cast<RankedTensorType>(getSource().
getType());
960 if (indexVal < 0 || indexVal >= tensorType.getRank())
964 if (!tensorType.isDynamicDim(indexVal)) {
966 return builder.
getIndexAttr(tensorType.getShape()[indexVal]);
969 Operation *definingOp = getSource().getDefiningOp();
972 if (
auto fromElements = dyn_cast_or_null<tensor::GenerateOp>(definingOp)) {
974 llvm::cast<RankedTensorType>(fromElements.getResult().getType());
977 assert(ShapedType::isDynamic(resultType.getShape()[indexVal]));
980 auto dynExtents = fromElements.getDynamicExtents().begin();
981 for (
auto dim : resultType.getShape().take_front(indexVal))
982 if (ShapedType::isDynamic(dim))
985 return Value{*dynExtents};
989 if (
auto sliceOp = dyn_cast_or_null<tensor::ExtractSliceOp>(definingOp)) {
992 if (sliceOp.getType().getRank() == sliceOp.getSourceType().getRank() &&
993 sliceOp.isDynamicSize(indexVal)) {
994 return {sliceOp.getDynamicSize(indexVal)};
1008 using OpRewritePattern<DimOp>::OpRewritePattern;
1010 LogicalResult matchAndRewrite(DimOp dimOp,
1011 PatternRewriter &rewriter)
const override {
1012 auto castOp = dimOp.getSource().getDefiningOp<CastOp>();
1015 Value newSource = castOp.getOperand();
1024 using OpRewritePattern<DimOp>::OpRewritePattern;
1026 LogicalResult matchAndRewrite(DimOp dimOp,
1027 PatternRewriter &rewriter)
const override {
1028 auto source = dimOp.getSource();
1029 auto destOp = source.getDefiningOp<DestinationStyleOpInterface>();
1033 auto resultIndex = cast<OpResult>(source).getResultNumber();
1034 auto *initOperand = destOp.getDpsInitOperand(resultIndex);
1037 dimOp, [&]() { dimOp.getSourceMutable().assign(initOperand->get()); });
1045 using OpRewritePattern<DimOp>::OpRewritePattern;
1047 LogicalResult matchAndRewrite(DimOp dim,
1048 PatternRewriter &rewriter)
const override {
1049 auto reshape = dim.getSource().getDefiningOp<ReshapeOp>();
1057 Location loc = dim.getLoc();
1059 ExtractOp::create(rewriter, loc, reshape.getShape(), dim.getIndex());
1060 if (extract.
getType() != dim.getType())
1062 arith::IndexCastOp::create(rewriter, loc, dim.getType(), extract);
1071 results.
add<DimOfCastOp, DimOfDestStyleOp, DimOfReshapeOp>(context);
1081 assert(none_of(staticShape, ShapedType::isDynamic) &&
1082 "expected only static sizes");
1086void EmptyOp::build(OpBuilder &builder, OperationState &
result,
1087 ArrayRef<int64_t> staticShape, Type elementType,
1088 ValueRange dynamicSizes, Attribute encoding) {
1089 auto tensorType = RankedTensorType::get(staticShape, elementType, encoding);
1090 build(builder,
result, tensorType, dynamicSizes);
1093void EmptyOp::build(OpBuilder &builder, OperationState &
result,
1094 ArrayRef<OpFoldResult> sizes, Type elementType,
1095 Attribute encoding) {
1096 SmallVector<int64_t> staticShape;
1097 SmallVector<Value> dynamicSizes;
1099 build(builder,
result, staticShape, elementType, dynamicSizes, encoding);
1102LogicalResult EmptyOp::verify() {
1108EmptyOp::reifyResultShapes(OpBuilder &builder,
1110 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
1112 for (int64_t i = 0; i <
getType().getRank(); ++i) {
1113 if (
getType().isDynamicDim(i)) {
1122Value EmptyOp::getDynamicSize(
unsigned idx) {
1123 assert(
getType().isDynamicDim(idx) &&
"expected dynamic dim");
1125 for (int64_t i = 0; i < static_cast<int64_t>(idx); ++i)
1126 if (
getType().isDynamicDim(i))
1131SmallVector<OpFoldResult> EmptyOp::getMixedSizes() {
1132 SmallVector<OpFoldResult>
result;
1136 if (ShapedType::isDynamic(dim)) {
1139 result.push_back(
b.getIndexAttr(dim));
1157struct ReplaceEmptyTensorStaticShapeDims : OpRewritePattern<EmptyOp> {
1158 using OpRewritePattern<EmptyOp>::OpRewritePattern;
1160 LogicalResult matchAndRewrite(EmptyOp op,
1161 PatternRewriter &rewriter)
const override {
1162 SmallVector<Value> foldedDynamicSizes;
1164 op.getType(), op.getDynamicSizes(), foldedDynamicSizes);
1167 if (foldedTensorType == op.getType())
1170 auto newOp = EmptyOp::create(rewriter, op.getLoc(), foldedTensorType,
1171 foldedDynamicSizes);
1177struct FoldEmptyTensorWithDimOp :
public OpRewritePattern<DimOp> {
1178 using OpRewritePattern<DimOp>::OpRewritePattern;
1180 LogicalResult matchAndRewrite(tensor::DimOp dimOp,
1181 PatternRewriter &rewriter)
const override {
1182 std::optional<int64_t> maybeConstantIndex = dimOp.getConstantIndex();
1183 auto emptyTensorOp = dimOp.getSource().getDefiningOp<EmptyOp>();
1184 if (!emptyTensorOp || !maybeConstantIndex)
1186 auto emptyTensorType = emptyTensorOp.getType();
1187 if (*maybeConstantIndex < 0 ||
1188 *maybeConstantIndex >= emptyTensorType.getRank() ||
1189 !emptyTensorType.isDynamicDim(*maybeConstantIndex))
1192 emptyTensorOp.getDynamicSize(*maybeConstantIndex));
1212struct FoldEmptyTensorWithCastOp :
public OpRewritePattern<CastOp> {
1213 using OpRewritePattern<CastOp>::OpRewritePattern;
1215 LogicalResult matchAndRewrite(CastOp castOp,
1216 PatternRewriter &rewriter)
const override {
1219 auto producer = castOp.getSource().getDefiningOp<EmptyOp>();
1224 llvm::cast<RankedTensorType>(castOp->getResult(0).getType());
1225 ArrayRef<int64_t> resultShape = resultType.getShape();
1226 SmallVector<OpFoldResult> currMixedSizes = producer.getMixedSizes();
1227 SmallVector<OpFoldResult> newMixedSizes;
1228 newMixedSizes.reserve(currMixedSizes.size());
1229 assert(resultShape.size() == currMixedSizes.size() &&
1230 "mismatch in result shape and sizes of empty op");
1231 for (
auto [newDim, currDim] : llvm::zip(resultShape, currMixedSizes)) {
1234 if (
auto attr = llvm::dyn_cast_if_present<Attribute>(currDim)) {
1235 if (ShapedType::isDynamic(newDim) ||
1236 newDim != llvm::cast<IntegerAttr>(attr).getInt()) {
1241 producer,
"mismatch in static value of shape of empty tensor "
1242 "result and cast result");
1244 newMixedSizes.push_back(attr);
1250 if (ShapedType::isStatic(newDim)) {
1251 newMixedSizes.push_back(rewriter.
getIndexAttr(newDim));
1257 newMixedSizes.push_back(currDim);
1261 resultType.getElementType(),
1262 resultType.getEncoding());
1269void EmptyOp::getCanonicalizationPatterns(RewritePatternSet &results,
1270 MLIRContext *context) {
1271 results.
add<FoldEmptyTensorWithCastOp, FoldEmptyTensorWithDimOp,
1272 ReplaceEmptyTensorStaticShapeDims>(context);
1289struct ExtractFromTensorCast :
public OpRewritePattern<tensor::ExtractOp> {
1290 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
1292 LogicalResult matchAndRewrite(tensor::ExtractOp extract,
1293 PatternRewriter &rewriter)
const final {
1294 auto tensorCast = extract.getTensor().getDefiningOp<tensor::CastOp>();
1297 if (!llvm::isa<RankedTensorType>(tensorCast.getSource().getType()))
1300 extract, tensorCast.getSource(), extract.getIndices());
1315struct ExtractFromCollapseShape :
public OpRewritePattern<tensor::ExtractOp> {
1316 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
1318 LogicalResult matchAndRewrite(tensor::ExtractOp extractOp,
1319 PatternRewriter &rewriter)
const final {
1321 extractOp.getTensor().getDefiningOp<tensor::CollapseShapeOp>();
1324 if (!collapseOp.getSrcType().hasStaticShape())
1327 auto sourceSizes = collapseOp.getSrcType().getShape();
1329 SmallVector<Value>
indices(extractOp.getIndices().begin(),
1330 extractOp.getIndices().end());
1331 SmallVector<Value> sourceIndices;
1332 for (
auto [index, group] :
1333 llvm::zip(
indices, collapseOp.getReassociationIndices())) {
1334 assert(!group.empty() &&
"association indices groups cannot be empty");
1335 auto groupSize = group.size();
1337 if (groupSize == 1) {
1338 sourceIndices.push_back(index);
1342 SmallVector<int64_t> basis =
1343 llvm::map_to_vector(group, [&](int64_t d) {
return sourceSizes[d]; });
1344 auto delinearize = affine::AffineDelinearizeIndexOp::create(
1345 rewriter, extractOp.getLoc(), index, basis,
true);
1346 llvm::append_range(sourceIndices,
delinearize.getResults());
1348 if (collapseOp.getReassociationIndices().empty()) {
1351 cast<RankedTensorType>(collapseOp.getSrcType()).getRank();
1353 rewriter, extractOp.getLoc(), zeroAffineMap,
1354 ArrayRef<OpFoldResult>{});
1355 for (int64_t i = 0; i < srcRank; i++) {
1356 sourceIndices.push_back(
1362 extractOp, collapseOp.getSrc(), sourceIndices);
1369void ExtractOp::getAsmResultNames(
1371 setNameFn(getResult(),
"extracted");
1374LogicalResult ExtractOp::verify() {
1376 auto tensorType = llvm::cast<RankedTensorType>(getTensor().
getType());
1377 if (tensorType.getRank() !=
static_cast<int64_t
>(
getIndices().size()))
1378 return emitOpError(
"incorrect number of indices for extract_element");
1387 auto insertOp = extractOp.getTensor().
getDefiningOp<InsertOp>();
1392 if (insertOp && insertOp.getScalar().getType() == extractOp.getType() &&
1393 llvm::equal(insertOp.getIndices(), extractOp.getIndices(), isSame))
1394 return insertOp.getScalar();
1399OpFoldResult ExtractOp::fold(FoldAdaptor adaptor) {
1400 if (Attribute tensor = adaptor.getTensor()) {
1403 if (
auto splatTensor = llvm::dyn_cast<SplatElementsAttr>(tensor))
1404 return splatTensor.getSplatValue<Attribute>();
1407 if (isa<DenseResourceElementsAttr>(tensor))
1412 SmallVector<uint64_t, 8>
indices;
1413 for (Attribute indice : adaptor.getIndices()) {
1414 if (!indice || !llvm::isa<IntegerAttr>(indice))
1416 indices.push_back(llvm::cast<IntegerAttr>(indice).getInt());
1420 if (
auto fromElementsOp = getTensor().getDefiningOp<FromElementsOp>()) {
1421 auto tensorType = llvm::cast<RankedTensorType>(fromElementsOp.getType());
1422 auto rank = tensorType.getRank();
1423 assert(
static_cast<int64_t
>(
indices.size()) == tensorType.getRank() &&
1427 for (
int i = rank - 1; i >= 0; --i) {
1428 flatIndex +=
indices[i] * stride;
1429 stride *= tensorType.getDimSize(i);
1433 if (
static_cast<int>(fromElementsOp.getElements().size()) <= flatIndex ||
1436 return fromElementsOp.getElements()[flatIndex];
1440 if (Attribute tensor = adaptor.getTensor()) {
1441 auto elementsAttr = llvm::dyn_cast<ElementsAttr>(tensor);
1442 if (elementsAttr && elementsAttr.isValidIndex(
indices))
1443 return elementsAttr.getValues<Attribute>()[
indices];
1452void ExtractOp::getCanonicalizationPatterns(RewritePatternSet &results,
1453 MLIRContext *context) {
1454 results.
add<ExtractFromTensorCast>(context);
1459 patterns.
add<ExtractFromCollapseShape>(patterns.
getContext());
1466void FromElementsOp::getAsmResultNames(
1468 setNameFn(getResult(),
"from_elements");
1473 assert(!elements.empty() &&
"expected at least one element");
1474 Type resultType = RankedTensorType::get(
1475 {
static_cast<int64_t>(elements.size())}, elements.front().
getType());
1476 build(builder,
result, resultType, elements);
1479OpFoldResult FromElementsOp::fold(FoldAdaptor adaptor) {
1484 Type eltType =
getType().getElementType();
1487 if (!llvm::is_contained(adaptor.getElements(),
nullptr))
1510struct ExtractElementFromIndexCast
1511 :
public OpRewritePattern<tensor::ExtractOp> {
1512 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
1514 LogicalResult matchAndRewrite(tensor::ExtractOp extract,
1515 PatternRewriter &rewriter)
const final {
1516 Location loc = extract.getLoc();
1517 auto indexCast = extract.getTensor().getDefiningOp<arith::IndexCastOp>();
1523 auto newExtract = tensor::ExtractOp::create(
1524 rewriter, loc, elementTy, indexCast.getIn(), extract.getIndices());
1535void FromElementsOp::getCanonicalizationPatterns(RewritePatternSet &results,
1536 MLIRContext *context) {
1537 results.
add<ExtractElementFromIndexCast>(context);
1544void GatherOp::getAsmResultNames(
1546 setNameFn(getResult(),
"gather");
1561RankedTensorType GatherOp::inferResultType(RankedTensorType sourceType,
1562 RankedTensorType indicesType,
1563 ArrayRef<int64_t> gatherDims,
1565 SmallVector<int64_t> resultShape(indicesType.getShape().drop_back());
1566 resultShape.reserve(resultShape.size() + sourceType.getRank());
1567 for (int64_t idx : llvm::seq<int64_t>(0, sourceType.getRank())) {
1568 if (llvm::binary_search(gatherDims, idx)) {
1570 resultShape.push_back(1);
1573 resultShape.push_back(sourceType.getDimSize(idx));
1575 return RankedTensorType::Builder(sourceType).setShape(resultShape);
1581 StringRef gatherOrScatter, StringRef sourceOrDest) {
1583 return op->
emitOpError(gatherOrScatter) <<
"_dims must be non-empty";
1585 int64_t numGatherDims = dims.size();
1586 if (numGatherDims > rank)
1588 <<
"_dims overflow " << sourceOrDest <<
" rank";
1591 <<
"_dims length must match the size of last dimension of indices";
1595 <<
"_dims value must be non-negative";
1598 <<
"_dims value must be smaller than " << sourceOrDest <<
" rank";
1600 for (
int64_t i = 1; i < numGatherDims; ++i) {
1601 if (dims[i - 1] >= dims[i])
1603 <<
"_dims values must be strictly increasing";
1608LogicalResult GatherOp::verify() {
1609 int64_t sourceRank = getSourceType().getRank();
1610 ArrayRef<int64_t> gatherDims = getGatherDims();
1612 getIndicesType().
getShape(), sourceRank,
1613 "gather",
"source")))
1616 RankedTensorType expectedResultType = GatherOp::inferResultType(
1617 getSourceType(), getIndicesType(), gatherDims,
false);
1618 RankedTensorType expectedRankReducedResultType = GatherOp::inferResultType(
1619 getSourceType(), getIndicesType(), gatherDims,
true);
1620 if (getResultType() != expectedResultType &&
1621 getResultType() != expectedRankReducedResultType) {
1625 << expectedResultType <<
" or its rank-reduced variant "
1626 << expectedRankReducedResultType <<
" (got: " << getResultType()
1633OpFoldResult GatherOp::fold(FoldAdaptor adaptor) {
1634 if (OpFoldResult reshapedSource = reshapeConstantSource(
1635 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
1637 return reshapedSource;
1645void InsertOp::getAsmResultNames(
1647 setNameFn(getResult(),
"inserted");
1650LogicalResult InsertOp::verify() {
1652 auto destType = llvm::cast<RankedTensorType>(getDest().
getType());
1653 if (destType.getRank() !=
static_cast<int64_t
>(
getIndices().size()))
1654 return emitOpError(
"incorrect number of indices");
1658OpFoldResult InsertOp::fold(FoldAdaptor adaptor) {
1659 Attribute scalar = adaptor.getScalar();
1660 Attribute dest = adaptor.getDest();
1662 if (
auto splatDest = llvm::dyn_cast<SplatElementsAttr>(dest))
1663 if (scalar == splatDest.getSplatValue<Attribute>())
1672void GenerateOp::getAsmResultNames(
1674 setNameFn(getResult(),
"generated");
1677LogicalResult GenerateOp::reifyResultShapes(
1679 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
1681 for (
auto dim : llvm::seq<int64_t>(0,
getType().getRank())) {
1682 if (
getType().isDynamicDim(dim)) {
1683 reifiedReturnShapes[0][dim] = getOperand(idx++);
1685 reifiedReturnShapes[0][dim] =
1692LogicalResult GenerateOp::verify() {
1695 RankedTensorType resultType = llvm::cast<RankedTensorType>(
getType());
1702LogicalResult GenerateOp::verifyRegions() {
1703 RankedTensorType resultTy = llvm::cast<RankedTensorType>(
getType());
1705 if (!llvm::all_of(getBody().getArgumentTypes(),
1706 [](Type ty) {
return ty.
isIndex(); }))
1707 return emitError(
"all body arguments must be index");
1708 if (getBody().getNumArguments() != resultTy.getRank())
1709 return emitError(
"must have one body argument per input dimension");
1712 auto yieldOp = cast<YieldOp>(getBody().getBlocks().front().getTerminator());
1714 if (yieldOp.getValue().getType() != resultTy.getElementType())
1716 "body must be terminated with a `yield` operation of the tensor "
1722void GenerateOp::build(
1723 OpBuilder &
b, OperationState &
result, Type resultTy,
1726 build(
b,
result, resultTy, dynamicExtents);
1729 OpBuilder::InsertionGuard guard(
b);
1730 Region *bodyRegion =
result.regions.front().get();
1731 auto rank = llvm::cast<RankedTensorType>(resultTy).getRank();
1732 SmallVector<Type, 2> argumentTypes(rank,
b.getIndexType());
1733 SmallVector<Location, 2> argumentLocs(rank,
result.location);
1735 b.createBlock(bodyRegion, bodyRegion->
end(), argumentTypes, argumentLocs);
1745struct StaticTensorGenerate :
public OpRewritePattern<GenerateOp> {
1746 using OpRewritePattern<GenerateOp>::OpRewritePattern;
1748 LogicalResult matchAndRewrite(GenerateOp generateOp,
1749 PatternRewriter &rewriter)
const final {
1750 SmallVector<Value> foldedDynamicSizes;
1752 generateOp.getType(), generateOp.getDynamicExtents(),
1753 foldedDynamicSizes);
1756 if (foldedTensorType == generateOp.getType())
1759 auto loc = generateOp.getLoc();
1761 GenerateOp::create(rewriter, loc, foldedTensorType, foldedDynamicSizes);
1763 newOp.getBody().begin());
1765 generateOp.getType(), newOp);
1781struct ExtractFromTensorGenerate :
public OpRewritePattern<tensor::ExtractOp> {
1782 using OpRewritePattern<tensor::ExtractOp>::OpRewritePattern;
1784 LogicalResult matchAndRewrite(tensor::ExtractOp extract,
1785 PatternRewriter &rewriter)
const final {
1786 auto tensorFromElements = extract.getTensor().getDefiningOp<GenerateOp>();
1791 Block *body = &tensorFromElements.getBody().front();
1794 rewriter.
clone(op, mapping);
1805void GenerateOp::getCanonicalizationPatterns(RewritePatternSet &results,
1806 MLIRContext *context) {
1808 results.
add<ExtractFromTensorGenerate, StaticTensorGenerate>(context);
1815void RankOp::getAsmResultNames(
function_ref<
void(Value, StringRef)> setNameFn) {
1816 setNameFn(getResult(),
"rank");
1819OpFoldResult RankOp::fold(FoldAdaptor adaptor) {
1821 auto type = getOperand().getType();
1822 auto shapedType = llvm::dyn_cast<ShapedType>(type);
1823 if (shapedType && shapedType.hasRank())
1824 return IntegerAttr::get(IndexType::get(
getContext()), shapedType.getRank());
1825 return IntegerAttr();
1832void ReshapeOp::getAsmResultNames(
1834 setNameFn(getResult(),
"reshape");
1839 for (
auto dim : type.getShape())
1844LogicalResult ReshapeOp::verify() {
1845 TensorType operandType = llvm::cast<TensorType>(getSource().
getType());
1846 TensorType resultType = llvm::cast<TensorType>(getResult().
getType());
1849 return emitOpError(
"element types of source and destination tensor "
1850 "types should be the same");
1854 auto resultRankedType = llvm::dyn_cast<RankedTensorType>(resultType);
1855 auto operandRankedType = llvm::dyn_cast<RankedTensorType>(operandType);
1857 if (resultRankedType) {
1858 if (operandRankedType && resultRankedType.hasStaticShape() &&
1859 operandRankedType.hasStaticShape()) {
1861 return emitOpError(
"source and destination tensor should have the "
1862 "same number of elements");
1864 if (ShapedType::isDynamic(shapeSize))
1865 return emitOpError(
"cannot use shape operand with dynamic length to "
1866 "reshape to statically-ranked tensor type");
1867 if (shapeSize != resultRankedType.getRank())
1869 "length of shape operand differs from the result's tensor rank");
1874OpFoldResult ReshapeOp::fold(FoldAdaptor adaptor) {
1875 if (OpFoldResult reshapedSource = reshapeConstantSource(
1876 llvm::dyn_cast_if_present<DenseElementsAttr>(adaptor.getSource()),
1878 return reshapedSource;
1883 if (
auto reshapeOpProducer = getSource().getDefiningOp<ReshapeOp>()) {
1884 getSourceMutable().assign(reshapeOpProducer.getSource());
1888 auto source = getSource();
1889 auto sourceTy = dyn_cast<RankedTensorType>(source.getType());
1890 auto resultTy = dyn_cast<RankedTensorType>(
getType());
1891 if (!sourceTy || !resultTy || sourceTy != resultTy)
1896 if (sourceTy.getRank() <= 1)
1899 if (
auto fromElements =
getShape().getDefiningOp<tensor::FromElementsOp>()) {
1900 auto elements = fromElements.getElements();
1902 sourceTy.getRank() ==
static_cast<int64_t
>(elements.size());
1903 for (
int id = 0, s = elements.size();
id < s && dynamicNoop; ++
id) {
1904 auto element = elements[id];
1907 dynamicNoop &= cst.value() == sourceTy.getDimSize(
id);
1911 if (
auto dimOp = element.getDefiningOp<tensor::DimOp>()) {
1912 dynamicNoop &= dimOp.getSource() == source;
1916 cst.has_value() && cst.value() ==
static_cast<int64_t
>(id);
1920 dynamicNoop =
false;
1935void CollapseShapeOp::getAsmResultNames(
1937 setNameFn(getResult(),
"collapsed");
1940void ExpandShapeOp::getAsmResultNames(
1942 setNameFn(getResult(),
"expanded");
1945int64_t ExpandShapeOp::getCorrespondingSourceDim(int64_t resultDim) {
1946 assert(resultDim >= 0 && resultDim < getResultType().getRank() &&
1947 "invalid resultDim");
1948 for (
const auto &it : llvm::enumerate(getReassociationIndices()))
1949 if (llvm::is_contained(it.value(), resultDim))
1951 llvm_unreachable(
"could not find reassociation group");
1954FailureOr<SmallVector<OpFoldResult>>
1955ExpandShapeOp::inferOutputShape(OpBuilder &
b, Location loc,
1956 RankedTensorType expandedType,
1957 ArrayRef<ReassociationIndices> reassociation,
1958 ArrayRef<OpFoldResult> inputShape) {
1959 std::optional<SmallVector<OpFoldResult>> outputShape =
1964 return *outputShape;
1967SmallVector<OpFoldResult> ExpandShapeOp::getMixedOutputShape() {
1971void ExpandShapeOp::build(OpBuilder &builder, OperationState &
result,
1972 Type resultType, Value src,
1973 ArrayRef<ReassociationIndices> reassociation,
1974 ArrayRef<OpFoldResult> outputShape) {
1975 auto [staticOutputShape, dynamicOutputShape] =
1977 build(builder,
result, cast<RankedTensorType>(resultType), src,
1979 dynamicOutputShape, staticOutputShape);
1982void ExpandShapeOp::build(OpBuilder &builder, OperationState &
result,
1983 Type resultType, Value src,
1984 ArrayRef<ReassociationIndices> reassociation) {
1985 SmallVector<OpFoldResult> inputShape =
1987 auto tensorResultTy = cast<RankedTensorType>(resultType);
1988 FailureOr<SmallVector<OpFoldResult>> outputShape = inferOutputShape(
1989 builder,
result.location, tensorResultTy, reassociation, inputShape);
1990 SmallVector<OpFoldResult> outputShapeOrEmpty;
1991 if (succeeded(outputShape)) {
1992 outputShapeOrEmpty = *outputShape;
1994 build(builder,
result, tensorResultTy, src, reassociation,
1995 outputShapeOrEmpty);
1998SmallVector<AffineMap, 4> CollapseShapeOp::getReassociationMaps() {
2001SmallVector<ReassociationExprs, 4> CollapseShapeOp::getReassociationExprs() {
2003 getReassociationIndices());
2006SmallVector<AffineMap, 4> ExpandShapeOp::getReassociationMaps() {
2009SmallVector<ReassociationExprs, 4> ExpandShapeOp::getReassociationExprs() {
2011 getReassociationIndices());
2014RankedTensorType CollapseShapeOp::inferCollapsedType(
2015 RankedTensorType type, ArrayRef<ReassociationIndices> reassociation) {
2016 return inferCollapsedType(
2018 type.getContext(), reassociation)));
2024CollapseShapeOp::inferCollapsedType(RankedTensorType type,
2025 ArrayRef<AffineMap> reassociation) {
2026 auto shape = type.getShape();
2027 SmallVector<int64_t, 4> newShape;
2028 newShape.reserve(reassociation.size());
2033 unsigned currentDim = 0;
2034 for (AffineMap m : reassociation) {
2035 unsigned dim = m.getNumResults();
2036 auto band = shape.slice(currentDim, dim);
2038 if (llvm::is_contained(band, ShapedType::kDynamic))
2039 size = ShapedType::kDynamic;
2041 for (
unsigned d = 0; d < dim; ++d)
2042 size *= shape[currentDim + d];
2043 newShape.push_back(size);
2047 return RankedTensorType::get(newShape, type.getElementType());
2050void CollapseShapeOp::build(OpBuilder &
b, OperationState &
result, Value src,
2051 ArrayRef<ReassociationIndices> reassociation,
2052 ArrayRef<NamedAttribute> attrs) {
2053 auto srcType = llvm::cast<RankedTensorType>(src.
getType());
2054 RankedTensorType collapsedType = inferCollapsedType(srcType, reassociation);
2056 RankedTensorType::get(collapsedType.getShape(), srcType.getElementType(),
2057 srcType.getEncoding());
2058 result.addAttribute(getReassociationAttrStrName(),
2060 build(
b,
result, resultType, src, attrs);
2063template <
typename TensorReshapeOp,
bool isExpansion = std::is_same<
2064 TensorReshapeOp, ExpandShapeOp>::value>
2066 RankedTensorType expandedType,
2067 RankedTensorType collapsedType) {
2069 verifyReshapeLikeTypes(op, expandedType, collapsedType, isExpansion)))
2073 if (expandedType.hasStaticShape() && collapsedType.hasStaticShape()) {
2074 int64_t expandedNumElements = expandedType.getNumElements();
2075 int64_t collapsedNumElements = collapsedType.getNumElements();
2076 if (expandedNumElements != collapsedNumElements) {
2077 return op.emitOpError(
"number of elements must be preserved: ")
2078 << expandedNumElements <<
" != " << collapsedNumElements;
2082 auto maps = op.getReassociationMaps();
2083 RankedTensorType expectedType =
2084 CollapseShapeOp::inferCollapsedType(expandedType, maps);
2086 return op.emitOpError(
"expected collapsed type to be ")
2087 << expectedType <<
", but got " << collapsedType;
2091LogicalResult ExpandShapeOp::verify() {
2092 RankedTensorType srcType = getSrc().getType();
2093 RankedTensorType resultType = getResult().getType();
2095 if ((int64_t)getStaticOutputShape().size() != resultType.getRank())
2096 return emitOpError(
"expected number of static shape dims to be equal to "
2097 "the output rank (")
2098 << resultType.getRank() <<
") but found "
2099 << getStaticOutputShape().size() <<
" inputs instead";
2101 if ((int64_t)getOutputShape().size() !=
2102 llvm::count(getStaticOutputShape(), ShapedType::kDynamic))
2103 return emitOpError(
"mismatch in dynamic dims in output_shape and "
2104 "static_output_shape: static_output_shape has ")
2105 << llvm::count(getStaticOutputShape(), ShapedType::kDynamic)
2106 <<
" dynamic dims while output_shape has " << getOutputShape().size()
2117 ArrayRef<int64_t> resShape = getResult().getType().getShape();
2118 for (
auto [pos, shape] : llvm::enumerate(resShape))
2119 if (ShapedType::isStatic(shape) && shape != staticOutputShapes[pos])
2120 return emitOpError(
"invalid output shape provided at pos ") << pos;
2125LogicalResult CollapseShapeOp::verify() {
2126 CollapseShapeOp op = *
this;
2127 if (llvm::any_of(op.getReassociationIndices(),
2129 return op.emitOpError(
"reassociation indices must not be empty");
2131 RankedTensorType srcType = op.getSrc().getType();
2132 RankedTensorType resultType = op.getResult().getType();
2140template <
typename TensorReshapeOp>
2141struct FoldReshapeWithConstant : OpRewritePattern<TensorReshapeOp> {
2142 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2143 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2144 PatternRewriter &rewriter)
const override {
2145 DenseElementsAttr attr;
2152 if (!reshapeOp.getResultType().hasStaticShape())
2155 reshapeOp.getResultType(), attr.
getRawData());
2162template <
typename TensorReshapeOp>
2163class FoldReshapeWithSplat :
public OpRewritePattern<TensorReshapeOp> {
2165 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2167 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2168 PatternRewriter &rewriter)
const override {
2169 auto splatOp = reshapeOp.getSrc().template getDefiningOp<tensor::SplatOp>();
2170 if (!splatOp || !splatOp.getAggregate().getType().hasStaticShape())
2174 reshapeOp, reshapeOp.getResultType(), splatOp.getInput());
2181template <
typename TensorReshapeOp>
2182struct FoldReshapeWithFromElements : OpRewritePattern<TensorReshapeOp> {
2183 using OpRewritePattern<TensorReshapeOp>::OpRewritePattern;
2184 LogicalResult matchAndRewrite(TensorReshapeOp reshapeOp,
2185 PatternRewriter &rewriter)
const override {
2187 reshapeOp.getSrc().template getDefiningOp<FromElementsOp>();
2191 auto shapedTy = llvm::cast<ShapedType>(reshapeOp.getType());
2193 if (!shapedTy.hasStaticShape())
2197 fromElements.getElements());
2203struct FoldCollapseOfCastOp :
public OpRewritePattern<CollapseShapeOp> {
2204 using OpRewritePattern<CollapseShapeOp>::OpRewritePattern;
2206 LogicalResult matchAndRewrite(CollapseShapeOp collapseShapeOp,
2207 PatternRewriter &rewriter)
const override {
2208 auto castOp = collapseShapeOp.getSrc().getDefiningOp<tensor::CastOp>();
2212 RankedTensorType srcType =
2213 llvm::cast<RankedTensorType>(castOp.getSource().getType());
2214 RankedTensorType newResultType = CollapseShapeOp::inferCollapsedType(
2215 srcType, collapseShapeOp.getReassociationMaps());
2217 if (newResultType == collapseShapeOp.getResultType()) {
2219 collapseShapeOp.getSrcMutable().assign(castOp.getSource());
2222 auto newOp = CollapseShapeOp::create(rewriter, collapseShapeOp.getLoc(),
2223 newResultType, castOp.getSource(),
2224 collapseShapeOp.getReassociation());
2226 collapseShapeOp, collapseShapeOp.getResultType(), newOp);
2236struct ConvertToStaticExpandShape :
public OpRewritePattern<ExpandShapeOp> {
2237 using OpRewritePattern<ExpandShapeOp>::OpRewritePattern;
2239 LogicalResult matchAndRewrite(ExpandShapeOp expandOp,
2240 PatternRewriter &rewriter)
const override {
2241 auto castOp = expandOp.getSrc().getDefiningOp<CastOp>();
2245 ArrayRef<int64_t> castSrcShape = castOp.getSource().getType().getShape();
2246 SmallVector<ReassociationIndices, 4> reassoc =
2247 expandOp.getReassociationIndices();
2249 SmallVector<int64_t> newOutputShape(expandOp.getResultType().getShape());
2250 SmallVector<Value> dynamicOutputShape;
2251 auto outputIt = expandOp.getOutputShape().begin();
2253 for (
const auto &[inputDim, innerReassoc] : llvm::enumerate(reassoc)) {
2254 for (uint64_t outDim : innerReassoc) {
2255 if (ShapedType::isStatic(newOutputShape[outDim]))
2262 Value val = *outputIt;
2264 if (ShapedType::isDynamic(castSrcShape[inputDim])) {
2265 dynamicOutputShape.push_back(val);
2271 newOutputShape[outDim] = cst.getSExtValue();
2273 dynamicOutputShape.push_back(val);
2279 if (expandOp.getOutputShape().size() == dynamicOutputShape.size())
2283 SmallVector<int64_t> newInputShape(expandOp.getSrcType().getRank(), 1l);
2284 for (
auto inDim : llvm::seq<int>(0, newInputShape.size())) {
2285 for (
auto outDim : reassoc[inDim]) {
2286 auto ofr = newOutputShape[outDim];
2287 if (ShapedType::isDynamic(ofr)) {
2288 newInputShape[inDim] = ShapedType::kDynamic;
2291 newInputShape[inDim] *= ofr;
2295 SmallVector<OpFoldResult> outputOfr =
2300 Type elementType = expandOp.getSrcType().getElementType();
2301 auto inputType = RankedTensorType::get(
2302 newInputShape, elementType,
2305 auto outputType = RankedTensorType::get(
2306 newOutputShape, elementType,
2308 newOutputShape, elementType));
2309 auto inputCast = CastOp::create(rewriter, expandOp.getLoc(), inputType,
2311 auto newExpand = ExpandShapeOp::create(
2312 rewriter, expandOp.getLoc(), outputType, inputCast.getResult(),
2313 expandOp.getReassociationIndices(), outputOfr);
2315 newExpand.getResult());
2321void ExpandShapeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2322 MLIRContext *context) {
2324 ComposeReassociativeReshapeOps<ExpandShapeOp, ReshapeOpKind::kExpand>,
2325 ComposeExpandOfCollapseOp<ExpandShapeOp, CollapseShapeOp, CastOp>,
2326 ConvertToStaticExpandShape, FoldReshapeWithConstant<ExpandShapeOp>,
2327 FoldReshapeWithSplat<ExpandShapeOp>,
2328 FoldReshapeWithFromElements<ExpandShapeOp>>(context);
2331void CollapseShapeOp::getCanonicalizationPatterns(RewritePatternSet &results,
2332 MLIRContext *context) {
2334 ComposeReassociativeReshapeOps<CollapseShapeOp, ReshapeOpKind::kCollapse>,
2335 ComposeCollapseOfExpandOp<CollapseShapeOp, ExpandShapeOp, CastOp,
2336 tensor::DimOp, RankedTensorType>,
2337 FoldReshapeWithConstant<CollapseShapeOp>,
2338 FoldReshapeWithSplat<CollapseShapeOp>,
2339 FoldReshapeWithFromElements<CollapseShapeOp>, FoldCollapseOfCastOp>(
2343OpFoldResult ExpandShapeOp::fold(FoldAdaptor adaptor) {
2345 adaptor.getOperands());
2348OpFoldResult CollapseShapeOp::fold(FoldAdaptor adaptor) {
2350 adaptor.getOperands());
2357void ExtractSliceOp::getAsmResultNames(
2359 setNameFn(getResult(),
"extracted_slice");
2366ExtractSliceOp::inferResultType(RankedTensorType sourceTensorType,
2367 ArrayRef<int64_t> staticSizes) {
2371 assert(
static_cast<int64_t
>(staticSizes.size()) ==
2372 sourceTensorType.getRank() &&
2373 "unexpected staticSizes not equal to rank of source");
2374 return RankedTensorType::get(staticSizes, sourceTensorType.getElementType(),
2375 sourceTensorType.getEncoding());
2379ExtractSliceOp::inferResultType(RankedTensorType sourceTensorType,
2380 ArrayRef<OpFoldResult> sizes) {
2381 SmallVector<int64_t> staticSizes;
2384 assert(
static_cast<int64_t
>(staticSizes.size()) ==
2385 sourceTensorType.getRank() &&
2386 "unexpected staticSizes not equal to rank of source");
2387 return RankedTensorType::get(staticSizes, sourceTensorType.getElementType(),
2388 sourceTensorType.getEncoding());
2394 const llvm::SmallBitVector &droppedDims) {
2395 assert(staticSizes.size() == droppedDims.size() &&
2396 "expected one dropped-dimension bit per size");
2399 resultShape.reserve(staticSizes.size() - droppedDims.count());
2400 for (
auto [idx, size] : llvm::enumerate(staticSizes))
2401 if (!droppedDims.test(idx))
2402 resultShape.push_back(size);
2404 Type elementType = sourceTensorType.getElementType();
2405 return RankedTensorType::get(resultShape, elementType,
2407 resultShape, elementType));
2413 const llvm::SmallBitVector &droppedDims) {
2416 return inferSliceType(sourceTensorType, staticSizes, droppedDims);
2422 RankedTensorType resultType,
Value source,
2432 auto sourceRankedTensorType = llvm::cast<RankedTensorType>(source.
getType());
2435 resultType = llvm::cast<RankedTensorType>(
2436 ExtractSliceOp::inferResultType(sourceRankedTensorType, staticSizes));
2438 result.addAttributes(attrs);
2439 build(
b,
result, resultType, source, dynamicOffsets, dynamicSizes,
2440 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
2441 b.getDenseI64ArrayAttr(staticSizes),
2442 b.getDenseI64ArrayAttr(staticStrides));
2447void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2448 ArrayRef<OpFoldResult> offsets,
2449 ArrayRef<OpFoldResult> sizes,
2450 ArrayRef<OpFoldResult> strides,
2451 ArrayRef<NamedAttribute> attrs) {
2452 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2457void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2458 ArrayRef<Range> ranges,
2459 ArrayRef<NamedAttribute> attrs) {
2461 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2466void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result,
2467 RankedTensorType resultType, Value source,
2469 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2470 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
2471 offsets, [](Value v) -> OpFoldResult {
return v; });
2472 SmallVector<OpFoldResult> sizeValues =
2473 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
2474 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
2475 strides, [](Value v) -> OpFoldResult {
return v; });
2476 build(
b,
result, resultType, source, offsetValues, sizeValues, strideValues);
2480void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2482 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2483 build(
b,
result, RankedTensorType(), source, offsets, sizes, strides, attrs);
2488 RankedTensorType expectedType) {
2493 return op->
emitError(
"expected rank to be smaller or equal to ")
2494 <<
"the other rank. ";
2496 return op->
emitError(
"expected type to be ")
2497 << expectedType <<
" or a rank-reduced version. (size mismatch) ";
2499 return op->
emitError(
"expected element type to be ")
2500 << expectedType.getElementType();
2502 llvm_unreachable(
"unexpected extract_slice op verification result");
2508void ExtractSliceOp::build(OpBuilder &
b, OperationState &
result,
2509 RankedTensorType resultType, Value source,
2510 ArrayRef<OpFoldResult> sizes,
2511 ArrayRef<NamedAttribute> attrs) {
2512 Attribute zeroIdxAttr =
b.getIndexAttr(0);
2513 Attribute oneIdxAttr =
b.getIndexAttr(1);
2514 SmallVector<OpFoldResult> readStrides(sizes.size(), oneIdxAttr);
2515 SmallVector<OpFoldResult> readOffsets(sizes.size(), zeroIdxAttr);
2516 build(
b,
result, resultType, source, readOffsets, sizes, readStrides, attrs);
2520LogicalResult ExtractSliceOp::verify() {
2521 RankedTensorType sourceType = getSourceType();
2524 RankedTensorType expectedType =
2525 ExtractSliceOp::inferResultType(sourceType,
getMixedSizes());
2533 sourceType.getShape(), getStaticOffsets(), getStaticSizes(),
2534 getStaticStrides(),
true);
2536 return getOperation()->emitError(boundsResult.
errorMessage);
2541llvm::SmallBitVector ExtractSliceOp::getDroppedDims() {
2546ExtractSliceOp::rankReduceIfNeeded(OpBuilder &
b, Location loc, Value value,
2547 ArrayRef<int64_t> desiredShape) {
2548 auto sourceTensorType = llvm::dyn_cast<RankedTensorType>(value.
getType());
2549 assert(sourceTensorType &&
"not a ranked tensor type");
2550 auto sourceShape = sourceTensorType.getShape();
2551 if (sourceShape.equals(desiredShape))
2553 auto maybeRankReductionMask =
2555 if (!maybeRankReductionMask)
2559 RankedTensorType::Builder(sourceTensorType).setShape(desiredShape));
2562LogicalResult ExtractSliceOp::reifyResultShapes(
2564 reifiedReturnShapes.resize(1);
2565 reifiedReturnShapes[0].reserve(
getType().getRank());
2568 for (
const auto &size :
enumerate(mixedSizes)) {
2569 if (droppedDims.test(size.index()))
2571 reifiedReturnShapes[0].push_back(size.value());
2592class ExtractSliceOpCastFolder final :
public OpRewritePattern<ExtractSliceOp> {
2594 using OpRewritePattern<ExtractSliceOp>::OpRewritePattern;
2596 LogicalResult matchAndRewrite(ExtractSliceOp sliceOp,
2597 PatternRewriter &rewriter)
const override {
2599 if (llvm::any_of(sliceOp.getOperands(), [](Value operand) {
2600 return matchPattern(operand, matchConstantIndex());
2604 auto castOp = sliceOp.getSource().getDefiningOp<CastOp>();
2613 cast<RankedTensorType>(castOp.getSource().getType()).getShape(),
2614 sliceOp.getStaticOffsets(), sliceOp.getStaticSizes(),
2615 sliceOp.getStaticStrides());
2620 Location loc = sliceOp.getLoc();
2621 Value newResult = ExtractSliceOp::create(
2622 rewriter, loc, sliceOp.getType(), castOp.getSource(),
2623 sliceOp.getOffsets(), sliceOp.getSizes(), sliceOp.getStrides(),
2624 sliceOp.getStaticOffsets(), sliceOp.getStaticSizes(),
2625 sliceOp.getStaticStrides());
2634template <
typename IterTy,
typename ElemTy>
2635static void sliceElements(IterTy values, ArrayRef<int64_t> counts,
2636 ArrayRef<int64_t> offsets, ArrayRef<int64_t> sizes,
2637 ArrayRef<int64_t> strides,
2638 llvm::SmallVectorImpl<ElemTy> *outValues) {
2639 assert(offsets.size() == sizes.size());
2640 assert(offsets.size() == strides.size());
2641 if (offsets.empty())
2644 int64_t offset = offsets.front();
2645 int64_t size = sizes.front();
2646 int64_t stride = strides.front();
2647 if (offsets.size() == 1) {
2648 for (int64_t i = 0; i < size; ++i, offset += stride)
2649 outValues->push_back(*(values + offset));
2654 for (int64_t i = 0; i < size; ++i, offset += stride) {
2655 auto begin = values + offset * counts.front();
2656 sliceElements<IterTy, ElemTy>(begin, counts.drop_front(),
2657 offsets.drop_front(), sizes.drop_front(),
2658 strides.drop_front(), outValues);
2665class ConstantOpExtractSliceFolder final
2666 :
public OpRewritePattern<ExtractSliceOp> {
2668 using OpRewritePattern<ExtractSliceOp>::OpRewritePattern;
2670 ConstantOpExtractSliceFolder(MLIRContext *context,
2672 : OpRewritePattern<ExtractSliceOp>(context),
2673 controlFn(std::move(controlFn)) {}
2675 LogicalResult matchAndRewrite(ExtractSliceOp op,
2676 PatternRewriter &rewriter)
const override {
2677 DenseElementsAttr attr;
2686 auto sourceType = llvm::cast<ShapedType>(op.getSource().getType());
2687 auto resultType = llvm::cast<ShapedType>(op.getResult().getType());
2688 if (!sourceType.hasStaticShape() || !resultType.hasStaticShape())
2695 int64_t count = sourceType.getNumElements();
2700 auto offsets = op.getStaticOffsets();
2701 if (llvm::is_contained(offsets, ShapedType::kDynamic))
2703 auto sizes = op.getStaticSizes();
2704 if (llvm::is_contained(sizes, ShapedType::kDynamic))
2706 auto strides = op.getStaticStrides();
2707 if (llvm::is_contained(strides, ShapedType::kDynamic))
2711 SmallVector<int64_t> counts;
2712 ArrayRef<int64_t> shape = sourceType.getShape();
2713 counts.reserve(shape.size());
2714 for (int64_t v : shape) {
2716 counts.push_back(count);
2720 SmallVector<Attribute> outValues;
2721 outValues.reserve(resultType.getNumElements());
2722 sliceElements(attr.
value_begin<Attribute>(), counts, offsets, sizes,
2723 strides, &outValues);
2740 patterns.
add<ConstantOpExtractSliceFolder>(patterns.
getContext(), controlFn);
2752 return inferSliceType(op.getSourceType(), mixedSizes, op.getDroppedDims());
2759 ExtractSliceOp newOp) {
2762 replacement = tensor::CastOp::create(rewriter, op.getLoc(), op.getType(),
2768void ExtractSliceOp::getCanonicalizationPatterns(RewritePatternSet &results,
2769 MLIRContext *context) {
2771 OpWithOffsetSizesAndStridesConstantArgumentFolder<
2772 ExtractSliceOp, SliceReturnTypeCanonicalizer, SliceCanonicalizer>,
2773 ExtractSliceOpCastFolder>(context);
2779 ShapedType shapedType) {
2786 auto shape = shapedType.getShape();
2787 for (
auto it : llvm::zip(op.getMixedSizes(),
shape))
2801 auto insertOp = extractOp.getSource().getDefiningOp<InsertSliceOp>();
2804 if (insertOp && insertOp.getSource().getType() == extractOp.getType() &&
2805 insertOp.isSameAs(extractOp, isSame))
2806 return insertOp.getSource();
2811OpFoldResult ExtractSliceOp::fold(FoldAdaptor adaptor) {
2812 if (OpFoldResult reshapedSource = reshapeConstantSource(
2813 llvm::dyn_cast_if_present<SplatElementsAttr>(adaptor.getSource()),
2815 return reshapedSource;
2816 if (getSourceType() ==
getType() &&
2818 return this->getSource();
2822 return OpFoldResult();
2827 auto rankedTensorType = llvm::cast<RankedTensorType>(
tensor.getType());
2828 unsigned rank = rankedTensorType.getRank();
2832 return b.createOrFold<tensor::ExtractSliceOp>(loc, targetType,
tensor,
2833 offsets, sizes, strides);
2840void InsertSliceOp::getAsmResultNames(
2842 setNameFn(getResult(),
"inserted_slice");
2856 result.addAttributes(attrs);
2857 build(
b,
result, dest.
getType(), source, dest, dynamicOffsets, dynamicSizes,
2858 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
2859 b.getDenseI64ArrayAttr(staticSizes),
2860 b.getDenseI64ArrayAttr(staticStrides));
2865void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2866 Value dest, ArrayRef<Range> ranges,
2867 ArrayRef<NamedAttribute> attrs) {
2869 build(
b,
result, source, dest, offsets, sizes, strides, attrs);
2873void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
2875 ValueRange strides, ArrayRef<NamedAttribute> attrs) {
2876 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
2877 offsets, [](Value v) -> OpFoldResult {
return v; });
2878 SmallVector<OpFoldResult> sizeValues =
2879 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
2880 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
2881 strides, [](Value v) -> OpFoldResult {
return v; });
2882 build(
b,
result, source, dest, offsetValues, sizeValues, strideValues);
2888 RankedTensorType srcType, RankedTensorType dstType,
2893 RankedTensorType expected =
2894 ExtractSliceOp::inferResultType(dstType, staticSizes);
2896 *expectedType = expected;
2901LogicalResult InsertSliceOp::verify() {
2903 RankedTensorType expectedType;
2906 getStaticSizes(), getStaticStrides(), &expectedType);
2913 getDestType().
getShape(), getStaticOffsets(), getStaticSizes(),
2914 getStaticStrides(),
true);
2916 return getOperation()->emitError(boundsResult.
errorMessage);
2939 auto prevInsertOp = insertOp.getDest().getDefiningOp<InsertSliceOp>();
2942 if (!prevInsertOp ||
2943 prevInsertOp.getSource().getType() != insertOp.getSource().getType() ||
2944 !prevInsertOp.isSameAs(insertOp, isSame))
2947 insertOp.getDestMutable().assign(prevInsertOp.getDest());
2959 auto extractOp = insertOp.getSource().
getDefiningOp<ExtractSliceOp>();
2962 if (!extractOp || extractOp.getSource() != insertOp.getDest() ||
2963 !extractOp.isSameAs(insertOp, isSame))
2966 return extractOp.getSource();
2969OpFoldResult InsertSliceOp::fold(FoldAdaptor) {
2970 if (getSourceType().hasStaticShape() &&
getType().hasStaticShape() &&
2971 getSourceType() ==
getType() &&
2973 return this->getSource();
2980 return OpFoldResult();
2983LogicalResult InsertSliceOp::reifyResultShapes(
2985 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
2994template <
typename InsertOpTy>
2995class InsertSliceOpConstantArgumentFolder final
2996 :
public OpRewritePattern<InsertOpTy> {
2998 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3000 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3001 PatternRewriter &rewriter)
const override {
3002 SmallVector<OpFoldResult> mixedOffsets(insertSliceOp.getMixedOffsets());
3003 SmallVector<OpFoldResult> mixedSizes(insertSliceOp.getMixedSizes());
3004 SmallVector<OpFoldResult> mixedStrides(insertSliceOp.getMixedStrides());
3013 SliceBoundsVerificationResult sliceResult =
3015 mixedOffsets, mixedSizes, mixedStrides);
3019 auto sourceType =
inferSliceType(insertSliceOp.getSourceType(), mixedSizes,
3020 insertSliceOp.getDroppedDims());
3021 Value toInsert = insertSliceOp.getSource();
3022 if (sourceType != insertSliceOp.getSourceType()) {
3023 OpBuilder::InsertionGuard g(rewriter);
3027 if (isa<InParallelOpInterface>(insertSliceOp->getParentOp()))
3029 toInsert = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3030 sourceType, toInsert);
3033 insertSliceOp, toInsert, insertSliceOp.getDest(), mixedOffsets,
3034 mixedSizes, mixedStrides);
3059template <
typename InsertOpTy>
3060struct InsertSliceOpCastFolder final :
public OpRewritePattern<InsertOpTy> {
3061 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3063 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3064 PatternRewriter &rewriter)
const override {
3065 if (llvm::any_of(insertSliceOp.getOperands(), [](Value operand) {
3066 return matchPattern(operand, matchConstantIndex());
3070 auto getSourceOfCastOp = [](Value v) -> std::optional<Value> {
3073 return std::nullopt;
3074 return castOp.getSource();
3076 std::optional<Value> sourceCastSource =
3077 getSourceOfCastOp(insertSliceOp.getSource());
3078 std::optional<Value> destCastSource =
3079 getSourceOfCastOp(insertSliceOp.getDest());
3080 if (!sourceCastSource && !destCastSource)
3084 (sourceCastSource ? *sourceCastSource : insertSliceOp.getSource());
3085 auto dst = (destCastSource ? *destCastSource : insertSliceOp.getDest());
3086 auto srcType = llvm::dyn_cast<RankedTensorType>(src.
getType());
3087 auto dstType = llvm::dyn_cast<RankedTensorType>(dst.getType());
3088 if (!srcType || !dstType)
3094 SmallVector<int64_t> staticSizes(insertSliceOp.getStaticSizes());
3096 staticSizes, srcType.getShape(),
true);
3097 if (!rankReductionMask.has_value())
3104 SmallVector<OpFoldResult> mixedSizes(insertSliceOp.getMixedSizes());
3105 int64_t rankReducedIdx = 0;
3106 for (
auto [idx, size] :
enumerate(staticSizes)) {
3107 if (!rankReductionMask.value().contains(idx) &&
3108 !srcType.isDynamicDim(rankReducedIdx)) {
3110 rewriter.
getContext(), srcType.getDimSize(rankReducedIdx));
3111 size = srcType.getDimSize(rankReducedIdx++);
3117 staticSizes, insertSliceOp.getStaticStrides()) !=
3118 SliceVerificationResult::Success)
3120 SliceBoundsVerificationResult sliceResult =
3122 mixedSizes, insertSliceOp.getMixedStrides());
3127 InsertOpTy::create(rewriter, insertSliceOp.getLoc(), src, dst,
3128 insertSliceOp.getMixedOffsets(), mixedSizes,
3129 insertSliceOp.getMixedStrides());
3132 bool isParallelInsert =
3133 std::is_same<InsertOpTy, ParallelInsertSliceOp>::value;
3134 if (!isParallelInsert && dst.getType() != insertSliceOp.getDestType()) {
3135 replacement = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3136 insertSliceOp.getDestType(),
3165template <
typename InsertOpTy>
3166struct InsertSliceOpSourceCastInserter final
3167 :
public OpRewritePattern<InsertOpTy> {
3168 using OpRewritePattern<InsertOpTy>::OpRewritePattern;
3170 LogicalResult matchAndRewrite(InsertOpTy insertSliceOp,
3171 PatternRewriter &rewriter)
const override {
3172 RankedTensorType srcType = insertSliceOp.getSourceType();
3173 if (srcType.getRank() != insertSliceOp.getDestType().getRank())
3175 SmallVector<int64_t> newSrcShape(srcType.getShape());
3176 for (int64_t i = 0; i < srcType.getRank(); ++i) {
3177 if (std::optional<int64_t> constInt =
3182 newSrcShape[i] = *constInt;
3188 RankedTensorType newSrcType = RankedTensorType::get(
3189 newSrcShape, srcType.getElementType(), srcType.getEncoding());
3190 if (srcType == newSrcType ||
3192 !tensor::CastOp::areCastCompatible(srcType, newSrcType))
3200 OpBuilder::InsertionGuard g(rewriter);
3204 if (isa<ParallelCombiningOpInterface>(insertSliceOp->getParentOp()))
3206 Value cast = tensor::CastOp::create(rewriter, insertSliceOp.getLoc(),
3207 newSrcType, insertSliceOp.getSource());
3209 insertSliceOp, cast, insertSliceOp.getDest(),
3210 insertSliceOp.getMixedOffsets(), insertSliceOp.getMixedSizes(),
3211 insertSliceOp.getMixedStrides());
3217llvm::SmallBitVector InsertSliceOp::getDroppedDims() {
3221void InsertSliceOp::getCanonicalizationPatterns(RewritePatternSet &results,
3222 MLIRContext *context) {
3223 results.
add<InsertSliceOpConstantArgumentFolder<InsertSliceOp>,
3224 InsertSliceOpCastFolder<InsertSliceOp>,
3225 InsertSliceOpSourceCastInserter<InsertSliceOp>>(context);
3232 auto rankedTensorType = llvm::cast<RankedTensorType>(dest.
getType());
3233 unsigned rank = rankedTensorType.getRank();
3237 return b.createOrFold<tensor::InsertSliceOp>(loc,
tensor, dest, offsets,
3246 setNameFn(getResult(),
"padded");
3249LogicalResult PadOp::verify() {
3250 auto sourceType = llvm::cast<RankedTensorType>(getSource().
getType());
3251 auto resultType = llvm::cast<RankedTensorType>(getResult().
getType());
3253 PadOp::inferResultType(sourceType, getStaticLow(), getStaticHigh());
3254 if (!expectedType) {
3255 return emitError(
"failed to infer expectedType from sourceType ")
3256 << sourceType <<
", specified resultType is " << resultType;
3258 if (resultType.getRank() != expectedType.getRank()) {
3260 << resultType <<
" does not match the inferred type "
3263 for (
int i = 0, e = sourceType.getRank(); i < e; ++i) {
3264 if (resultType.getDimSize(i) == expectedType.getDimSize(i))
3266 if (expectedType.isDynamicDim(i))
3269 << resultType <<
" does not match the inferred type "
3276LogicalResult PadOp::verifyRegions() {
3277 auto ®ion = getRegion();
3278 unsigned rank = llvm::cast<RankedTensorType>(getResult().
getType()).getRank();
3279 Block &block = region.front();
3281 return emitError(
"expected the block to have ") << rank <<
" arguments";
3285 if (!en.value().isIndex())
3287 << (en.index() + 1) <<
" to be an index";
3292 if (yieldOp.getValue().getType() !=
3294 return emitOpError(
"expected yield type to match shape element type");
3299RankedTensorType PadOp::inferResultType(RankedTensorType sourceType,
3300 ArrayRef<int64_t> staticLow,
3301 ArrayRef<int64_t> staticHigh,
3302 ArrayRef<int64_t> resultShape) {
3303 unsigned rank = sourceType.getRank();
3304 if (staticLow.size() != rank)
3305 return RankedTensorType();
3306 if (staticHigh.size() != rank)
3307 return RankedTensorType();
3308 if (!resultShape.empty() && resultShape.size() != rank)
3309 return RankedTensorType();
3311 SmallVector<int64_t, 4> inferredShape;
3312 for (
auto i : llvm::seq<unsigned>(0, rank)) {
3313 if (sourceType.isDynamicDim(i) || staticLow[i] == ShapedType::kDynamic ||
3314 staticHigh[i] == ShapedType::kDynamic) {
3315 inferredShape.push_back(resultShape.empty() ? ShapedType::kDynamic
3318 int64_t size = sourceType.getDimSize(i) + staticLow[i] + staticHigh[i];
3319 assert((resultShape.empty() || size == resultShape[i] ||
3320 resultShape[i] == ShapedType::kDynamic) &&
3321 "mismatch between inferred shape and result shape");
3322 inferredShape.push_back(size);
3326 Type elementType = sourceType.getElementType();
3327 return RankedTensorType::get(
3328 inferredShape, elementType,
3332void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3333 Value source, ArrayRef<int64_t> staticLow,
3335 bool nofold, ArrayRef<NamedAttribute> attrs) {
3336 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3338 resultType = inferResultType(sourceType, staticLow, staticHigh);
3339 result.addAttributes(attrs);
3340 build(
b,
result, resultType, source, low, high,
3341 b.getDenseI64ArrayAttr(staticLow),
b.getDenseI64ArrayAttr(staticHigh),
3342 nofold ?
b.getUnitAttr() : UnitAttr());
3345void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3347 ArrayRef<NamedAttribute> attrs) {
3348 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3349 unsigned rank = sourceType.getRank();
3350 SmallVector<int64_t, 4> staticVector(rank, ShapedType::kDynamic);
3351 build(
b,
result, resultType, source, staticVector, staticVector, low, high,
3355void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3356 Value source, ArrayRef<OpFoldResult> low,
3357 ArrayRef<OpFoldResult> high,
bool nofold,
3358 ArrayRef<NamedAttribute> attrs) {
3359 auto sourceType = llvm::cast<RankedTensorType>(source.
getType());
3360 SmallVector<Value, 4> dynamicLow, dynamicHigh;
3361 SmallVector<int64_t, 4> staticLow, staticHigh;
3369 resultType = PadOp::inferResultType(sourceType, staticLow, staticHigh);
3371 assert(llvm::isa<RankedTensorType>(resultType));
3372 result.addAttributes(attrs);
3373 build(
b,
result, resultType, source, dynamicLow, dynamicHigh,
3374 b.getDenseI64ArrayAttr(staticLow),
b.getDenseI64ArrayAttr(staticHigh),
3375 nofold ?
b.getUnitAttr() : UnitAttr());
3378void PadOp::build(OpBuilder &
b, OperationState &
result, Type resultType,
3379 Value source, ArrayRef<OpFoldResult> low,
3380 ArrayRef<OpFoldResult> high, Value constantPadValue,
3381 bool nofold, ArrayRef<NamedAttribute> attrs) {
3382 build(
b,
result, resultType, source, low, high, nofold, attrs);
3385 Region *region =
result.regions[0].get();
3386 int sourceRank = llvm::cast<RankedTensorType>(source.
getType()).getRank();
3387 Repeated<Type> blockArgTypes(sourceRank,
b.getIndexType());
3388 SmallVector<Location> blockArgLocs(sourceRank,
result.location);
3392 OpBuilder::InsertionGuard guard(
b);
3393 b.createBlock(region, region->
end(), blockArgTypes, blockArgLocs);
3394 tensor::YieldOp::create(
b,
result.location, constantPadValue);
3397llvm::SmallBitVector PadOp::getPaddedDims() {
3398 llvm::SmallBitVector paddedDims(getSourceType().getRank());
3399 auto extractPaddedDims = [&](ArrayRef<OpFoldResult> paddingWidths) {
3400 for (
const auto &en :
enumerate(paddingWidths))
3402 paddedDims.set(en.index());
3404 extractPaddedDims(getMixedLowPad());
3405 extractPaddedDims(getMixedHighPad());
3412struct FoldStaticZeroPadding :
public OpRewritePattern<PadOp> {
3413 using OpRewritePattern<PadOp>::OpRewritePattern;
3415 LogicalResult matchAndRewrite(PadOp padTensorOp,
3416 PatternRewriter &rewriter)
const override {
3417 if (!padTensorOp.hasZeroLowPad() || !padTensorOp.hasZeroHighPad())
3419 if (padTensorOp.getNofold())
3422 padTensorOp, padTensorOp.getResult().
getType(),
3423 padTensorOp.getSource());
3429struct FoldSourceTensorCast :
public OpRewritePattern<PadOp> {
3430 using OpRewritePattern<PadOp>::OpRewritePattern;
3432 LogicalResult matchAndRewrite(PadOp padTensorOp,
3433 PatternRewriter &rewriter)
const override {
3434 auto castOp = padTensorOp.getSource().getDefiningOp<tensor::CastOp>();
3438 auto newResultType = PadOp::inferResultType(
3439 llvm::cast<RankedTensorType>(castOp.getSource().getType()),
3440 padTensorOp.getStaticLow(), padTensorOp.getStaticHigh(),
3441 padTensorOp.getResultType().getShape());
3443 if (newResultType == padTensorOp.getResultType()) {
3445 padTensorOp.getSourceMutable().assign(castOp.getSource());
3448 auto newOp = PadOp::create(
3449 rewriter, padTensorOp->getLoc(), newResultType,
3450 padTensorOp.getSource(), padTensorOp.getStaticLow(),
3451 padTensorOp.getStaticHigh(), padTensorOp.getLow(),
3452 padTensorOp.getHigh(), padTensorOp.getNofold(),
3455 padTensorOp.getRegion().cloneInto(&newOp.getRegion(), mapper);
3458 padTensorOp, padTensorOp.getResultType(), newOp);
3466struct FoldTargetTensorCast :
public OpRewritePattern<PadOp> {
3467 using OpRewritePattern<PadOp>::OpRewritePattern;
3469 LogicalResult matchAndRewrite(PadOp padTensorOp,
3470 PatternRewriter &rewriter)
const override {
3471 if (!padTensorOp.getResult().hasOneUse())
3474 dyn_cast<tensor::CastOp>(*padTensorOp->getUsers().begin());
3478 tensorCastOp.getDest().getType()))
3481 auto replacementOp = PadOp::create(
3482 rewriter, padTensorOp.getLoc(), tensorCastOp.getDest().getType(),
3483 padTensorOp.getSource(), padTensorOp.getStaticLow(),
3484 padTensorOp.getStaticHigh(), padTensorOp.getLow(),
3485 padTensorOp.getHigh(), padTensorOp.getNofold(),
3487 replacementOp.getRegion().takeBody(padTensorOp.getRegion());
3489 rewriter.
replaceOp(padTensorOp, replacementOp.getResult());
3490 rewriter.
replaceOp(tensorCastOp, replacementOp.getResult());
3530struct FoldOrthogonalPaddings :
public OpRewritePattern<PadOp> {
3531 using OpRewritePattern<PadOp>::OpRewritePattern;
3533 LogicalResult matchAndRewrite(PadOp padOp,
3534 PatternRewriter &rewriter)
const override {
3535 auto innerSliceOp = padOp.getSource().getDefiningOp<ExtractSliceOp>();
3538 auto outerPadOp = innerSliceOp.getSource().getDefiningOp<PadOp>();
3539 if (!outerPadOp || outerPadOp.getNofold())
3541 auto outerSliceOp = outerPadOp.getSource().getDefiningOp<ExtractSliceOp>();
3546 int64_t rank = padOp.getSourceType().getRank();
3547 if (outerSliceOp.getSourceType().getRank() != rank) {
3549 "cannot fold rank-reducing chain");
3553 if (!innerSliceOp.hasUnitStride() || !outerSliceOp.hasUnitStride()) {
3555 padOp,
"cannot fold non-unit stride ExtractSliceOps");
3559 if (!padOp.hasZeroLowPad() || !outerPadOp.hasZeroLowPad()) {
3561 "cannot fold PadOps with low padding");
3565 Attribute innerAttr, outerAttr;
3566 Value innerValue = padOp.getConstantPaddingValue();
3567 Value outerValue = outerPadOp.getConstantPaddingValue();
3568 if (!innerValue || !outerValue ||
3571 innerAttr != outerAttr) {
3573 padOp,
"cannot fold PadOps with different padding values");
3577 llvm::SmallBitVector innerDims = padOp.getPaddedDims();
3578 llvm::SmallBitVector outerDims = outerPadOp.getPaddedDims();
3579 if (innerDims.anyCommon(outerDims)) {
3581 padOp,
"cannot fold PadOps with common padding dimensions");
3589 SmallVector<OpFoldResult> newOffsets(rank, rewriter.
getIndexAttr(0));
3591 OpFoldResult innerOffset = innerSliceOp.getMixedOffsets()[en.index()];
3592 OpFoldResult outerOffset = outerSliceOp.getMixedOffsets()[en.index()];
3593 if (!innerDims.test(en.index()) &&
3595 en.value() = outerOffset;
3598 if (!outerDims.test(en.index()) &&
3600 en.value() = innerOffset;
3604 padOp,
"cannot find zero-offset and zero-padding pair");
3612 SmallVector<OpFoldResult> newSizes = innerSliceOp.getMixedSizes();
3614 if (!outerDims.test(en.index()))
3616 OpFoldResult sliceSize = innerSliceOp.getMixedSizes()[en.index()];
3617 int64_t sourceSize = innerSliceOp.getSourceType().getShape()[en.index()];
3618 assert(ShapedType::isStatic(sourceSize) &&
3619 "expected padded dimension to have a static size");
3622 padOp,
"cannot fold since the inner ExtractSliceOp size does not "
3623 "match the size of the outer padding");
3625 en.value() = outerSliceOp.getMixedSizes()[en.index()];
3629 SmallVector<OpFoldResult> newHighPad(rank, rewriter.
getIndexAttr(0));
3631 if (innerDims.test(en.index()))
3632 newHighPad[en.index()] = padOp.getMixedHighPad()[en.index()];
3633 if (outerDims.test(en.index()))
3634 newHighPad[en.index()] = outerPadOp.getMixedHighPad()[en.index()];
3639 auto newSliceOp = ExtractSliceOp::create(
3640 rewriter, padOp.getLoc(), outerSliceOp.getSource(), newOffsets,
3641 newSizes, innerSliceOp.getMixedStrides());
3642 auto newPadOp = PadOp::create(
3643 rewriter, padOp.getLoc(), padOp.getResultType(), newSliceOp.getResult(),
3644 padOp.getMixedLowPad(), newHighPad, padOp.getNofold(),
3647 newPadOp.getRegion().begin());
3648 rewriter.
replaceOp(padOp, newPadOp.getResult());
3653struct FoldStaticPadding :
public OpRewritePattern<PadOp> {
3654 using OpRewritePattern<PadOp>::OpRewritePattern;
3656 LogicalResult matchAndRewrite(PadOp padTensorOp,
3657 PatternRewriter &rewriter)
const override {
3658 Value input = padTensorOp.getSource();
3659 if (!llvm::isa<RankedTensorType>(input.
getType()))
3661 auto inputDims = llvm::cast<RankedTensorType>(input.
getType()).getShape();
3662 auto inputRank = inputDims.size();
3664 auto oldResultType =
3665 dyn_cast<RankedTensorType>(padTensorOp.getResult().getType());
3669 auto outputDims = oldResultType.getShape();
3672 SmallVector<int64_t> constOperandsLow;
3673 SmallVector<Value> newLows;
3674 for (
auto operand : padTensorOp.getLow()) {
3677 constOperandsLow.push_back(ShapedType::kDynamic);
3678 newLows.push_back(operand);
3681 constOperandsLow.push_back(intOp.getExtValue());
3683 SmallVector<int64_t> constOperandsHigh;
3684 SmallVector<Value> newHighs;
3685 for (
auto operand : padTensorOp.getHigh()) {
3688 constOperandsHigh.push_back(ShapedType::kDynamic);
3689 newHighs.push_back(operand);
3692 constOperandsHigh.push_back(intOp.getExtValue());
3695 SmallVector<int64_t> constLow(padTensorOp.getStaticLow());
3696 SmallVector<int64_t> constHigh(padTensorOp.getStaticHigh());
3699 if (inputDims.size() != outputDims.size() ||
3700 inputDims.size() != constLow.size() ||
3701 inputDims.size() != constHigh.size())
3706 for (
size_t i = 0; i < inputRank; i++) {
3707 if (constLow[i] == ShapedType::kDynamic)
3708 constLow[i] = constOperandsLow[lowCount++];
3709 if (constHigh[i] == ShapedType::kDynamic)
3710 constHigh[i] = constOperandsHigh[highCount++];
3713 auto staticLow = ArrayRef<int64_t>(constLow);
3714 auto staticHigh = ArrayRef<int64_t>(constHigh);
3717 SmallVector<int64_t> newOutDims;
3718 for (
size_t i = 0; i < inputRank; i++) {
3719 if (outputDims[i] == ShapedType::kDynamic) {
3720 newOutDims.push_back(
3721 (staticLow[i] == ShapedType::kDynamic ||
3722 staticHigh[i] == ShapedType::kDynamic ||
3723 inputDims[i] == ShapedType::kDynamic
3724 ? ShapedType::kDynamic
3725 : inputDims[i] + staticLow[i] + staticHigh[i]));
3727 newOutDims.push_back(outputDims[i]);
3731 if (SmallVector<int64_t>(outputDims) == newOutDims ||
3732 llvm::all_of(newOutDims,
3733 [&](int64_t x) {
return x == ShapedType::kDynamic; }))
3736 Type elementType = padTensorOp.getType().getElementType();
3737 auto newResultType = RankedTensorType::get(
3738 newOutDims, elementType,
3741 auto newOp = PadOp::create(
3742 rewriter, padTensorOp->getLoc(), newResultType, input, staticLow,
3743 staticHigh, newLows, newHighs, padTensorOp.getNofold(),
3747 padTensorOp.getRegion().cloneInto(&newOp.getRegion(), mapper);
3775struct FoldConsecutiveConstantPadding :
public OpRewritePattern<tensor::PadOp> {
3776 using OpRewritePattern<tensor::PadOp>::OpRewritePattern;
3778 LogicalResult matchAndRewrite(tensor::PadOp padOp,
3779 PatternRewriter &rewriter)
const override {
3780 if (padOp.getNofold()) {
3784 auto producerPad = padOp.getSource().getDefiningOp<tensor::PadOp>();
3785 if (!producerPad || producerPad.getNofold()) {
3787 padOp,
"producer is not a foldable tensor.pad op");
3791 Value consumerPadValue = padOp.getConstantPaddingValue();
3792 Value producerPadValue = producerPad.getConstantPaddingValue();
3793 if (!consumerPadValue || !producerPadValue ||
3794 consumerPadValue != producerPadValue) {
3797 "cannot fold PadOps with different or non-constant padding values");
3800 Location loc = padOp.getLoc();
3805 auto addPaddings = [&](ArrayRef<OpFoldResult> consumerPaddings,
3806 ArrayRef<OpFoldResult> producerPaddings) {
3807 SmallVector<OpFoldResult> sumPaddings;
3808 for (
auto [consumerIndex, producerIndex] :
3809 llvm::zip_equal(consumerPaddings, producerPaddings)) {
3811 rewriter, loc, d0 + d1, {consumerIndex, producerIndex}));
3816 SmallVector<OpFoldResult> newHighPad =
3817 addPaddings(padOp.getMixedHighPad(), producerPad.getMixedHighPad());
3818 SmallVector<OpFoldResult> newLowPad =
3819 addPaddings(padOp.getMixedLowPad(), producerPad.getMixedLowPad());
3821 auto newPadOp = tensor::PadOp::create(
3822 rewriter, padOp.getLoc(), padOp.getResultType(),
3823 producerPad.getSource(), newLowPad, newHighPad, padOp.getNofold(),
3826 newPadOp.getRegion().begin());
3827 rewriter.
replaceOp(padOp, newPadOp.getResult());
3835PadOp::reifyResultShapes(OpBuilder &
b,
3837 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
3838 SmallVector<OpFoldResult> lp = getMixedLowPad();
3839 SmallVector<OpFoldResult> hp = getMixedHighPad();
3840 for (int64_t i = 0; i < getResultType().getRank(); ++i) {
3841 if (!
getType().isDynamicDim(i)) {
3842 reifiedReturnShapes[0][i] =
b.getIndexAttr(
getType().getDimSize(i));
3845 Location loc = getLoc();
3846 Value dim =
b.createOrFold<tensor::DimOp>(
3849 AffineExpr d0, d1, d2;
3852 b, loc, {d0 + d1 + d2}, {dim, lp[i], hp[i]});
3857void PadOp::getCanonicalizationPatterns(RewritePatternSet &results,
3858 MLIRContext *context) {
3859 results.
add<FoldStaticZeroPadding, FoldSourceTensorCast, FoldTargetTensorCast,
3860 FoldOrthogonalPaddings, FoldStaticPadding,
3861 FoldConsecutiveConstantPadding>(context);
3873Value PadOp::getConstantPaddingValue() {
3874 auto yieldOp = dyn_cast<YieldOp>(getRegion().front().getTerminator());
3877 Value padValue = yieldOp.getValue();
3888OpFoldResult PadOp::fold(FoldAdaptor) {
3889 if (getResultType().hasStaticShape() && getResultType() == getSourceType() &&
3899OpResult ParallelInsertSliceOp::getTiedOpResult() {
3900 InParallelOpInterface parallelCombiningParent = getParallelCombiningParent();
3901 for (
const auto &it :
3902 llvm::enumerate(parallelCombiningParent.getYieldingOps())) {
3903 Operation &nextOp = it.value();
3904 if (&nextOp == getOperation())
3905 return parallelCombiningParent.getParentResult(it.index());
3907 llvm_unreachable(
"ParallelInsertSliceOp no tied OpResult found");
3911void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3912 Value source, Value dest,
3913 ArrayRef<OpFoldResult> offsets,
3914 ArrayRef<OpFoldResult> sizes,
3915 ArrayRef<OpFoldResult> strides,
3916 ArrayRef<NamedAttribute> attrs) {
3917 SmallVector<int64_t> staticOffsets, staticSizes, staticStrides;
3918 SmallVector<Value> dynamicOffsets, dynamicSizes, dynamicStrides;
3922 result.addAttributes(attrs);
3923 build(
b,
result, {}, source, dest, dynamicOffsets, dynamicSizes,
3924 dynamicStrides,
b.getDenseI64ArrayAttr(staticOffsets),
3925 b.getDenseI64ArrayAttr(staticSizes),
3926 b.getDenseI64ArrayAttr(staticStrides));
3931void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3932 Value source, Value dest,
3933 ArrayRef<Range> ranges,
3934 ArrayRef<NamedAttribute> attrs) {
3936 build(
b,
result, source, dest, offsets, sizes, strides, attrs);
3940void ParallelInsertSliceOp::build(OpBuilder &
b, OperationState &
result,
3941 Value source, Value dest,
ValueRange offsets,
3943 ArrayRef<NamedAttribute> attrs) {
3944 SmallVector<OpFoldResult> offsetValues = llvm::map_to_vector<4>(
3945 offsets, [](Value v) -> OpFoldResult {
return v; });
3946 SmallVector<OpFoldResult> sizeValues =
3947 llvm::map_to_vector<4>(sizes, [](Value v) -> OpFoldResult {
return v; });
3948 SmallVector<OpFoldResult> strideValues = llvm::map_to_vector<4>(
3949 strides, [](Value v) -> OpFoldResult {
return v; });
3950 build(
b,
result, source, dest, offsetValues, sizeValues, strideValues);
3955void InsertSliceOp::build(OpBuilder &
b, OperationState &
result, Value source,
3956 Value dest, ArrayRef<OpFoldResult> sizes,
3957 ArrayRef<NamedAttribute> attrs) {
3958 Attribute zeroIdxAttr =
b.getIndexAttr(0);
3959 Attribute oneIdxAttr =
b.getIndexAttr(1);
3960 SmallVector<OpFoldResult> writeStrides(sizes.size(), oneIdxAttr);
3961 SmallVector<OpFoldResult> writeOffsets(sizes.size(), zeroIdxAttr);
3962 build(
b,
result, source, dest, writeOffsets, sizes, writeStrides, attrs);
3965LogicalResult ParallelInsertSliceOp::verify() {
3966 if (!isa<InParallelOpInterface>(getOperation()->getParentOp()))
3967 return this->
emitError(
"expected InParallelOpInterface parent, got:")
3968 << *(getOperation()->getParentOp());
3971 RankedTensorType expectedType;
3974 getStaticSizes(), getStaticStrides(), &expectedType);
3981 getDestType().
getShape(), getStaticOffsets(), getStaticSizes(),
3982 getStaticStrides(),
true);
3984 return getOperation()->emitError(boundsResult.
errorMessage);
3989void ParallelInsertSliceOp::getCanonicalizationPatterns(
3990 RewritePatternSet &results, MLIRContext *context) {
3991 results.
add<InsertSliceOpConstantArgumentFolder<ParallelInsertSliceOp>,
3992 InsertSliceOpCastFolder<ParallelInsertSliceOp>,
3993 InsertSliceOpSourceCastInserter<ParallelInsertSliceOp>>(context);
3996llvm::SmallBitVector ParallelInsertSliceOp::getDroppedDims() {
4001MutableOperandRange ParallelInsertSliceOp::getUpdatedDestinations() {
4002 return getDestMutable();
4005Operation *ParallelInsertSliceOp::getIteratingParent() {
4007 if (
auto combiningOp =
4008 dyn_cast<InParallelOpInterface>(getOperation()->getParentOp()))
4009 return combiningOp->getParentOp();
4017void ScatterOp::getAsmResultNames(
4019 setNameFn(getResult(),
"scatter");
4022LogicalResult ScatterOp::verify() {
4023 int64_t destRank = getDestType().getRank();
4024 ArrayRef<int64_t> scatterDims = getScatterDims();
4026 getIndicesType().
getShape(), destRank,
4027 "scatter",
"dest")))
4031 return emitOpError(
"requires 'unique' attribute to be set");
4038 RankedTensorType expectedSourceType = GatherOp::inferResultType(
4039 getDestType(), getIndicesType(), scatterDims,
false);
4040 RankedTensorType expectedRankReducedSourceType = GatherOp::inferResultType(
4041 getDestType(), getIndicesType(), scatterDims,
true);
4042 if (getSourceType() != expectedSourceType &&
4043 getSourceType() != expectedRankReducedSourceType) {
4047 << expectedSourceType <<
" or its rank-reduced variant "
4048 << expectedRankReducedSourceType <<
" (got: " << getSourceType()
4059void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4060 Type aggregateType,
ValueRange dynamicSizes) {
4061 build(builder,
result, aggregateType, element, dynamicSizes);
4064void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4065 ArrayRef<int64_t> staticShape,
ValueRange dynamicSizes) {
4066 auto aggregateType = RankedTensorType::get(staticShape, element.
getType());
4067 build(builder,
result, aggregateType, element, dynamicSizes);
4070void SplatOp::build(OpBuilder &builder, OperationState &
result, Value element,
4071 ArrayRef<OpFoldResult> sizes) {
4072 SmallVector<int64_t> staticShape;
4073 SmallVector<Value> dynamicSizes;
4075 build(builder,
result, element, staticShape, dynamicSizes);
4078void SplatOp::getAsmResultNames(
4080 setNameFn(getResult(),
"splat");
4083LogicalResult SplatOp::verify() {
4089SplatOp::reifyResultShapes(OpBuilder &builder,
4091 reifiedReturnShapes.resize(1, SmallVector<OpFoldResult>(
getType().getRank()));
4093 for (int64_t i = 0; i <
getType().getRank(); ++i) {
4094 if (
getType().isDynamicDim(i)) {
4103OpFoldResult SplatOp::fold(FoldAdaptor adaptor) {
4104 auto constOperand = adaptor.getInput();
4105 if (!isa_and_nonnull<IntegerAttr, FloatAttr>(constOperand))
4109 if (!
getType().hasStaticShape())
4124 if (isa<InsertSliceOp>(op.getOperation()) ||
4125 isa<LoopLikeOpInterface>(op.getOperation()))
4158 isa<linalg::RelayoutOpInterface>(*op))
4166 auto newOp =
clone(rewriter, op, newResultTypes, newOperands);
4169 replacements.reserve(newOp->getNumResults());
4170 for (
auto [oldResult, newResult] :
4171 llvm::zip(op->getResults(), newOp->getResults())) {
4172 if (newResult.getType() != oldResult.getType()) {
4173 replacements.push_back(tensor::CastOp::create(
4174 rewriter, op->getLoc(), oldResult.
getType(), newResult));
4176 replacements.push_back(newResult);
4189void TensorDialect::getCanonicalizationPatterns(
4190 RewritePatternSet &results)
const {
4198#define GET_OP_CLASSES
4199#include "mlir/Dialect/Tensor/IR/TensorOps.cpp.inc"
p<< " : "<< getMemRefType()<< ", "<< getType();}static LogicalResult verifyVectorMemoryOp(Operation *op, MemRefType memrefType, VectorType vectorType) { if(memrefType.getElementType() !=vectorType.getElementType()) return op-> emitOpError("requires memref and vector types of the same elemental type")
Given a list of lists of parsed operands, populates uniqueOperands with unique operands.
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
static int64_t getNumElements(Type t)
Compute the total number of elements in the given type, also taking into account nested types.
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
static void getDynamicSizes(RankedTensorType tp, ValueRange sizes, SmallVectorImpl< Value > &dynSizes)
Collects the dynamic dimension sizes for tp with the assumption that sizes are the dimension sizes fo...
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
static TensorType joinShapes(TensorType one, TensorType two)
Compute a TensorType that has the joined shape knowledge of the two given TensorTypes.
static Value foldExtractAfterInsert(ExtractOp extractOp)
If we have an ExtractOp consuming an InsertOp with the same indices, we can return the InsertOp's sca...
static LogicalResult verifyGatherOrScatterDims(Operation *op, ArrayRef< int64_t > dims, ArrayRef< int64_t > indices, int64_t rank, StringRef gatherOrScatter, StringRef sourceOrDest)
static LogicalResult produceSliceErrorMsg(SliceVerificationResult result, Operation *op, RankedTensorType expectedType)
static bool foldTensorCastPrecondition(DestinationStyleOpInterface op)
static LogicalResult foldInsertAfterInsertSlice(InsertSliceOp insertOp)
If we have two consecutive InsertSliceOp writing to the same slice, we can mutate the second InsertSl...
static Attribute propagateEncoding(Attribute encoding, ArrayRef< int64_t > shape, Type elementType)
Implements the VerifiableTensorEncoding contract documented in TensorEncoding.td for patterns that re...
static LogicalResult foldIdentityOffsetSizeAndStrideOpInterface(OffsetSizeAndStrideOpInterface op, ShapedType shapedType)
static Value foldExtractAfterInsertSlice(ExtractSliceOp extractOp)
If we have an ExtractSliceOp consuming an InsertSliceOp with the same slice, we can return the Insert...
static SliceVerificationResult verifyInsertSliceOp(RankedTensorType srcType, RankedTensorType dstType, ArrayRef< int64_t > staticOffsets, ArrayRef< int64_t > staticSizes, ArrayRef< int64_t > staticStrides, RankedTensorType *expectedType=nullptr)
Rank-reducing type verification for both InsertSliceOp and ParallelInsertSliceOp.
static RankedTensorType foldDynamicToStaticDimSizes(RankedTensorType type, ValueRange dynamicSizes, SmallVector< Value > &foldedDynamicSizes)
Given a ranked tensor type and a range of values that defines its dynamic dimension sizes,...
static llvm::SmallBitVector getDroppedDims(ArrayRef< int64_t > reducedShape, ArrayRef< OpFoldResult > mixedSizes)
Compute the dropped dimensions of a rank-reducing tensor.extract_slice op or rank-extending tensor....
static Value foldInsertAfterExtractSlice(InsertSliceOp insertOp)
Folds round-trip extract/insert slice op pairs.
static LogicalResult verifyTensorReshapeOp(TensorReshapeOp op, RankedTensorType expandedType, RankedTensorType collapsedType)
static ArrayRef< int64_t > getShape(Type type)
Returns the shape of the given type.
Base type for affine expression.
Attributes are known-constant values of operations.
MLIRContext * getContext() const
Return the context this attribute belongs to.
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
unsigned getNumArguments()
Operation * getTerminator()
Get the terminator operation of this block.
BlockArgListType getArguments()
iterator_range< iterator > without_terminator()
Return an iterator range over the operation within this block excluding the terminator operation at t...
This class is a general helper class for creating context-global objects like types,...
IntegerAttr getIndexAttr(int64_t value)
AffineExpr getAffineSymbolExpr(unsigned position)
Ty getType(Args &&...args)
Get or construct an instance of the type Ty with provided arguments.
AffineExpr getAffineDimExpr(unsigned position)
AffineMap getConstantAffineMap(int64_t val)
Returns a single constant result affine map with 0 dimensions and 0 symbols.
MLIRContext * getContext() const
auto value_begin() const
Get an iterator of the given type to the start of the held element values.
static DenseElementsAttr getFromRawBuffer(ShapedType type, ArrayRef< char > rawBuffer)
Construct a dense elements attribute from a raw buffer representing the data for this attribute.
bool isSplat() const
Returns true if this attribute corresponds to a splat, i.e.
ArrayRef< char > getRawData() const
Return the raw storage data held by this attribute.
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
This class contains all of the information necessary to report a diagnostic to the DiagnosticEngine.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
Operation * clone(Operation &op, IRMapping &mapper)
Creates a deep copy of the specified operation, remapping any operands that use values outside of the...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
This class represents a single result from folding an operation.
This class represents an operand of an operation.
This is a value defined by a result of an operation.
unsigned getResultNumber() const
Returns the number of this result.
Operation is the basic unit of execution within MLIR.
MutableArrayRef< OpOperand > getOpOperands()
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
result_range getResults()
InFlightDiagnostic emitOpError(const Twine &message={})
Emit an error with the op name prefixed, like "'dim' op " which is convenient for verifiers.
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
void inlineRegionBefore(Region ®ion, Region &parent, Region::iterator before)
Move the blocks that belong to "region" before the given position in another region "parent".
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
This diagnostic handler is a simple RAII class that registers and erases a diagnostic handler on a gi...
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
bool hasRank() const
Returns if this type is ranked, i.e. it has a known number of dimensions.
Type getElementType() const
Returns the element type of this tensor type.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
bool isIntOrIndexOrFloat() const
Return true if this is an integer (of any signedness), index, or float type.
This class provides an abstraction over the different types of ranges over Values.
type_range getType() const
type_range getTypes() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Block * getParentBlock()
Return the Block in which this Value is defined.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Speculatability
This enum is returned from the getSpeculatability method in the ConditionallySpeculatable op interfac...
constexpr auto Speculatable
constexpr auto NotSpeculatable
OpFoldResult makeComposedFoldedAffineApply(OpBuilder &b, Location loc, AffineMap map, ArrayRef< OpFoldResult > operands, bool composeAffineMin=false)
Constructs an AffineApplyOp that applies map to operands after composing the map with the maps of any...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
ConstantIntRanges inferShapedDimOpInterface(ShapedDimOpInterface op, const IntegerValueRange &maybeDim)
Returns the integer range for the result of a ShapedDimOpInterface given the optional inferred ranges...
Operation::operand_range getIndices(Operation *op)
Get the indices that the given load/store operation is operating on.
DynamicAPInt getIndex(const ConeV &cone)
Get the index of a cone, i.e., the volume of the parallelepiped spanned by its generators,...
Value constantIndex(OpBuilder &builder, Location loc, int64_t i)
Generates a constant of index type.
LogicalResult foldTensorCast(Operation *op)
Performs folding of any operand of op if it comes from a tensor::CastOp that can be folded.
bool hasFoldableTensorCastOperand(Operation *op)
Return true if any of the operands of op is a CastOp that can be folded into its consumer,...
void populateFoldConstantExtractSlicePatterns(RewritePatternSet &patterns, const ControlConstantExtractSliceFusionFn &controlFn=[](ExtractSliceOp op) { return false;})
Patterns to fold the extract slice op with its constant operand.
bool canFoldIntoProducerOp(CastOp castOp)
Determines whether the tensor::CastOp casts to a more static version of the source tensor.
SmallVector< Value > getUpdatedOperandsAfterCastOpFolding(DestinationStyleOpInterface op, SmallVector< Type > &newResTy)
Assuming that op contains at least one operand that is a foldable CastOp (i.e.
bool canFoldIntoConsumerOp(CastOp castOp)
Determines whether tensor::CastOp casts to a more dynamic version of the source tensor.
Value createCanonicalRankReducingInsertSliceOp(OpBuilder &b, Location loc, Value tensor, Value dest)
Create a rank-reducing InsertSliceOp @[0 .
Value createCanonicalRankReducingExtractSliceOp(OpBuilder &b, Location loc, Value tensor, RankedTensorType targetType)
Create a rank-reducing ExtractSliceOp @[0 .
bool isSameTypeWithoutEncoding(Type tp1, Type tp2)
Tests if types are the same when ignoring encoding on ranked tensors.
RankedTensorType inferSliceType(RankedTensorType sourceTensorType, ArrayRef< int64_t > staticSizes, const llvm::SmallBitVector &droppedDims)
Infer a slice type for the given sizes and exact dropped-dimension mask.
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
void populateFoldCollapseExtractPatterns(RewritePatternSet &patterns)
Patterns to fold extracts of a collapse_shaped tensor to an extract of the source tensor.
FailureOr< Value > getOrCreateDestination(OpBuilder &b, Location loc, OpResult opResult)
This is a helper function for DestinationStyleOpInterface.
bool preservesStaticInformation(Type source, Type target)
Returns true if target is a ranked tensor type that preserves static information available in the sou...
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
LogicalResult getOrCreateDestinations(OpBuilder &b, Location loc, Operation *op, SmallVector< Value > &result)
This is a helper function for DestinationStyleOpInterface.
std::function< bool(ExtractSliceOp)> ControlConstantExtractSliceFusionFn
Function to control the folding of constant and extract slice.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
SmallVector< OpFoldResult > getMixedValues(ArrayRef< int64_t > staticValues, ValueRange dynamicValues, MLIRContext *context)
Return a vector of OpFoldResults with the same size a staticValues, but all elements for which Shaped...
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
OpFoldResult getAsIndexOpFoldResult(MLIRContext *ctx, int64_t val)
Convert int64_t to integer attributes of index type and return them as OpFoldResult.
std::tuple< SmallVector< OpFoldResult >, SmallVector< OpFoldResult >, SmallVector< OpFoldResult > > getOffsetsSizesAndStrides(ArrayRef< Range > ranges)
Given an array of Range values, return a tuple of (offset vector, sizes vector, and strides vector) f...
SliceVerificationResult
Enum that captures information related to verifier error conditions on slice insert/extract type of o...
detail::DenseArrayAttrImpl< int64_t > DenseI64ArrayAttr
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
LogicalResult reifyResultShapes(OpBuilder &b, Operation *op, ReifiedRankedShapedTypeDims &reifiedReturnShapes)
Reify the shape of the result of an operation (typically in terms of the shape of its operands).
LogicalResult foldDynamicStrideList(SmallVectorImpl< OpFoldResult > &strides)
Returns "success" when any of the elements in strides is a constant value.
llvm::function_ref< void(Value, const IntegerValueRange &)> SetIntLatticeFn
Similar to SetIntRangeFn, but operating on IntegerValueRange lattice values.
SliceBoundsVerificationResult verifyInBoundsSlice(ArrayRef< int64_t > shape, ArrayRef< int64_t > staticOffsets, ArrayRef< int64_t > staticSizes, ArrayRef< int64_t > staticStrides, bool generateErrorMessage=false)
Verify that the offsets/sizes/strides-style access into the given shape is in-bounds.
LogicalResult verifyDynamicDimensionCount(Operation *op, ShapedType type, ValueRange dynamicSizes)
Verify that the number of dynamic size operands matches the number of dynamic dimensions in the shape...
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
SmallVector< int64_t > delinearize(int64_t linearIndex, ArrayRef< int64_t > strides)
Given the strides together with a linear index in the dimension space, return the vector-space offset...
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
SmallVector< AffineMap, 4 > getSymbolLessAffineMaps(ArrayRef< ReassociationExprs > reassociation)
Constructs affine maps out of Array<Array<AffineExpr>>.
OpFoldResult foldReshapeOp(ReshapeOpTy reshapeOp, ArrayRef< Attribute > operands)
bool hasValidSizesOffsets(SmallVector< int64_t > sizesOrOffsets)
Helper function to check whether the passed in sizes or offsets are valid.
bool wouldOpBeTriviallyDead(Operation *op)
Return true if the given operation would be dead if unused, and has no side effects on memory that wo...
SmallVector< SmallVector< OpFoldResult > > ReifiedRankedShapedTypeDims
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.
bool isZeroInteger(OpFoldResult v)
Return "true" if v is an integer value/attribute with constant value 0.
void dispatchIndexOpFoldResults(ArrayRef< OpFoldResult > ofrs, SmallVectorImpl< Value > &dynamicVec, SmallVectorImpl< int64_t > &staticVec)
Helper function to dispatch multiple OpFoldResults according to the behavior of dispatchIndexOpFoldRe...
SmallVector< SmallVector< AffineExpr, 2 >, 2 > convertReassociationIndicesToExprs(MLIRContext *context, ArrayRef< ReassociationIndices > reassociationIndices)
Convert reassociation indices to affine expressions.
bool isReassociationValid(ArrayRef< AffineMap > reassociation, int *invalidIndex=nullptr)
Return true if the reassociation specification is valid, false otherwise.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
std::optional< SmallVector< OpFoldResult > > inferExpandShapeOutputShape(OpBuilder &b, Location loc, ShapedType expandedType, ArrayRef< ReassociationIndices > reassociation, ArrayRef< OpFoldResult > inputShape)
Infer the output shape for a {memref|tensor}.expand_shape when it is possible to do so.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
OpFoldResult getAsOpFoldResult(Value val)
Given a value, try to extract a constant Attribute.
std::optional< llvm::SmallDenseSet< unsigned > > computeRankReductionMask(ArrayRef< int64_t > originalShape, ArrayRef< int64_t > reducedShape, bool matchDynamic=false)
Given an originalShape and a reducedShape assumed to be a subset of originalShape with some 1 entries...
LogicalResult verifyCompatibleShape(ArrayRef< int64_t > shape1, ArrayRef< int64_t > shape2)
Returns success if the given two shapes are compatible.
SmallVector< int64_t, 2 > ReassociationIndices
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
SliceVerificationResult isRankReducedType(ShapedType originalType, ShapedType candidateReducedType)
Check if originalType can be rank reduced to candidateReducedType type by dropping some dimensions wi...
ArrayAttr getReassociationIndicesAttribute(Builder &b, ArrayRef< ReassociationIndices > reassociation)
Wraps a list of reassociations in an ArrayAttr.
llvm::function_ref< Fn > function_ref
SmallVector< NamedAttribute > getPrunedAttributeList(Operation *op, ArrayRef< StringRef > elidedAttrs)
LogicalResult foldDynamicOffsetSizeList(SmallVectorImpl< OpFoldResult > &offsetsOrSizes)
Returns "success" when any of the elements in offsetsOrSizes is a constant value.
std::pair< SmallVector< int64_t >, SmallVector< Value > > decomposeMixedValues(ArrayRef< OpFoldResult > mixedValues)
Decompose a vector of mixed static or dynamic values into the corresponding pair of arrays.
Folds a tensor.cast op into a consuming DestinationStyleOpInterface op if the tensor....
LogicalResult matchAndRewrite(DestinationStyleOpInterface op, PatternRewriter &rewriter) const override
A canonicalizer wrapper to replace ExtractSliceOps.
void operator()(PatternRewriter &rewriter, ExtractSliceOp op, ExtractSliceOp newOp)
Return the canonical type of the result of an extract_slice op.
RankedTensorType operator()(ExtractSliceOp op, ArrayRef< OpFoldResult > mixedOffsets, ArrayRef< OpFoldResult > mixedSizes, ArrayRef< OpFoldResult > mixedStrides)
OpInterfaceRewritePattern(MLIRContext *context, PatternBenefit benefit=1)
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...
This represents an operation in an abstracted form, suitable for use with the builder APIs.
Idiomatic saturated operations on values like offsets, sizes, and strides.
static SaturatedInteger wrap(int64_t v)
FailureOr< SaturatedInteger > desaturate(SaturatedInteger other)
bool isValid
If set to "true", the slice bounds verification was successful.
std::string errorMessage
An error message that can be printed during op verification.