github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/third_party/mlir/test/lib/Transforms/TestVectorizationUtils.cpp (about)

     1  //===- VectorizerTestPass.cpp - VectorizerTestPass Pass Impl --------------===//
     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  // This file implements a simple testing pass for vectorization functionality.
    19  //
    20  //===----------------------------------------------------------------------===//
    21  
    22  #include "mlir/Analysis/AffineAnalysis.h"
    23  #include "mlir/Analysis/NestedMatcher.h"
    24  #include "mlir/Analysis/SliceAnalysis.h"
    25  #include "mlir/Analysis/VectorAnalysis.h"
    26  #include "mlir/Dialect/AffineOps/AffineOps.h"
    27  #include "mlir/IR/Builders.h"
    28  #include "mlir/IR/Diagnostics.h"
    29  #include "mlir/IR/StandardTypes.h"
    30  #include "mlir/Pass/Pass.h"
    31  #include "mlir/Support/Functional.h"
    32  #include "mlir/Support/STLExtras.h"
    33  #include "mlir/Transforms/Passes.h"
    34  
    35  #include "llvm/ADT/STLExtras.h"
    36  #include "llvm/Support/CommandLine.h"
    37  #include "llvm/Support/Debug.h"
    38  
    39  #define DEBUG_TYPE "affine-vectorizer-test"
    40  
    41  using namespace mlir;
    42  
    43  using llvm::SetVector;
    44  
    45  using functional::map;
    46  
    47  static llvm::cl::OptionCategory clOptionsCategory(DEBUG_TYPE " options");
    48  
    49  static llvm::cl::list<int> clTestVectorShapeRatio(
    50      "vector-shape-ratio",
    51      llvm::cl::desc("Specify the HW vector size for vectorization"),
    52      llvm::cl::ZeroOrMore, llvm::cl::cat(clOptionsCategory));
    53  static llvm::cl::opt<bool> clTestForwardSlicingAnalysis(
    54      "forward-slicing",
    55      llvm::cl::desc("Enable testing forward static slicing and topological sort "
    56                     "functionalities"),
    57      llvm::cl::cat(clOptionsCategory));
    58  static llvm::cl::opt<bool> clTestBackwardSlicingAnalysis(
    59      "backward-slicing",
    60      llvm::cl::desc("Enable testing backward static slicing and "
    61                     "topological sort functionalities"),
    62      llvm::cl::cat(clOptionsCategory));
    63  static llvm::cl::opt<bool> clTestSlicingAnalysis(
    64      "slicing",
    65      llvm::cl::desc("Enable testing static slicing and topological sort "
    66                     "functionalities"),
    67      llvm::cl::cat(clOptionsCategory));
    68  static llvm::cl::opt<bool> clTestComposeMaps(
    69      "compose-maps",
    70      llvm::cl::desc(
    71          "Enable testing the composition of AffineMap where each "
    72          "AffineMap in the composition is specified as the affine_map attribute "
    73          "in a constant op."),
    74      llvm::cl::cat(clOptionsCategory));
    75  static llvm::cl::opt<bool> clTestNormalizeMaps(
    76      "normalize-maps",
    77      llvm::cl::desc(
    78          "Enable testing the normalization of AffineAffineApplyOp "
    79          "where each AffineAffineApplyOp in the composition is a single output "
    80          "operation."),
    81      llvm::cl::cat(clOptionsCategory));
    82  
    83  namespace {
    84  struct VectorizerTestPass : public FunctionPass<VectorizerTestPass> {
    85    static constexpr auto kTestAffineMapOpName = "test_affine_map";
    86    static constexpr auto kTestAffineMapAttrName = "affine_map";
    87  
    88    void runOnFunction() override;
    89    void testVectorShapeRatio(llvm::raw_ostream &outs);
    90    void testForwardSlicing(llvm::raw_ostream &outs);
    91    void testBackwardSlicing(llvm::raw_ostream &outs);
    92    void testSlicing(llvm::raw_ostream &outs);
    93    void testComposeMaps(llvm::raw_ostream &outs);
    94    void testNormalizeMaps();
    95  };
    96  
    97  } // end anonymous namespace
    98  
    99  void VectorizerTestPass::testVectorShapeRatio(llvm::raw_ostream &outs) {
   100    auto f = getFunction();
   101    using matcher::Op;
   102    SmallVector<int64_t, 8> shape(clTestVectorShapeRatio.begin(),
   103                                  clTestVectorShapeRatio.end());
   104    auto subVectorType =
   105        VectorType::get(shape, FloatType::getF32(f.getContext()));
   106    // Only filter operations that operate on a strict super-vector and have one
   107    // return. This makes testing easier.
   108    auto filter = [&](Operation &op) {
   109      assert(subVectorType.getElementType().isF32() &&
   110             "Only f32 supported for now");
   111      if (!matcher::operatesOnSuperVectorsOf(op, subVectorType)) {
   112        return false;
   113      }
   114      if (op.getNumResults() != 1) {
   115        return false;
   116      }
   117      return true;
   118    };
   119    auto pat = Op(filter);
   120    SmallVector<NestedMatch, 8> matches;
   121    pat.match(f, &matches);
   122    for (auto m : matches) {
   123      auto *opInst = m.getMatchedOperation();
   124      // This is a unit test that only checks and prints shape ratio.
   125      // As a consequence we write only Ops with a single return type for the
   126      // purpose of this test. If we need to test more intricate behavior in the
   127      // future we can always extend.
   128      auto superVectorType = opInst->getResult(0)->getType().cast<VectorType>();
   129      auto ratio = shapeRatio(superVectorType, subVectorType);
   130      if (!ratio.hasValue()) {
   131        opInst->emitRemark("NOT MATCHED");
   132      } else {
   133        outs << "\nmatched: " << *opInst << " with shape ratio: ";
   134        interleaveComma(MutableArrayRef<unsigned>(*ratio), outs);
   135      }
   136    }
   137  }
   138  
   139  static NestedPattern patternTestSlicingOps() {
   140    using functional::map;
   141    using matcher::Op;
   142    // Match all operations with the kTestSlicingOpName name.
   143    auto filter = [](Operation &op) {
   144      // Just use a custom op name for this test, it makes life easier.
   145      return op.getName().getStringRef() == "slicing-test-op";
   146    };
   147    return Op(filter);
   148  }
   149  
   150  void VectorizerTestPass::testBackwardSlicing(llvm::raw_ostream &outs) {
   151    auto f = getFunction();
   152    outs << "\n" << f.getName();
   153  
   154    SmallVector<NestedMatch, 8> matches;
   155    patternTestSlicingOps().match(f, &matches);
   156    for (auto m : matches) {
   157      SetVector<Operation *> backwardSlice;
   158      getBackwardSlice(m.getMatchedOperation(), &backwardSlice);
   159      outs << "\nmatched: " << *m.getMatchedOperation()
   160           << " backward static slice: ";
   161      for (auto *op : backwardSlice)
   162        outs << "\n" << *op;
   163    }
   164  }
   165  
   166  void VectorizerTestPass::testForwardSlicing(llvm::raw_ostream &outs) {
   167    auto f = getFunction();
   168    outs << "\n" << f.getName();
   169  
   170    SmallVector<NestedMatch, 8> matches;
   171    patternTestSlicingOps().match(f, &matches);
   172    for (auto m : matches) {
   173      SetVector<Operation *> forwardSlice;
   174      getForwardSlice(m.getMatchedOperation(), &forwardSlice);
   175      outs << "\nmatched: " << *m.getMatchedOperation()
   176           << " forward static slice: ";
   177      for (auto *op : forwardSlice)
   178        outs << "\n" << *op;
   179    }
   180  }
   181  
   182  void VectorizerTestPass::testSlicing(llvm::raw_ostream &outs) {
   183    auto f = getFunction();
   184    outs << "\n" << f.getName();
   185  
   186    SmallVector<NestedMatch, 8> matches;
   187    patternTestSlicingOps().match(f, &matches);
   188    for (auto m : matches) {
   189      SetVector<Operation *> staticSlice = getSlice(m.getMatchedOperation());
   190      outs << "\nmatched: " << *m.getMatchedOperation() << " static slice: ";
   191      for (auto *op : staticSlice)
   192        outs << "\n" << *op;
   193    }
   194  }
   195  
   196  static bool customOpWithAffineMapAttribute(Operation &op) {
   197    return op.getName().getStringRef() ==
   198           VectorizerTestPass::kTestAffineMapOpName;
   199  }
   200  
   201  void VectorizerTestPass::testComposeMaps(llvm::raw_ostream &outs) {
   202    auto f = getFunction();
   203  
   204    using matcher::Op;
   205    auto pattern = Op(customOpWithAffineMapAttribute);
   206    SmallVector<NestedMatch, 8> matches;
   207    pattern.match(f, &matches);
   208    SmallVector<AffineMap, 4> maps;
   209    maps.reserve(matches.size());
   210    for (auto m : llvm::reverse(matches)) {
   211      auto *opInst = m.getMatchedOperation();
   212      auto map = opInst->getAttr(VectorizerTestPass::kTestAffineMapAttrName)
   213                     .cast<AffineMapAttr>()
   214                     .getValue();
   215      maps.push_back(map);
   216    }
   217    AffineMap res;
   218    for (auto m : maps) {
   219      res = res ? res.compose(m) : m;
   220    }
   221    simplifyAffineMap(res).print(outs << "\nComposed map: ");
   222  }
   223  
   224  static bool affineApplyOp(Operation &op) { return isa<AffineApplyOp>(op); }
   225  
   226  static bool singleResultAffineApplyOpWithoutUses(Operation &op) {
   227    auto app = dyn_cast<AffineApplyOp>(op);
   228    return app && app.use_empty();
   229  }
   230  
   231  void VectorizerTestPass::testNormalizeMaps() {
   232    using matcher::Op;
   233  
   234    auto f = getFunction();
   235  
   236    // Save matched AffineApplyOp that all need to be erased in the end.
   237    auto pattern = Op(affineApplyOp);
   238    SmallVector<NestedMatch, 8> toErase;
   239    pattern.match(f, &toErase);
   240    {
   241      // Compose maps.
   242      auto pattern = Op(singleResultAffineApplyOpWithoutUses);
   243      SmallVector<NestedMatch, 8> matches;
   244      pattern.match(f, &matches);
   245      for (auto m : matches) {
   246        auto app = cast<AffineApplyOp>(m.getMatchedOperation());
   247        OpBuilder b(m.getMatchedOperation());
   248        SmallVector<Value *, 8> operands(app.getOperands());
   249        makeComposedAffineApply(b, app.getLoc(), app.getAffineMap(), operands);
   250      }
   251    }
   252    // We should now be able to erase everything in reverse order in this test.
   253    for (auto m : llvm::reverse(toErase)) {
   254      m.getMatchedOperation()->erase();
   255    }
   256  }
   257  
   258  void VectorizerTestPass::runOnFunction() {
   259    // Thread-safe RAII local context, BumpPtrAllocator freed on exit.
   260    NestedPatternContext mlContext;
   261  
   262    // Only support single block functions at this point.
   263    FuncOp f = getFunction();
   264    if (f.getBlocks().size() != 1)
   265      return;
   266  
   267    std::string str;
   268    llvm::raw_string_ostream outs(str);
   269  
   270    if (!clTestVectorShapeRatio.empty())
   271      testVectorShapeRatio(outs);
   272  
   273    if (clTestForwardSlicingAnalysis)
   274      testForwardSlicing(outs);
   275  
   276    if (clTestBackwardSlicingAnalysis)
   277      testBackwardSlicing(outs);
   278  
   279    if (clTestSlicingAnalysis)
   280      testSlicing(outs);
   281  
   282    if (clTestComposeMaps)
   283      testComposeMaps(outs);
   284  
   285    if (clTestNormalizeMaps)
   286      testNormalizeMaps();
   287  
   288    if (!outs.str().empty()) {
   289      emitRemark(UnknownLoc::get(&getContext()), outs.str());
   290    }
   291  }
   292  
   293  std::unique_ptr<FunctionPassBase> mlir::createVectorizerTestPass() {
   294    return std::make_unique<VectorizerTestPass>();
   295  }
   296  
   297  static PassRegistration<VectorizerTestPass>
   298      pass("affine-vectorizer-test",
   299           "Tests vectorizer standalone functionality.");
   300  
   301  #undef DEBUG_TYPE