blob: ebddaff61701d63b46468a8f3a87ad5a7ea8f65a [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"
Nicolas Vasilache787a93c2018-12-06 11:37:25 -080019#include "mlir/Analysis/LoopAnalysis.h"
20#include "mlir/IR/Builders.h"
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080021#include "mlir/IR/BuiltinOps.h"
22#include "mlir/IR/Statements.h"
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -080023#include "mlir/StandardOps/StandardOps.h"
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080024#include "mlir/Support/Functional.h"
25#include "mlir/Support/STLExtras.h"
26
Nicolas Vasilache787a93c2018-12-06 11:37:25 -080027#include "llvm/ADT/DenseSet.h"
28#include "llvm/ADT/SetVector.h"
29
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080030///
31/// Implements Analysis functions specific to vectors which support
32/// the vectorization and vectorization materialization passes.
33///
34
35using namespace mlir;
36
Nicolas Vasilache787a93c2018-12-06 11:37:25 -080037using llvm::SetVector;
38
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080039Optional<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 };
Nicolas Vasilacheb8863072018-11-21 12:34:10 -080056 functional::zipApply(
57 divide, SmallVector<int, 8>{superShape.rbegin(), superShape.rend()},
58 SmallVector<int, 8>{subShape.rbegin(), subShape.rend()});
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080059
60 // If integral division does not occur, return and let the caller decide.
61 if (!divides) {
Nicolas Vasilacheb8863072018-11-21 12:34:10 -080062 return None;
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080063 }
64
Nicolas Vasilacheb8863072018-11-21 12:34:10 -080065 // At this point we computed the ratio (in reverse) for the common
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080066 // 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() &&
Nicolas Vasilacheb8863072018-11-21 12:34:10 -080073 "super to sub shape ratio is not of the same size as the super rank");
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080074
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");
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080083 return shapeRatio(superVectorType.getShape(), subVectorType.getShape());
84}
85
Nicolas Vasilache787a93c2018-12-06 11:37:25 -080086/// Constructs a permutation map from memref indices to vector dimension.
87///
88/// The implementation uses the knowledge of the mapping of enclosing loop to
89/// vector dimension. `enclosingLoopToVectorDim` carries this information as a
90/// map with:
91/// - keys representing "vectorized enclosing loops";
92/// - values representing the corresponding vector dimension.
93/// The algorithm traverses "vectorized enclosing loops" and extracts the
94/// at-most-one MemRef index that is invariant along said loop. This index is
95/// guaranteed to be at most one by construction: otherwise the MemRef is not
96/// vectorizable.
97/// If this invariant index is found, it is added to the permutation_map at the
98/// proper vector dimension.
99/// If no index is found to be invariant, 0 is added to the permutation_map and
100/// corresponds to a vector broadcast along that dimension.
101///
102/// Examples can be found in the documentation of `makePermutationMap`, in the
103/// header file.
104static AffineMap makePermutationMap(
105 MLIRContext *context,
106 llvm::iterator_range<Operation::operand_iterator> indices,
107 const DenseMap<ForStmt *, unsigned> &enclosingLoopToVectorDim) {
108 using functional::makePtrDynCaster;
109 using functional::map;
110 auto unwrappedIndices = map(makePtrDynCaster<SSAValue, MLValue>(), indices);
111 SmallVector<AffineExpr, 4> perm(enclosingLoopToVectorDim.size(),
112 getAffineConstantExpr(0, context));
113 for (auto kvp : enclosingLoopToVectorDim) {
114 assert(kvp.second < perm.size());
115 auto invariants = getInvariantAccesses(*kvp.first, unwrappedIndices);
116 unsigned numIndices = unwrappedIndices.size();
117 unsigned countInvariantIndices = 0;
118 for (unsigned dim = 0; dim < numIndices; ++dim) {
119 if (!invariants.count(unwrappedIndices[dim])) {
120 assert(perm[kvp.second] == getAffineConstantExpr(0, context) &&
121 "permutationMap already has an entry along dim");
122 perm[kvp.second] = getAffineDimExpr(dim, context);
123 } else {
124 ++countInvariantIndices;
125 }
126 }
127 assert((countInvariantIndices == numIndices ||
128 countInvariantIndices == numIndices - 1) &&
129 "Vectorization prerequisite violated: at most 1 index may be "
130 "invariant wrt a vectorized loop");
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -0800131 }
Nicolas Vasilache787a93c2018-12-06 11:37:25 -0800132 return AffineMap::get(unwrappedIndices.size(), 0, perm, {});
133}
134
135/// Implementation detail that walks up the parents and records the ones with
136/// the specified type.
137/// TODO(ntv): could also be implemented as a collect parents followed by a
138/// filter and made available outside this file.
139template <typename T> static SetVector<T *> getParentsOfType(Statement *stmt) {
140 SetVector<T *> res;
141 auto *current = stmt;
142 while (auto *parent = current->getParentStmt()) {
143 auto *typedParent = dyn_cast<T>(parent);
144 if (typedParent) {
145 assert(res.count(typedParent) == 0 && "Already inserted");
146 res.insert(typedParent);
147 }
148 current = parent;
149 }
150 return res;
151}
152
153/// Returns the enclosing ForStmt, from closest to farthest.
154static SetVector<ForStmt *> getEnclosingForStmts(Statement *stmt) {
155 return getParentsOfType<ForStmt>(stmt);
156}
157
158AffineMap
159mlir::makePermutationMap(OperationStmt *opStmt,
160 const DenseMap<ForStmt *, unsigned> &loopToVectorDim) {
161 DenseMap<ForStmt *, unsigned> enclosingLoopToVectorDim;
162 auto enclosingLoops = getEnclosingForStmts(opStmt);
163 for (auto *forStmt : enclosingLoops) {
164 auto it = loopToVectorDim.find(forStmt);
165 if (it != loopToVectorDim.end()) {
166 enclosingLoopToVectorDim.insert(*it);
167 }
168 }
169
170 if (auto load = opStmt->dyn_cast<LoadOp>()) {
171 return ::makePermutationMap(opStmt->getContext(), load->getIndices(),
172 enclosingLoopToVectorDim);
173 }
174
175 auto store = opStmt->cast<StoreOp>();
176 return ::makePermutationMap(opStmt->getContext(), store->getIndices(),
177 enclosingLoopToVectorDim);
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -0800178}
179
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800180bool mlir::matcher::operatesOnStrictSuperVectors(const OperationStmt &opStmt,
181 VectorType subVectorType) {
182 // First, extract the vector type and ditinguish between:
183 // a. ops that *must* lower a super-vector (i.e. vector_transfer_read,
184 // vector_transfer_write); and
185 // b. ops that *may* lower a super-vector (all other ops).
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800186 // The ops that *may* lower a super-vector only do so if the super-vector to
187 // sub-vector ratio is striclty greater than 1. The ops that *must* lower a
188 // super-vector are explicitly checked for this property.
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800189 /// TODO(ntv): there should be a single function for all ops to do this so we
190 /// do not have to special case. Maybe a trait, or just a method, unclear atm.
191 bool mustDivide = false;
192 VectorType superVectorType;
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -0800193 if (auto read = opStmt.dyn_cast<VectorTransferReadOp>()) {
194 superVectorType = read->getResultType();
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800195 mustDivide = true;
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -0800196 } else if (auto write = opStmt.dyn_cast<VectorTransferWriteOp>()) {
197 superVectorType = write->getVectorType();
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800198 mustDivide = true;
199 } else if (opStmt.getNumResults() == 0) {
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800200 assert(opStmt.isa<ReturnOp>() &&
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800201 "NYI: assuming only return statements can have 0 results at this "
202 "point");
203 return false;
204 } else if (opStmt.getNumResults() == 1) {
205 if (auto v = opStmt.getResult(0)->getType().dyn_cast<VectorType>()) {
206 superVectorType = v;
207 } else {
208 // Not a vector type.
209 return false;
210 }
211 } else {
212 // Not a vector_transfer and has more than 1 result, fail hard for now to
213 // wake us up when something changes.
214 assert(false && "NYI: statement has more than 1 result");
215 return false;
216 }
217
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800218 // Get the ratio.
219 auto ratio = shapeRatio(superVectorType, subVectorType);
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800220
221 // Sanity check.
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800222 assert((ratio.hasValue() || !mustDivide) &&
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800223 "NYI: vector_transfer instruction in which super-vector size is not an"
224 " integer multiple of sub-vector size");
225
226 // This catches cases that are not strictly necessary to have multiplicity but
227 // still aren't divisible by the sub-vector shape.
228 // This could be useful information if we wanted to reshape at the level of
229 // the vector type (but we would have to look at the compute and distinguish
230 // between parallel, reduction and possibly other cases.
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800231 if (!ratio.hasValue()) {
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800232 return false;
233 }
234
235 // A strict super-vector is at least 2 sub-vectors.
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800236 for (auto m : *ratio) {
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800237 if (m > 1) {
238 return true;
239 }
240 }
241
242 // Not a strict super-vector.
243 return false;
244}