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