blob: bfef98d76da05b090be2d26d804f87859d7f1291 [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"
Alex Zinenkof9e30b92018-12-14 09:31:17 -080024#include "mlir/SuperVectorOps/SuperVectorOps.h"
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080025#include "mlir/Support/Functional.h"
26#include "mlir/Support/STLExtras.h"
27
Nicolas Vasilache787a93c2018-12-06 11:37:25 -080028#include "llvm/ADT/DenseSet.h"
29#include "llvm/ADT/SetVector.h"
30
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080031///
32/// Implements Analysis functions specific to vectors which support
33/// the vectorization and vectorization materialization passes.
34///
35
36using namespace mlir;
37
Nicolas Vasilache787a93c2018-12-06 11:37:25 -080038using llvm::SetVector;
39
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080040Optional<SmallVector<unsigned, 4>> mlir::shapeRatio(ArrayRef<int> superShape,
41 ArrayRef<int> subShape) {
42 if (superShape.size() < subShape.size()) {
43 return Optional<SmallVector<unsigned, 4>>();
44 }
45
46 // Starting from the end, compute the integer divisors.
47 // Set the boolean `divides` if integral division is not possible.
48 std::vector<unsigned> result;
49 result.reserve(superShape.size());
50 bool divides = true;
51 auto divide = [&divides, &result](int superSize, int subSize) {
52 assert(superSize > 0 && "superSize must be > 0");
53 assert(subSize > 0 && "subSize must be > 0");
54 divides &= (superSize % subSize == 0);
55 result.push_back(superSize / subSize);
56 };
Nicolas Vasilacheb8863072018-11-21 12:34:10 -080057 functional::zipApply(
58 divide, SmallVector<int, 8>{superShape.rbegin(), superShape.rend()},
59 SmallVector<int, 8>{subShape.rbegin(), subShape.rend()});
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080060
61 // If integral division does not occur, return and let the caller decide.
62 if (!divides) {
Nicolas Vasilacheb8863072018-11-21 12:34:10 -080063 return None;
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080064 }
65
Nicolas Vasilacheb8863072018-11-21 12:34:10 -080066 // At this point we computed the ratio (in reverse) for the common
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080067 // size. Fill with the remaining entries from the super-vector shape (still in
68 // reverse).
69 int commonSize = subShape.size();
70 std::copy(superShape.rbegin() + commonSize, superShape.rend(),
71 std::back_inserter(result));
72
73 assert(result.size() == superShape.size() &&
Nicolas Vasilacheb8863072018-11-21 12:34:10 -080074 "super to sub shape ratio is not of the same size as the super rank");
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080075
76 // Reverse again to get it back in the proper order and return.
77 return SmallVector<unsigned, 4>{result.rbegin(), result.rend()};
78}
79
80Optional<SmallVector<unsigned, 4>> mlir::shapeRatio(VectorType superVectorType,
81 VectorType subVectorType) {
82 assert(superVectorType.getElementType() == subVectorType.getElementType() &&
Nicolas Vasilache2aca1812018-12-06 11:38:44 -080083 "vector types must be of the same elemental type");
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -080084 return shapeRatio(superVectorType.getShape(), subVectorType.getShape());
85}
86
Nicolas Vasilache787a93c2018-12-06 11:37:25 -080087/// Constructs a permutation map from memref indices to vector dimension.
88///
89/// The implementation uses the knowledge of the mapping of enclosing loop to
90/// vector dimension. `enclosingLoopToVectorDim` carries this information as a
91/// map with:
92/// - keys representing "vectorized enclosing loops";
93/// - values representing the corresponding vector dimension.
94/// The algorithm traverses "vectorized enclosing loops" and extracts the
95/// at-most-one MemRef index that is invariant along said loop. This index is
96/// guaranteed to be at most one by construction: otherwise the MemRef is not
97/// vectorizable.
98/// If this invariant index is found, it is added to the permutation_map at the
99/// proper vector dimension.
100/// If no index is found to be invariant, 0 is added to the permutation_map and
101/// corresponds to a vector broadcast along that dimension.
102///
103/// Examples can be found in the documentation of `makePermutationMap`, in the
104/// header file.
105static AffineMap makePermutationMap(
106 MLIRContext *context,
107 llvm::iterator_range<Operation::operand_iterator> indices,
108 const DenseMap<ForStmt *, unsigned> &enclosingLoopToVectorDim) {
109 using functional::makePtrDynCaster;
110 using functional::map;
111 auto unwrappedIndices = map(makePtrDynCaster<SSAValue, MLValue>(), indices);
112 SmallVector<AffineExpr, 4> perm(enclosingLoopToVectorDim.size(),
113 getAffineConstantExpr(0, context));
114 for (auto kvp : enclosingLoopToVectorDim) {
115 assert(kvp.second < perm.size());
116 auto invariants = getInvariantAccesses(*kvp.first, unwrappedIndices);
117 unsigned numIndices = unwrappedIndices.size();
118 unsigned countInvariantIndices = 0;
119 for (unsigned dim = 0; dim < numIndices; ++dim) {
120 if (!invariants.count(unwrappedIndices[dim])) {
121 assert(perm[kvp.second] == getAffineConstantExpr(0, context) &&
122 "permutationMap already has an entry along dim");
123 perm[kvp.second] = getAffineDimExpr(dim, context);
124 } else {
125 ++countInvariantIndices;
126 }
127 }
128 assert((countInvariantIndices == numIndices ||
129 countInvariantIndices == numIndices - 1) &&
130 "Vectorization prerequisite violated: at most 1 index may be "
131 "invariant wrt a vectorized loop");
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -0800132 }
Nicolas Vasilache787a93c2018-12-06 11:37:25 -0800133 return AffineMap::get(unwrappedIndices.size(), 0, perm, {});
134}
135
136/// Implementation detail that walks up the parents and records the ones with
137/// the specified type.
138/// TODO(ntv): could also be implemented as a collect parents followed by a
139/// filter and made available outside this file.
140template <typename T> static SetVector<T *> getParentsOfType(Statement *stmt) {
141 SetVector<T *> res;
142 auto *current = stmt;
143 while (auto *parent = current->getParentStmt()) {
144 auto *typedParent = dyn_cast<T>(parent);
145 if (typedParent) {
146 assert(res.count(typedParent) == 0 && "Already inserted");
147 res.insert(typedParent);
148 }
149 current = parent;
150 }
151 return res;
152}
153
154/// Returns the enclosing ForStmt, from closest to farthest.
155static SetVector<ForStmt *> getEnclosingForStmts(Statement *stmt) {
156 return getParentsOfType<ForStmt>(stmt);
157}
158
159AffineMap
160mlir::makePermutationMap(OperationStmt *opStmt,
161 const DenseMap<ForStmt *, unsigned> &loopToVectorDim) {
162 DenseMap<ForStmt *, unsigned> enclosingLoopToVectorDim;
163 auto enclosingLoops = getEnclosingForStmts(opStmt);
164 for (auto *forStmt : enclosingLoops) {
165 auto it = loopToVectorDim.find(forStmt);
166 if (it != loopToVectorDim.end()) {
167 enclosingLoopToVectorDim.insert(*it);
168 }
169 }
170
171 if (auto load = opStmt->dyn_cast<LoadOp>()) {
172 return ::makePermutationMap(opStmt->getContext(), load->getIndices(),
173 enclosingLoopToVectorDim);
174 }
175
176 auto store = opStmt->cast<StoreOp>();
177 return ::makePermutationMap(opStmt->getContext(), store->getIndices(),
178 enclosingLoopToVectorDim);
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -0800179}
180
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800181bool mlir::matcher::operatesOnStrictSuperVectors(const OperationStmt &opStmt,
182 VectorType subVectorType) {
183 // First, extract the vector type and ditinguish between:
184 // a. ops that *must* lower a super-vector (i.e. vector_transfer_read,
185 // vector_transfer_write); and
186 // b. ops that *may* lower a super-vector (all other ops).
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800187 // The ops that *may* lower a super-vector only do so if the super-vector to
188 // sub-vector ratio is striclty greater than 1. The ops that *must* lower a
189 // super-vector are explicitly checked for this property.
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800190 /// TODO(ntv): there should be a single function for all ops to do this so we
191 /// do not have to special case. Maybe a trait, or just a method, unclear atm.
192 bool mustDivide = false;
193 VectorType superVectorType;
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -0800194 if (auto read = opStmt.dyn_cast<VectorTransferReadOp>()) {
195 superVectorType = read->getResultType();
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800196 mustDivide = true;
Nicolas Vasilache9a19ada2018-12-03 15:21:27 -0800197 } else if (auto write = opStmt.dyn_cast<VectorTransferWriteOp>()) {
198 superVectorType = write->getVectorType();
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800199 mustDivide = true;
200 } else if (opStmt.getNumResults() == 0) {
Nicolas Vasilache2aca1812018-12-06 11:38:44 -0800201 if (!opStmt.isa<ReturnOp>()) {
202 opStmt.emitError("NYI: assuming only return statements can have 0 "
203 " results at this point");
204 }
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800205 return false;
206 } else if (opStmt.getNumResults() == 1) {
207 if (auto v = opStmt.getResult(0)->getType().dyn_cast<VectorType>()) {
208 superVectorType = v;
209 } else {
210 // Not a vector type.
211 return false;
212 }
213 } else {
214 // Not a vector_transfer and has more than 1 result, fail hard for now to
215 // wake us up when something changes.
Nicolas Vasilache2aca1812018-12-06 11:38:44 -0800216 opStmt.emitError("NYI: statement has more than 1 result");
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800217 return false;
218 }
219
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800220 // Get the ratio.
221 auto ratio = shapeRatio(superVectorType, subVectorType);
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800222
223 // Sanity check.
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800224 assert((ratio.hasValue() || !mustDivide) &&
Nicolas Vasilache2aca1812018-12-06 11:38:44 -0800225 "vector_transfer instruction in which super-vector size is not an"
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800226 " integer multiple of sub-vector size");
227
228 // This catches cases that are not strictly necessary to have multiplicity but
229 // still aren't divisible by the sub-vector shape.
230 // This could be useful information if we wanted to reshape at the level of
231 // the vector type (but we would have to look at the compute and distinguish
232 // between parallel, reduction and possibly other cases.
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800233 if (!ratio.hasValue()) {
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800234 return false;
235 }
236
237 // A strict super-vector is at least 2 sub-vectors.
Nicolas Vasilacheb8863072018-11-21 12:34:10 -0800238 for (auto m : *ratio) {
Nicolas Vasilache13b3bce2018-11-20 08:36:07 -0800239 if (m > 1) {
240 return true;
241 }
242 }
243
244 // Not a strict super-vector.
245 return false;
246}