1*7d0426ddSRiver Riddle //===- ComposeSubView.cpp - Combining composed subview ops ----------------===// 2*7d0426ddSRiver Riddle // 3*7d0426ddSRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*7d0426ddSRiver Riddle // See https://llvm.org/LICENSE.txt for license information. 5*7d0426ddSRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*7d0426ddSRiver Riddle // 7*7d0426ddSRiver Riddle //===----------------------------------------------------------------------===// 8*7d0426ddSRiver Riddle // 9*7d0426ddSRiver Riddle // This file contains patterns for combining composed subview ops (i.e. subview 10*7d0426ddSRiver Riddle // of a subview becomes a single subview). 11*7d0426ddSRiver Riddle // 12*7d0426ddSRiver Riddle //===----------------------------------------------------------------------===// 13*7d0426ddSRiver Riddle 14*7d0426ddSRiver Riddle #include "mlir/Dialect/MemRef/Transforms/ComposeSubView.h" 15*7d0426ddSRiver Riddle #include "mlir/Dialect/Affine/IR/AffineOps.h" 16*7d0426ddSRiver Riddle #include "mlir/Dialect/MemRef/IR/MemRef.h" 17*7d0426ddSRiver Riddle #include "mlir/IR/BuiltinAttributes.h" 18*7d0426ddSRiver Riddle #include "mlir/IR/OpDefinition.h" 19*7d0426ddSRiver Riddle #include "mlir/IR/PatternMatch.h" 20*7d0426ddSRiver Riddle #include "mlir/Transforms/DialectConversion.h" 21*7d0426ddSRiver Riddle #include "mlir/Transforms/GreedyPatternRewriteDriver.h" 22*7d0426ddSRiver Riddle 23*7d0426ddSRiver Riddle using namespace mlir; 24*7d0426ddSRiver Riddle 25*7d0426ddSRiver Riddle namespace { 26*7d0426ddSRiver Riddle 27*7d0426ddSRiver Riddle // Replaces a subview of a subview with a single subview. Only supports subview 28*7d0426ddSRiver Riddle // ops with static sizes and static strides of 1 (both static and dynamic 29*7d0426ddSRiver Riddle // offsets are supported). 30*7d0426ddSRiver Riddle struct ComposeSubViewOpPattern : public OpRewritePattern<memref::SubViewOp> { 31*7d0426ddSRiver Riddle using OpRewritePattern::OpRewritePattern; 32*7d0426ddSRiver Riddle 33*7d0426ddSRiver Riddle LogicalResult matchAndRewrite(memref::SubViewOp op, 34*7d0426ddSRiver Riddle PatternRewriter &rewriter) const override { 35*7d0426ddSRiver Riddle // 'op' is the 'SubViewOp' we're rewriting. 'sourceOp' is the op that 36*7d0426ddSRiver Riddle // produces the input of the op we're rewriting (for 'SubViewOp' the input 37*7d0426ddSRiver Riddle // is called the "source" value). We can only combine them if both 'op' and 38*7d0426ddSRiver Riddle // 'sourceOp' are 'SubViewOp'. 39*7d0426ddSRiver Riddle auto sourceOp = op.source().getDefiningOp<memref::SubViewOp>(); 40*7d0426ddSRiver Riddle if (!sourceOp) 41*7d0426ddSRiver Riddle return failure(); 42*7d0426ddSRiver Riddle 43*7d0426ddSRiver Riddle // A 'SubViewOp' can be "rank-reducing" by eliminating dimensions of the 44*7d0426ddSRiver Riddle // output memref that are statically known to be equal to 1. We do not 45*7d0426ddSRiver Riddle // allow 'sourceOp' to be a rank-reducing subview because then our two 46*7d0426ddSRiver Riddle // 'SubViewOp's would have different numbers of offset/size/stride 47*7d0426ddSRiver Riddle // parameters (just difficult to deal with, not impossible if we end up 48*7d0426ddSRiver Riddle // needing it). 49*7d0426ddSRiver Riddle if (sourceOp.getSourceType().getRank() != sourceOp.getType().getRank()) { 50*7d0426ddSRiver Riddle return failure(); 51*7d0426ddSRiver Riddle } 52*7d0426ddSRiver Riddle 53*7d0426ddSRiver Riddle // Offsets, sizes and strides OpFoldResult for the combined 'SubViewOp'. 54*7d0426ddSRiver Riddle SmallVector<OpFoldResult> offsets, sizes, strides; 55*7d0426ddSRiver Riddle 56*7d0426ddSRiver Riddle // Because we only support input strides of 1, the output stride is also 57*7d0426ddSRiver Riddle // always 1. 58*7d0426ddSRiver Riddle if (llvm::all_of(strides, [](OpFoldResult &valueOrAttr) { 59*7d0426ddSRiver Riddle Attribute attr = valueOrAttr.dyn_cast<Attribute>(); 60*7d0426ddSRiver Riddle return attr && attr.cast<IntegerAttr>().getInt() == 1; 61*7d0426ddSRiver Riddle })) { 62*7d0426ddSRiver Riddle strides = SmallVector<OpFoldResult>(sourceOp.getMixedStrides().size(), 63*7d0426ddSRiver Riddle rewriter.getI64IntegerAttr(1)); 64*7d0426ddSRiver Riddle } else { 65*7d0426ddSRiver Riddle return failure(); 66*7d0426ddSRiver Riddle } 67*7d0426ddSRiver Riddle 68*7d0426ddSRiver Riddle // The rules for calculating the new offsets and sizes are: 69*7d0426ddSRiver Riddle // * Multiple subview offsets for a given dimension compose additively. 70*7d0426ddSRiver Riddle // ("Offset by m" followed by "Offset by n" == "Offset by m + n") 71*7d0426ddSRiver Riddle // * Multiple sizes for a given dimension compose by taking the size of the 72*7d0426ddSRiver Riddle // final subview and ignoring the rest. ("Take m values" followed by "Take 73*7d0426ddSRiver Riddle // n values" == "Take n values") This size must also be the smallest one 74*7d0426ddSRiver Riddle // by definition (a subview needs to be the same size as or smaller than 75*7d0426ddSRiver Riddle // its source along each dimension; presumably subviews that are larger 76*7d0426ddSRiver Riddle // than their sources are disallowed by validation). 77*7d0426ddSRiver Riddle for (auto it : llvm::zip(op.getMixedOffsets(), sourceOp.getMixedOffsets(), 78*7d0426ddSRiver Riddle op.getMixedSizes())) { 79*7d0426ddSRiver Riddle auto opOffset = std::get<0>(it); 80*7d0426ddSRiver Riddle auto sourceOffset = std::get<1>(it); 81*7d0426ddSRiver Riddle auto opSize = std::get<2>(it); 82*7d0426ddSRiver Riddle 83*7d0426ddSRiver Riddle // We only support static sizes. 84*7d0426ddSRiver Riddle if (opSize.is<Value>()) { 85*7d0426ddSRiver Riddle return failure(); 86*7d0426ddSRiver Riddle } 87*7d0426ddSRiver Riddle 88*7d0426ddSRiver Riddle sizes.push_back(opSize); 89*7d0426ddSRiver Riddle Attribute opOffsetAttr = opOffset.dyn_cast<Attribute>(), 90*7d0426ddSRiver Riddle sourceOffsetAttr = sourceOffset.dyn_cast<Attribute>(); 91*7d0426ddSRiver Riddle 92*7d0426ddSRiver Riddle if (opOffsetAttr && sourceOffsetAttr) { 93*7d0426ddSRiver Riddle // If both offsets are static we can simply calculate the combined 94*7d0426ddSRiver Riddle // offset statically. 95*7d0426ddSRiver Riddle offsets.push_back(rewriter.getI64IntegerAttr( 96*7d0426ddSRiver Riddle opOffsetAttr.cast<IntegerAttr>().getInt() + 97*7d0426ddSRiver Riddle sourceOffsetAttr.cast<IntegerAttr>().getInt())); 98*7d0426ddSRiver Riddle } else { 99*7d0426ddSRiver Riddle // When either offset is dynamic, we must emit an additional affine 100*7d0426ddSRiver Riddle // transformation to add the two offsets together dynamically. 101*7d0426ddSRiver Riddle AffineExpr expr = rewriter.getAffineConstantExpr(0); 102*7d0426ddSRiver Riddle SmallVector<Value> affineApplyOperands; 103*7d0426ddSRiver Riddle for (auto valueOrAttr : {opOffset, sourceOffset}) { 104*7d0426ddSRiver Riddle if (auto attr = valueOrAttr.dyn_cast<Attribute>()) { 105*7d0426ddSRiver Riddle expr = expr + attr.cast<IntegerAttr>().getInt(); 106*7d0426ddSRiver Riddle } else { 107*7d0426ddSRiver Riddle expr = 108*7d0426ddSRiver Riddle expr + rewriter.getAffineSymbolExpr(affineApplyOperands.size()); 109*7d0426ddSRiver Riddle affineApplyOperands.push_back(valueOrAttr.get<Value>()); 110*7d0426ddSRiver Riddle } 111*7d0426ddSRiver Riddle } 112*7d0426ddSRiver Riddle 113*7d0426ddSRiver Riddle AffineMap map = AffineMap::get(0, affineApplyOperands.size(), expr); 114*7d0426ddSRiver Riddle Value result = rewriter.create<AffineApplyOp>(op.getLoc(), map, 115*7d0426ddSRiver Riddle affineApplyOperands); 116*7d0426ddSRiver Riddle offsets.push_back(result); 117*7d0426ddSRiver Riddle } 118*7d0426ddSRiver Riddle } 119*7d0426ddSRiver Riddle 120*7d0426ddSRiver Riddle // This replaces 'op' but leaves 'sourceOp' alone; if it no longer has any 121*7d0426ddSRiver Riddle // uses it can be removed by a (separate) dead code elimination pass. 122*7d0426ddSRiver Riddle rewriter.replaceOpWithNewOp<memref::SubViewOp>(op, sourceOp.source(), 123*7d0426ddSRiver Riddle offsets, sizes, strides); 124*7d0426ddSRiver Riddle return success(); 125*7d0426ddSRiver Riddle } 126*7d0426ddSRiver Riddle }; 127*7d0426ddSRiver Riddle 128*7d0426ddSRiver Riddle } // namespace 129*7d0426ddSRiver Riddle 130*7d0426ddSRiver Riddle void mlir::memref::populateComposeSubViewPatterns( 131*7d0426ddSRiver Riddle OwningRewritePatternList &patterns, MLIRContext *context) { 132*7d0426ddSRiver Riddle patterns.insert<ComposeSubViewOpPattern>(context); 133*7d0426ddSRiver Riddle } 134