16#include "llvm/ADT/STLExtras.h"
17#include "llvm/Support/LogicalResult.h"
24struct FoldExpandOfRankReducingExtract
26 using OpRewritePattern<ExpandShapeOp>::OpRewritePattern;
28 LogicalResult matchAndRewrite(ExpandShapeOp expandShapeOp,
29 PatternRewriter &rewriter)
const override {
30 RankedTensorType resultType = expandShapeOp.getResultType();
32 expandShapeOp.getSrc().getDefiningOp<ExtractSliceOp>();
35 RankedTensorType srcType = extractSliceOp.getSourceType();
40 RankedTensorType nonReducingExtractType = ExtractSliceOp::inferResultType(
41 srcType, extractSliceOp.getStaticSizes());
42 if (nonReducingExtractType != resultType)
45 SmallVector<OpFoldResult> mixedOffsets = extractSliceOp.getMixedOffsets();
46 SmallVector<OpFoldResult> mixedSizes = extractSliceOp.getMixedSizes();
47 SmallVector<OpFoldResult> mixedStrides = extractSliceOp.getMixedStrides();
49 expandShapeOp, extractSliceOp.getSource(), mixedOffsets, mixedSizes,
57struct FoldExtractSliceOfExpandShape :
public OpRewritePattern<ExtractSliceOp> {
58 using OpRewritePattern<ExtractSliceOp>::OpRewritePattern;
60 LogicalResult matchAndRewrite(ExtractSliceOp sliceOp,
61 PatternRewriter &rewriter)
const override {
62 auto expandOp = sliceOp.getSource().getDefiningOp<ExpandShapeOp>();
66 if (sliceOp.getType() != expandOp.getSrcType())
68 sliceOp,
"slice result type does not match expand_shape source type");
70 SmallVector<OpFoldResult> mixedExpandedSizes =
71 expandOp.getMixedOutputShape();
72 if (mixedExpandedSizes.size() != sliceOp.getMixedSizes().size())
74 sliceOp,
"expand_shape output rank does not match slice rank");
76 for (
auto [offset, size, stride, expandedSize] :
77 llvm::zip_equal(sliceOp.getMixedOffsets(), sliceOp.getMixedSizes(),
78 sliceOp.getMixedStrides(), mixedExpandedSizes)) {
82 sliceOp,
"slice is not a zero-offset, unit-stride full slice");
83 if (size != expandedSize)
85 sliceOp,
"slice size does not match expand_shape output size");
88 rewriter.
replaceOp(sliceOp, expandOp.getSrc());
95struct FoldUnPaddingCollapseIntoExtract
97 using OpRewritePattern<tensor::CollapseShapeOp>::OpRewritePattern;
99 LogicalResult matchAndRewrite(tensor::CollapseShapeOp collapseShapeOp,
100 PatternRewriter &rewriter)
const override {
101 auto extractSliceOp =
102 collapseShapeOp.getSrc().getDefiningOp<tensor::ExtractSliceOp>();
106 if (!extractSliceOp || !extractSliceOp->hasOneUse())
112 collapseShapeOp.getSrcType(), collapseShapeOp.getResultType());
113 if (res != SliceVerificationResult::Success)
115 "expected unpadding collapse");
117 Value unPaddedExtractSlice = tensor::ExtractSliceOp::create(
118 rewriter, extractSliceOp.getLoc(), collapseShapeOp.getResultType(),
119 extractSliceOp.getSource(), extractSliceOp.getMixedOffsets(),
120 extractSliceOp.getMixedSizes(), extractSliceOp.getMixedStrides());
121 rewriter.
replaceOp(collapseShapeOp, unPaddedExtractSlice);
127template <
typename OpTy>
129 using OpRewritePattern<OpTy>::OpRewritePattern;
131 LogicalResult matchAndRewrite(OpTy insertSliceOp,
132 PatternRewriter &rewriter)
const override {
133 auto collapseShapeOp =
134 insertSliceOp.getSource().template getDefiningOp<CollapseShapeOp>();
135 if (!collapseShapeOp)
137 RankedTensorType srcType = collapseShapeOp.getSrcType();
142 RankedTensorType nonReducingInsertType =
143 RankedTensorType::get(insertSliceOp.getStaticSizes(),
144 insertSliceOp.getDestType().getElementType());
145 if (nonReducingInsertType != srcType)
148 SmallVector<OpFoldResult> mixedOffsets = insertSliceOp.getMixedOffsets();
149 SmallVector<OpFoldResult> mixedSizes = insertSliceOp.getMixedSizes();
150 SmallVector<OpFoldResult> mixedStrides = insertSliceOp.getMixedStrides();
152 insertSliceOp.getDest(), mixedOffsets,
153 mixedSizes, mixedStrides);
160template <
typename OpTy>
162 using OpRewritePattern<OpTy>::OpRewritePattern;
164 LogicalResult matchAndRewrite(OpTy insertSliceOp,
165 PatternRewriter &rewriter)
const override {
166 auto expandShapeOp = insertSliceOp.getSource()
167 .template getDefiningOp<tensor::ExpandShapeOp>();
174 expandShapeOp.getResultType(), expandShapeOp.getSrcType());
175 if (res != SliceVerificationResult::Success)
177 "expected rank increasing expansion");
180 insertSliceOp.getSourceMutable().assign(expandShapeOp.getSrc());
188struct BubbleUpExpandThroughParallelCollapse
190 using OpRewritePattern<tensor::ExpandShapeOp>::OpRewritePattern;
192 LogicalResult matchAndRewrite(tensor::ExpandShapeOp expandOp,
193 PatternRewriter &rewriter)
const override {
195 expandOp.getSrc().getDefiningOp<tensor::CollapseShapeOp>();
198 auto expandReInds = expandOp.getReassociationIndices();
199 auto collapseReInds = collapseOp.getReassociationIndices();
203 if (expandReInds.size() == 0) {
212 ArrayRef<int64_t> staticSourceSize = collapseOp.getSrcType().getShape();
213 ArrayRef<int64_t> staticResultSize = expandOp.getStaticOutputShape();
214 for (
auto [expandReassociation, collapseReassociation] :
215 llvm::zip_equal(expandReInds, collapseReInds)) {
216 if (collapseReassociation.size() == expandReassociation.size()) {
222 ArrayRef<int64_t> collapsedStaticShapes = staticSourceSize.slice(
223 collapseReassociation.front(), collapseReassociation.size());
224 int64_t numCollapsedDynamic =
225 llvm::count_if(collapsedStaticShapes, ShapedType::isDynamic);
226 ArrayRef<int64_t> expandedStaticShapes = staticResultSize.slice(
227 expandReassociation.front(), expandReassociation.size());
228 int64_t numExpandedDynamic =
229 llvm::count_if(expandedStaticShapes, ShapedType::isDynamic);
230 if (numCollapsedDynamic > 1 || numExpandedDynamic > 1 ||
231 collapsedStaticShapes != expandedStaticShapes) {
238 if (collapseReassociation.size() != 1 && expandReassociation.size() != 1)
243 SmallVector<ReassociationIndices> newExpandReInds, newCollapseReInds;
244 Location loc = expandOp->getLoc();
245 SmallVector<OpFoldResult> sourceSizes =
247 SmallVector<OpFoldResult> resultSizes = expandOp.getMixedOutputShape();
248 SmallVector<OpFoldResult> newExpandSizes;
250 int64_t newExpandIndex = 0, newCollapseIndex = 0, sourceSizeIndex = 0,
253 for (
size_t idx = 0, idxEnd = collapseReInds.size(); idx < idxEnd; idx++) {
254 auto &collapseReassociation = collapseReInds[idx];
255 auto &expandReassociation = expandReInds[idx];
263 if (collapseReassociation.size() == expandReassociation.size()) {
264 for (
size_t i = 0; i < collapseReassociation.size(); ++i) {
265 newCollapseReInds.push_back({newCollapseIndex++});
266 newExpandReInds.push_back({newExpandIndex++});
267 newExpandSizes.push_back(resultSizes[resultSizeIndex++]);
276 if (collapseReassociation.size() != 1) {
278 for (
size_t i = 0; i < collapseReassociation.size(); ++i) {
279 newCollapseReassociation.push_back(newCollapseIndex++);
280 newExpandReInds.push_back({newExpandIndex++});
281 newExpandSizes.push_back(sourceSizes[sourceSizeIndex++]);
284 newCollapseReInds.push_back(newCollapseReassociation);
292 for (
size_t i = 0; i < expandReassociation.size(); ++i) {
293 newExpandReassociation.push_back(newExpandIndex++);
294 newCollapseReInds.push_back({newCollapseIndex++});
295 newExpandSizes.push_back(resultSizes[resultSizeIndex++]);
297 newExpandReInds.push_back(newExpandReassociation);
302 SmallVector<Value> dynamicSizes;
303 SmallVector<int64_t> staticSizes;
305 auto expandResultType = expandOp.getResultType().clone(staticSizes);
306 Value newCollapseSrc = collapseOp.getSrc();
310 if (newExpandReInds.size() != newExpandSizes.size()) {
311 newCollapseSrc = tensor::ExpandShapeOp::create(
312 rewriter, loc, expandResultType, newCollapseSrc, newExpandReInds,
320 if (newCollapseReInds.size() != newExpandSizes.size()) {
322 rewriter, loc, newCollapseSrc, newCollapseReInds);
360struct BubbleUpExtractSliceThroughExpandShape
362 using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern;
364 LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
365 PatternRewriter &rewriter)
const override {
367 sliceOp.getSource().getDefiningOp<tensor::ExpandShapeOp>();
368 if (!expandShapeOp) {
370 sliceOp,
"tensor.extract_slice source not produced by expand_shape");
372 SmallVector<ReassociationIndices> reassociation =
373 expandShapeOp.getReassociationIndices();
375 SmallVector<OpFoldResult> offsets, sizes, strides;
377 offsets, sizes, strides)))
381 SmallVector<OpFoldResult> expandedSizes = sliceOp.getMixedSizes();
382 RankedTensorType resultType = sliceOp.getResultType();
385 Location loc = sliceOp.getLoc();
386 Value newSliceOp = tensor::ExtractSliceOp::create(
387 rewriter, loc, expandShapeOp.getSrc(), offsets, sizes, strides);
389 sliceOp, resultType, newSliceOp,
390 expandShapeOp.getReassociationIndices(), expandedSizes);
468struct BubbleUpExtractSliceThroughCollapseShape
470 using OpRewritePattern<tensor::ExtractSliceOp>::OpRewritePattern;
472 LogicalResult matchAndRewrite(tensor::ExtractSliceOp sliceOp,
473 PatternRewriter &rewriter)
const override {
474 auto collapseShapeOp =
475 sliceOp.getSource().getDefiningOp<tensor::CollapseShapeOp>();
476 if (!collapseShapeOp) {
479 "tensor.extract_slice source not produced by tensor.collapse_shape");
482 SmallVector<OpFoldResult> offsets, sizes, strides;
484 rewriter, sliceOp, collapseShapeOp.getReassociationIndices(),
485 collapseShapeOp.getSrc(), offsets, sizes, strides)))
488 Value newSliceOp = tensor::ExtractSliceOp::create(
489 rewriter, collapseShapeOp->getLoc(), collapseShapeOp.getSrc(), offsets,
492 sliceOp, sliceOp.getResultType(), newSliceOp,
493 collapseShapeOp.getReassociationIndices());
507 if (!sliceOp.hasUnitStride()) {
514 if (
static_cast<size_t>(sliceOp.getResultType().getRank()) != sizes.size()) {
523 FailureOr<bool> maybeEqual =
525 return llvm::succeeded(maybeEqual) && maybeEqual.value();
555 if (!isZeroOffsetAndFullSize(offsets[expandedDim], sizes[expandedDim],
595 for (
long expandedDim :
indices) {
599 reassocGroupSizes.push_back(expandedShape[expandedDim]);
600 reassocGroupOffsets.push_back(expandedOffsets[expandedDim]);
601 collapsedSize =
mul(collapsedSize, expandedSizes[expandedDim]);
605 llvm::map_to_vector(reassocGroupOffsets, [&](
OpFoldResult ofr) {
608 OpFoldResult collapsedOffset = affine::AffineLinearizeIndexOp::create(
609 b, loc, offsetVals, reassocGroupSizes,
612 collapsedOffsets.push_back(collapsedOffset);
613 collapsedSizes.push_back(collapsedSize);
616 collapsedStrides.push_back(
b.getIndexAttr(1));
626 if (staticValue.has_value())
627 return staticValue.value() % factor == 0;
629 Value value = dyn_cast<Value>(ofr);
654 assert(groupSizes.empty() &&
"Group sizes must be empty");
660 int nonUnitSizeCount = llvm::count_if(
661 reassocIndices, [&expandedShape](
int64_t expandedShapeIdx) {
662 return expandedShape[expandedShapeIdx] != 1;
664 if (nonUnitSizeCount == 1) {
665 for (
int64_t expandedShapeIdx : reassocIndices) {
666 if (expandedShape[expandedShapeIdx] != 1)
667 groupSizes.push_back(collapsedSize);
669 groupSizes.push_back(
b.getIndexAttr(1));
676 if (isa<Value>(collapsedSize))
680 assert(staticSize.has_value() &&
"Expected static size");
685 if (staticSize.value() == 1) {
686 for (
size_t i = 0; i < reassocIndices.size(); ++i)
687 groupSizes.push_back(
b.getIndexAttr(1));
710 assert(staticSize.value() > 1 &&
"Expected size to be greater than 1");
711 int64_t currentCollapsedsize = staticSize.value();
712 int64_t currentOffsetDivisor = 1;
715 reassocIndices.rend());
717 int64_t reassocGroupSize = reassocIndices.size();
721 for (; idx < reassocGroupSize; ++idx) {
722 int64_t expandedShapeSize = expandedShape[reversedReassocIndices[idx]];
723 if (expandedShapeSize == ShapedType::kDynamic)
726 if (currentCollapsedsize < expandedShapeSize)
730 if ((currentCollapsedsize % expandedShapeSize) != 0)
734 currentOffsetDivisor *= expandedShapeSize;
735 if (!
isMultipleOf(collapsedOffset, currentOffsetDivisor))
739 groupSizes.push_back(
b.getIndexAttr(expandedShapeSize));
740 currentCollapsedsize /= expandedShapeSize;
744 if (idx < reassocGroupSize) {
745 int64_t expandedShapeSize = expandedShape[reversedReassocIndices[idx]];
748 if (staticOffset.has_value()) {
751 (staticOffset.value() / currentOffsetDivisor) % expandedShapeSize;
752 if ((currentCollapsedsize + offsetInDim) > expandedShapeSize)
761 if ((expandedShapeSize % currentCollapsedsize) != 0)
767 groupSizes.push_back(
b.getIndexAttr(currentCollapsedsize));
777 for (idx++; idx < reassocGroupSize; ++idx)
778 groupSizes.push_back(
b.getIndexAttr(1));
781 groupSizes = llvm::to_vector(llvm::reverse(groupSizes));
791 if (!sliceOp.hasUnitStride()) {
802 if (
static_cast<size_t>(sliceOp.getResultType().getRank()) !=
803 collapsedSizes.size()) {
812 cast<RankedTensorType>(expandedValue.
getType()).getShape();
814 for (
auto [collapsedSize, collapsedOffset, reassocIndices] :
815 llvm::zip_equal(collapsedSizes, collapsedOffsets, reassociation)) {
819 b, collapsedSize, collapsedOffset, reassocIndices, expandedShape,
823 groupResults.emplace_back(groupSizes);
826 expandedStrides.resize(expandedShape.size(),
b.getIndexAttr(1));
827 for (
auto [groupIdx, reassocIndices] : llvm::enumerate(reassociation)) {
828 auto &sizes = groupResults[groupIdx];
829 expandedSizes.append(sizes);
832 for (
int64_t expandedShapeIdx : reassocIndices)
836 OpFoldResult collapsedOffset = collapsedOffsets[groupIdx];
839 auto delinearizeOp = affine::AffineDelinearizeIndexOp::create(
840 b, sliceOp.getLoc(), offsetVal, basis,
true);
842 expandedOffsets.push_back(
result);
849 patterns.
add<FoldExpandOfRankReducingExtract, FoldExtractSliceOfExpandShape,
850 FoldUnPaddingCollapseIntoExtract,
851 FoldInsertOfRankReducingInsert<tensor::InsertSliceOp>,
852 FoldInsertOfRankReducingInsert<tensor::ParallelInsertSliceOp>,
853 FoldPaddingExpandIntoInsert<tensor::InsertSliceOp>,
854 FoldPaddingExpandIntoInsert<tensor::ParallelInsertSliceOp>>(
860 patterns.
add<BubbleUpExpandThroughParallelCollapse>(patterns.
getContext());
865 patterns.
add<BubbleUpExtractSliceThroughExpandShape,
866 BubbleUpExtractSliceThroughCollapseShape>(patterns.
getContext());
*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 LogicalResult computeExpandedSliceInfoForReassocGroup(OpBuilder &b, OpFoldResult collapsedSize, OpFoldResult collapsedOffset, const ReassociationIndices &reassocIndices, ArrayRef< int64_t > expandedShape, SmallVectorImpl< OpFoldResult > &groupSizes)
Given a collapsedOffset and collapsedSize, this function validates that the slice is representable as...
static bool isMultipleOf(OpFoldResult ofr, int64_t factor)
Base type for affine expression.
bool isMultipleOf(int64_t factor) const
Return true if the affine expression is a multiple of 'factor'.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumResults() const
AffineExpr getResult(unsigned idx) const
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
This class represents a single result from folding an operation.
This is a value defined by a result of an operation.
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.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
A variable that can be added to the constraint set as a "column".
static FailureOr< bool > areEqual(const Variable &var1, const Variable &var2)
Compute whether the given variables are equal.
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.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
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...
void fullyComposeAffineMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands, bool composeAffineMin=false)
Given an affine map map and its input operands, this method composes into map, maps of AffineApplyOps...
LogicalResult getCollapsedExtractSliceInfo(OpBuilder &b, tensor::ExtractSliceOp sliceOp, ArrayRef< ReassociationIndices > reassociation, SmallVectorImpl< OpFoldResult > &collapsedOffsets, SmallVectorImpl< OpFoldResult > &collapsedSizes, SmallVectorImpl< OpFoldResult > &collapsedStrides)
Computes the offsets, sizes, and strides needed to build a collapsed sliceOp.
LogicalResult getExpandedExtractSliceInfo(OpBuilder &b, tensor::ExtractSliceOp sliceOp, ArrayRef< ReassociationIndices > reassociation, Value expandedValue, SmallVectorImpl< OpFoldResult > &expandedOffsets, SmallVectorImpl< OpFoldResult > &expandedSizes, SmallVectorImpl< OpFoldResult > &expandedStrides)
Computes the offsets, sizes, and strides needed to build an expanded sliceOp.
void populateReassociativeReshapeFoldingPatterns(RewritePatternSet &patterns)
Populates patterns with patterns that fold tensor.expand_shape and tensor.collapse_shape into other o...
void populateBubbleUpExtractSliceOpPatterns(RewritePatternSet &patterns)
Appends patterns that are used to bubble up tensor.extract slice op above its producer.
void populateBubbleUpExpandShapePatterns(RewritePatternSet &patterns)
Populates patterns with patterns that bubble up tensor.expand_shape through tensor....
OpFoldResult getMixedSize(OpBuilder &builder, Location loc, Value value, int64_t dim)
Return the dimension of the given tensor value.
SmallVector< OpFoldResult > getMixedSizes(OpBuilder &builder, Location loc, Value value)
Return the dimensions of the given tensor value.
Include the generated interface declarations.
AffineMap simplifyAffineMap(AffineMap map)
Simplifies an affine map by simplifying its underlying AffineExpr results.
SliceVerificationResult
Enum that captures information related to verifier error conditions on slice insert/extract type of o...
std::optional< int64_t > getConstantIntValue(OpFoldResult ofr)
If ofr is a constant integer or an IntegerAttr, return the integer.
void bindDims(MLIRContext *ctx, AffineExprTy &...exprs)
Bind a list of AffineExpr references to DimExpr at positions: [0 .
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...
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
SmallVector< int64_t, 2 > ReassociationIndices
SliceVerificationResult isRankReducedType(ShapedType originalType, ShapedType candidateReducedType)
Check if originalType can be rank reduced to candidateReducedType type by dropping some dimensions wi...
bool isOneInteger(OpFoldResult v)
Return true if v is an IntegerAttr with value 1.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...