blob: f6bb9d4f5165938666dd2b51b3d3477868ca8c96 [file] [log] [blame]
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -08001//===- VectorAnalysis.cpp - Analysis for Vectorization --------------------===//
2//
3// Copyright 2019 The MLIR Authors.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16// =============================================================================
17
18#include "mlir/Analysis/VectorAnalysis.h"
19#include "mlir/IR/BuiltinOps.h"
20#include "mlir/IR/Statements.h"
21#include "mlir/Support/Functional.h"
22#include "mlir/Support/STLExtras.h"
23
24///
25/// Implements Analysis functions specific to vectors which support
26/// the vectorization and vectorization materialization passes.
27///
28
29using namespace mlir;
30
31bool mlir::isaVectorTransferRead(const OperationStmt &stmt) {
32 return stmt.getName().getStringRef().str() == kVectorTransferReadOpName;
33}
34
35bool mlir::isaVectorTransferWrite(const OperationStmt &stmt) {
36 return stmt.getName().getStringRef().str() == kVectorTransferWriteOpName;
37}
38
39Optional<SmallVector<unsigned, 4>> mlir::shapeRatio(ArrayRef<int> superShape,
40 ArrayRef<int> subShape) {
41 if (superShape.size() < subShape.size()) {
42 return Optional<SmallVector<unsigned, 4>>();
43 }
44
45 // Starting from the end, compute the integer divisors.
46 // Set the boolean `divides` if integral division is not possible.
47 std::vector<unsigned> result;
48 result.reserve(superShape.size());
49 bool divides = true;
50 auto divide = [&divides, &result](int superSize, int subSize) {
51 assert(superSize > 0 && "superSize must be > 0");
52 assert(subSize > 0 && "subSize must be > 0");
53 divides &= (superSize % subSize == 0);
54 result.push_back(superSize / subSize);
55 };
56 functional::zip(divide,
57 SmallVector<int, 8>{superShape.rbegin(), superShape.rend()},
58 SmallVector<int, 8>{subShape.rbegin(), subShape.rend()});
59
60 // If integral division does not occur, return and let the caller decide.
61 if (!divides) {
62 return Optional<SmallVector<unsigned, 4>>();
63 }
64
65 // At this point we computed the multiplicity (in reverse) for the common
66 // size. Fill with the remaining entries from the super-vector shape (still in
67 // reverse).
68 int commonSize = subShape.size();
69 std::copy(superShape.rbegin() + commonSize, superShape.rend(),
70 std::back_inserter(result));
71
72 assert(result.size() == superShape.size() &&
73 "multiplicity must be of the same size as the super-vector rank");
74
75 // Reverse again to get it back in the proper order and return.
76 return SmallVector<unsigned, 4>{result.rbegin(), result.rend()};
77}
78
79Optional<SmallVector<unsigned, 4>> mlir::shapeRatio(VectorType superVectorType,
80 VectorType subVectorType) {
81 assert(superVectorType.getElementType() == subVectorType.getElementType() &&
82 "NYI: vector types must be of the same elemental type");
83 assert(superVectorType.getElementType() ==
84 Type::getF32(superVectorType.getContext()) &&
85 "Only f32 supported for now");
86 return shapeRatio(superVectorType.getShape(), subVectorType.getShape());
87}
88
89/// Matches vector_transfer_read, vector_transfer_write and ops that return a
90/// vector type that is at least a 2-multiple of the sub-vector type size.
91/// This allows leaving other vector types in the function untouched and avoids
92/// interfering with operations on those.
93/// This is a first approximation, it can easily be extended in the future.
94/// TODO(ntv): this could all be much simpler if we added a bit that a vector
95/// type to mark that a vector is a strict super-vector but it is not strictly
96/// needed so let's avoid adding even 1 extra bit in the IR for now.
97bool mlir::matcher::operatesOnStrictSuperVectors(const OperationStmt &opStmt,
98 VectorType subVectorType) {
99 // First, extract the vector type and ditinguish between:
100 // a. ops that *must* lower a super-vector (i.e. vector_transfer_read,
101 // vector_transfer_write); and
102 // b. ops that *may* lower a super-vector (all other ops).
103 // The ops that *may* lower a super-vector only do so if the vector size is
104 // an integer multiple of the HW vector size, with multiplicity 1.
105 // The ops that *must* lower a super-vector are explicitly checked for this
106 // property.
107 /// TODO(ntv): there should be a single function for all ops to do this so we
108 /// do not have to special case. Maybe a trait, or just a method, unclear atm.
109 bool mustDivide = false;
110 VectorType superVectorType;
111 if (isaVectorTransferRead(opStmt)) {
112 superVectorType = opStmt.getResult(0)->getType().cast<VectorType>();
113 mustDivide = true;
114 } else if (isaVectorTransferWrite(opStmt)) {
115 // TODO(ntv): if vector_transfer_write had store-like semantics we could
116 // have written something similar to:
117 // auto store = storeOp->cast<StoreOp>();
118 // auto *value = store->getValueToStore();
119 superVectorType = opStmt.getOperand(0)->getType().cast<VectorType>();
120 mustDivide = true;
121 } else if (opStmt.getNumResults() == 0) {
122 assert(opStmt.dyn_cast<ReturnOp>() &&
123 "NYI: assuming only return statements can have 0 results at this "
124 "point");
125 return false;
126 } else if (opStmt.getNumResults() == 1) {
127 if (auto v = opStmt.getResult(0)->getType().dyn_cast<VectorType>()) {
128 superVectorType = v;
129 } else {
130 // Not a vector type.
131 return false;
132 }
133 } else {
134 // Not a vector_transfer and has more than 1 result, fail hard for now to
135 // wake us up when something changes.
136 assert(false && "NYI: statement has more than 1 result");
137 return false;
138 }
139
140 // Get the multiplicity.
141 auto multiplicity = shapeRatio(superVectorType, subVectorType);
142
143 // Sanity check.
144 assert((multiplicity.hasValue() || !mustDivide) &&
145 "NYI: vector_transfer instruction in which super-vector size is not an"
146 " integer multiple of sub-vector size");
147
148 // This catches cases that are not strictly necessary to have multiplicity but
149 // still aren't divisible by the sub-vector shape.
150 // This could be useful information if we wanted to reshape at the level of
151 // the vector type (but we would have to look at the compute and distinguish
152 // between parallel, reduction and possibly other cases.
153 if (!multiplicity.hasValue()) {
154 return false;
155 }
156
157 // A strict super-vector is at least 2 sub-vectors.
158 for (auto m : *multiplicity) {
159 if (m > 1) {
160 return true;
161 }
162 }
163
164 // Not a strict super-vector.
165 return false;
166}