github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/lib/Analysis/VectorAnalysis.cpp (about) 1 //===- 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/Analysis/AffineAnalysis.h" 20 #include "mlir/Analysis/LoopAnalysis.h" 21 #include "mlir/Dialect/AffineOps/AffineOps.h" 22 #include "mlir/Dialect/StandardOps/Ops.h" 23 #include "mlir/Dialect/VectorOps/VectorOps.h" 24 #include "mlir/IR/Builders.h" 25 #include "mlir/IR/IntegerSet.h" 26 #include "mlir/IR/Operation.h" 27 #include "mlir/Support/Functional.h" 28 #include "mlir/Support/STLExtras.h" 29 30 #include "llvm/ADT/DenseSet.h" 31 #include "llvm/ADT/SetVector.h" 32 33 /// 34 /// Implements Analysis functions specific to vectors which support 35 /// the vectorization and vectorization materialization passes. 36 /// 37 38 using namespace mlir; 39 40 using llvm::SetVector; 41 42 Optional<SmallVector<unsigned, 4>> 43 mlir::shapeRatio(ArrayRef<int64_t> superShape, ArrayRef<int64_t> subShape) { 44 if (superShape.size() < subShape.size()) { 45 return Optional<SmallVector<unsigned, 4>>(); 46 } 47 48 // Starting from the end, compute the integer divisors. 49 // Set the boolean `divides` if integral division is not possible. 50 std::vector<unsigned> result; 51 result.reserve(superShape.size()); 52 bool divides = true; 53 auto divide = [÷s, &result](int superSize, int subSize) { 54 assert(superSize > 0 && "superSize must be > 0"); 55 assert(subSize > 0 && "subSize must be > 0"); 56 divides &= (superSize % subSize == 0); 57 result.push_back(superSize / subSize); 58 }; 59 functional::zipApply( 60 divide, SmallVector<int64_t, 8>{superShape.rbegin(), superShape.rend()}, 61 SmallVector<int64_t, 8>{subShape.rbegin(), subShape.rend()}); 62 63 // If integral division does not occur, return and let the caller decide. 64 if (!divides) { 65 return None; 66 } 67 68 // At this point we computed the ratio (in reverse) for the common 69 // size. Fill with the remaining entries from the super-vector shape (still in 70 // reverse). 71 int commonSize = subShape.size(); 72 std::copy(superShape.rbegin() + commonSize, superShape.rend(), 73 std::back_inserter(result)); 74 75 assert(result.size() == superShape.size() && 76 "super to sub shape ratio is not of the same size as the super rank"); 77 78 // Reverse again to get it back in the proper order and return. 79 return SmallVector<unsigned, 4>{result.rbegin(), result.rend()}; 80 } 81 82 Optional<SmallVector<unsigned, 4>> mlir::shapeRatio(VectorType superVectorType, 83 VectorType subVectorType) { 84 assert(superVectorType.getElementType() == subVectorType.getElementType() && 85 "vector types must be of the same elemental type"); 86 return shapeRatio(superVectorType.getShape(), subVectorType.getShape()); 87 } 88 89 /// Constructs a permutation map from memref indices to vector dimension. 90 /// 91 /// The implementation uses the knowledge of the mapping of enclosing loop to 92 /// vector dimension. `enclosingLoopToVectorDim` carries this information as a 93 /// map with: 94 /// - keys representing "vectorized enclosing loops"; 95 /// - values representing the corresponding vector dimension. 96 /// The algorithm traverses "vectorized enclosing loops" and extracts the 97 /// at-most-one MemRef index that is invariant along said loop. This index is 98 /// guaranteed to be at most one by construction: otherwise the MemRef is not 99 /// vectorizable. 100 /// If this invariant index is found, it is added to the permutation_map at the 101 /// proper vector dimension. 102 /// If no index is found to be invariant, 0 is added to the permutation_map and 103 /// corresponds to a vector broadcast along that dimension. 104 /// 105 /// Returns an empty AffineMap if `enclosingLoopToVectorDim` is empty, 106 /// signalling that no permutation map can be constructed given 107 /// `enclosingLoopToVectorDim`. 108 /// 109 /// Examples can be found in the documentation of `makePermutationMap`, in the 110 /// header file. 111 static AffineMap makePermutationMap( 112 ArrayRef<Value *> indices, 113 const DenseMap<Operation *, unsigned> &enclosingLoopToVectorDim) { 114 if (enclosingLoopToVectorDim.empty()) 115 return AffineMap(); 116 MLIRContext *context = 117 enclosingLoopToVectorDim.begin()->getFirst()->getContext(); 118 using functional::makePtrDynCaster; 119 using functional::map; 120 SmallVector<AffineExpr, 4> perm(enclosingLoopToVectorDim.size(), 121 getAffineConstantExpr(0, context)); 122 123 for (auto kvp : enclosingLoopToVectorDim) { 124 assert(kvp.second < perm.size()); 125 auto invariants = getInvariantAccesses( 126 cast<AffineForOp>(kvp.first).getInductionVar(), indices); 127 unsigned numIndices = indices.size(); 128 unsigned countInvariantIndices = 0; 129 for (unsigned dim = 0; dim < numIndices; ++dim) { 130 if (!invariants.count(indices[dim])) { 131 assert(perm[kvp.second] == getAffineConstantExpr(0, context) && 132 "permutationMap already has an entry along dim"); 133 perm[kvp.second] = getAffineDimExpr(dim, context); 134 } else { 135 ++countInvariantIndices; 136 } 137 } 138 assert((countInvariantIndices == numIndices || 139 countInvariantIndices == numIndices - 1) && 140 "Vectorization prerequisite violated: at most 1 index may be " 141 "invariant wrt a vectorized loop"); 142 } 143 return AffineMap::get(indices.size(), 0, perm); 144 } 145 146 /// Implementation detail that walks up the parents and records the ones with 147 /// the specified type. 148 /// TODO(ntv): could also be implemented as a collect parents followed by a 149 /// filter and made available outside this file. 150 template <typename T> 151 static SetVector<Operation *> getParentsOfType(Operation *op) { 152 SetVector<Operation *> res; 153 auto *current = op; 154 while (auto *parent = current->getParentOp()) { 155 if (auto typedParent = dyn_cast<T>(parent)) { 156 assert(res.count(parent) == 0 && "Already inserted"); 157 res.insert(parent); 158 } 159 current = parent; 160 } 161 return res; 162 } 163 164 /// Returns the enclosing AffineForOp, from closest to farthest. 165 static SetVector<Operation *> getEnclosingforOps(Operation *op) { 166 return getParentsOfType<AffineForOp>(op); 167 } 168 169 AffineMap mlir::makePermutationMap( 170 Operation *op, ArrayRef<Value *> indices, 171 const DenseMap<Operation *, unsigned> &loopToVectorDim) { 172 DenseMap<Operation *, unsigned> enclosingLoopToVectorDim; 173 auto enclosingLoops = getEnclosingforOps(op); 174 for (auto *forInst : enclosingLoops) { 175 auto it = loopToVectorDim.find(forInst); 176 if (it != loopToVectorDim.end()) { 177 enclosingLoopToVectorDim.insert(*it); 178 } 179 } 180 return ::makePermutationMap(indices, enclosingLoopToVectorDim); 181 } 182 183 bool mlir::matcher::operatesOnSuperVectorsOf(Operation &op, 184 VectorType subVectorType) { 185 // First, extract the vector type and ditinguish between: 186 // a. ops that *must* lower a super-vector (i.e. vector.transfer_read, 187 // vector.transfer_write); and 188 // b. ops that *may* lower a super-vector (all other ops). 189 // The ops that *may* lower a super-vector only do so if the super-vector to 190 // sub-vector ratio exists. The ops that *must* lower a super-vector are 191 // explicitly checked for this property. 192 /// TODO(ntv): there should be a single function for all ops to do this so we 193 /// do not have to special case. Maybe a trait, or just a method, unclear atm. 194 bool mustDivide = false; 195 (void)mustDivide; 196 VectorType superVectorType; 197 if (auto read = dyn_cast<vector::VectorTransferReadOp>(op)) { 198 superVectorType = read.getResultType(); 199 mustDivide = true; 200 } else if (auto write = dyn_cast<vector::VectorTransferWriteOp>(op)) { 201 superVectorType = write.getVectorType(); 202 mustDivide = true; 203 } else if (op.getNumResults() == 0) { 204 if (!isa<ReturnOp>(op)) { 205 op.emitError("NYI: assuming only return operations can have 0 " 206 " results at this point"); 207 } 208 return false; 209 } else if (op.getNumResults() == 1) { 210 if (auto v = op.getResult(0)->getType().dyn_cast<VectorType>()) { 211 superVectorType = v; 212 } else { 213 // Not a vector type. 214 return false; 215 } 216 } else { 217 // Not a vector.transfer and has more than 1 result, fail hard for now to 218 // wake us up when something changes. 219 op.emitError("NYI: operation has more than 1 result"); 220 return false; 221 } 222 223 // Get the ratio. 224 auto ratio = shapeRatio(superVectorType, subVectorType); 225 226 // Sanity check. 227 assert((ratio.hasValue() || !mustDivide) && 228 "vector.transfer operation in which super-vector size is not an" 229 " integer multiple of sub-vector size"); 230 231 // This catches cases that are not strictly necessary to have multiplicity but 232 // still aren't divisible by the sub-vector shape. 233 // This could be useful information if we wanted to reshape at the level of 234 // the vector type (but we would have to look at the compute and distinguish 235 // between parallel, reduction and possibly other cases. 236 if (!ratio.hasValue()) { 237 return false; 238 } 239 240 return true; 241 }