//===- FoldTensorSubsetOps.cpp - Fold tensor subset ops -------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Fold tensor subset ops with producer / consumers. // //===----------------------------------------------------------------------===// #include "mlir/Dialect/Affine/IR/AffineOps.h" #include "mlir/Dialect/Affine/ViewLikeInterfaceUtils.h" #include "mlir/Dialect/Tensor/IR/Tensor.h" #include "mlir/Dialect/Tensor/Transforms/Passes.h" #include "mlir/Dialect/Tensor/Transforms/Transforms.h" #include "mlir/Dialect/Utils/IndexingUtils.h" #include "mlir/Dialect/Vector/IR/VectorOps.h" #include "mlir/IR/AffineMap.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "llvm/ADT/TypeSwitch.h" namespace mlir { namespace tensor { #define GEN_PASS_DEF_FOLDTENSORSUBSETOPS #include "mlir/Dialect/Tensor/Transforms/Passes.h.inc" } // namespace tensor } // namespace mlir using namespace mlir; static Value getTensorOperand(vector::TransferReadOp op) { return op.getSource(); } static Value getTensorOperand(tensor::InsertSliceOp op) { return op.getSource(); } //===----------------------------------------------------------------------===// // Patterns //===----------------------------------------------------------------------===// namespace { /// Merge extract_slice operation with load/transferRead operation. class TransferReadOfExtractSliceOpFolder final : public OpRewritePattern { public: using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(vector::TransferReadOp readOp, PatternRewriter &rewriter) const override; }; /// Merge insert_slice operation with store/transferWriteOp operation. class InsertSliceOfTransferWriteOpFolder final : public OpRewritePattern { public: using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(tensor::InsertSliceOp insertSliceOp, PatternRewriter &rewriter) const override; }; } // namespace template static LogicalResult preconditionsFoldExtractOrInsertWithTransferOp( RewriterBase &rewriter, XferOp xferOp, ExtractOrInsertOp extractOrInsertSliceOp) { if (xferOp.hasOutOfBoundsDim()) return rewriter.notifyMatchFailure(xferOp, "out of bounds transfer dim"); if (xferOp.getMask()) return rewriter.notifyMatchFailure(xferOp, "masked transfer"); if (!extractOrInsertSliceOp.hasUnitStride()) { return rewriter.notifyMatchFailure( xferOp, "non-1 stride insert/extract, requires keeping track of " "strides, this may result in needing to insert " "vector.insert_strided_slice/extract_strided_slice ops"); } return success(); } LogicalResult TransferReadOfExtractSliceOpFolder::matchAndRewrite( vector::TransferReadOp readOp, PatternRewriter &rewriter) const { auto extractSliceOp = getTensorOperand(readOp).getDefiningOp(); if (!extractSliceOp) return rewriter.notifyMatchFailure(readOp, "not an extract_slice"); LogicalResult preconditionResult = preconditionsFoldExtractOrInsertWithTransferOp(rewriter, readOp, extractSliceOp); if (failed(preconditionResult)) return preconditionResult; SmallVector indices(readOp.getIndices().begin(), readOp.getIndices().end()); SmallVector sourceIndices; resolveSourceIndicesOffsetsAndStrides( rewriter, readOp.getLoc(), extractSliceOp.getMixedOffsets(), extractSliceOp.getMixedStrides(), extractSliceOp.getDroppedDims(), indices, sourceIndices); rewriter.replaceOpWithNewOp( readOp, readOp.getVectorType(), extractSliceOp.getSource(), sourceIndices, AffineMapAttr::get(expandDimsToRank( readOp.getPermutationMap(), extractSliceOp.getSourceType().getRank(), extractSliceOp.getDroppedDims())), readOp.getPadding(), /*mask=*/Value(), readOp.getInBoundsAttr()); return success(); } LogicalResult InsertSliceOfTransferWriteOpFolder::matchAndRewrite( tensor::InsertSliceOp insertSliceOp, PatternRewriter &rewriter) const { auto writeOp = getTensorOperand(insertSliceOp) .template getDefiningOp(); if (!writeOp) return rewriter.notifyMatchFailure(insertSliceOp, "not a transfer_write"); LogicalResult preconditionResult = preconditionsFoldExtractOrInsertWithTransferOp(rewriter, writeOp, insertSliceOp); if (failed(preconditionResult)) return preconditionResult; SmallVector indices(writeOp.getIndices().begin(), writeOp.getIndices().end()); SmallVector sourceIndices; resolveSourceIndicesOffsetsAndStrides( rewriter, writeOp.getLoc(), insertSliceOp.getMixedOffsets(), insertSliceOp.getMixedStrides(), insertSliceOp.getDroppedDims(), indices, sourceIndices); rewriter.replaceOpWithNewOp( insertSliceOp, writeOp.getValue(), insertSliceOp.getDest(), sourceIndices, AffineMapAttr::get(expandDimsToRank(writeOp.getPermutationMap(), insertSliceOp.getDestType().getRank(), insertSliceOp.getDroppedDims())), writeOp.getInBoundsAttr()); return success(); } void tensor::populateFoldTensorSubsetOpPatterns(RewritePatternSet &patterns) { patterns.add(patterns.getContext()); } //===----------------------------------------------------------------------===// // Pass registration //===----------------------------------------------------------------------===// namespace { struct FoldTensorSubsetOpsPass final : public tensor::impl::FoldTensorSubsetOpsBase { void runOnOperation() override; }; } // namespace void FoldTensorSubsetOpsPass::runOnOperation() { RewritePatternSet patterns(&getContext()); tensor::populateFoldTensorSubsetOpPatterns(patterns); (void)applyPatternsAndFoldGreedily(getOperation(), std::move(patterns)); } std::unique_ptr tensor::createFoldTensorSubsetOpsPass() { return std::make_unique(); }