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 = [&divides, &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  }