xref: /llvm-project/mlir/test/lib/Dialect/Affine/TestVectorizationUtils.cpp (revision f023da12d12635f5fba436e825cbfc999e28e623)
1b8737614SUday Bondhugula //===- VectorizerTestPass.cpp - VectorizerTestPass Pass Impl --------------===//
2b8737614SUday Bondhugula //
3b8737614SUday Bondhugula // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b8737614SUday Bondhugula // See https://llvm.org/LICENSE.txt for license information.
5b8737614SUday Bondhugula // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b8737614SUday Bondhugula //
7b8737614SUday Bondhugula //===----------------------------------------------------------------------===//
8b8737614SUday Bondhugula //
9b8737614SUday Bondhugula // This file implements a simple testing pass for vectorization functionality.
10b8737614SUday Bondhugula //
11b8737614SUday Bondhugula //===----------------------------------------------------------------------===//
12b8737614SUday Bondhugula 
13b8737614SUday Bondhugula #include "mlir/Analysis/SliceAnalysis.h"
14755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h"
15755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/NestedMatcher.h"
16b8737614SUday Bondhugula #include "mlir/Dialect/Affine/IR/AffineOps.h"
17a70aa7bbSRiver Riddle #include "mlir/Dialect/Affine/LoopUtils.h"
1893936da9SDiego Caballero #include "mlir/Dialect/Affine/Utils.h"
1936550692SRiver Riddle #include "mlir/Dialect/Func/IR/FuncOps.h"
207a69a9d7SNicolas Vasilache #include "mlir/Dialect/Utils/IndexingUtils.h"
2199ef9eebSMatthias Springer #include "mlir/Dialect/Vector/IR/VectorOps.h"
2299ef9eebSMatthias Springer #include "mlir/Dialect/Vector/Utils/VectorUtils.h"
23b8737614SUday Bondhugula #include "mlir/IR/Builders.h"
2409f7a55fSRiver Riddle #include "mlir/IR/BuiltinTypes.h"
25b8737614SUday Bondhugula #include "mlir/IR/Diagnostics.h"
26b8737614SUday Bondhugula #include "mlir/Pass/Pass.h"
27b8737614SUday Bondhugula #include "mlir/Transforms/Passes.h"
28b8737614SUday Bondhugula 
29b8737614SUday Bondhugula #include "llvm/ADT/STLExtras.h"
30b8737614SUday Bondhugula #include "llvm/Support/CommandLine.h"
31b8737614SUday Bondhugula #include "llvm/Support/Debug.h"
32b8737614SUday Bondhugula 
33b8737614SUday Bondhugula #define DEBUG_TYPE "affine-super-vectorizer-test"
34b8737614SUday Bondhugula 
35b8737614SUday Bondhugula using namespace mlir;
364c48f016SMatthias Springer using namespace mlir::affine;
37b8737614SUday Bondhugula 
38b8737614SUday Bondhugula static llvm::cl::OptionCategory clOptionsCategory(DEBUG_TYPE " options");
39b8737614SUday Bondhugula 
40b8737614SUday Bondhugula namespace {
4180aca1eaSRiver Riddle struct VectorizerTestPass
4258ceae95SRiver Riddle     : public PassWrapper<VectorizerTestPass, OperationPass<func::FuncOp>> {
435e50dd04SRiver Riddle   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(VectorizerTestPass)
445e50dd04SRiver Riddle 
45b8737614SUday Bondhugula   static constexpr auto kTestAffineMapOpName = "test_affine_map";
46b8737614SUday Bondhugula   static constexpr auto kTestAffineMapAttrName = "affine_map";
47f9dc2b70SMehdi Amini   void getDependentDialects(DialectRegistry &registry) const override {
48f9dc2b70SMehdi Amini     registry.insert<vector::VectorDialect>();
49f9dc2b70SMehdi Amini   }
50b5e22e6dSMehdi Amini   StringRef getArgument() const final { return "affine-super-vectorizer-test"; }
51b5e22e6dSMehdi Amini   StringRef getDescription() const final {
52b5e22e6dSMehdi Amini     return "Tests vectorizer standalone functionality.";
53b5e22e6dSMehdi Amini   }
54b8737614SUday Bondhugula 
55608a663cSPhilip Lassen   VectorizerTestPass() = default;
56608a663cSPhilip Lassen   VectorizerTestPass(const VectorizerTestPass &pass) : PassWrapper(pass){};
57608a663cSPhilip Lassen 
58608a663cSPhilip Lassen   ListOption<int> clTestVectorShapeRatio{
59608a663cSPhilip Lassen       *this, "vector-shape-ratio",
60608a663cSPhilip Lassen       llvm::cl::desc("Specify the HW vector size for vectorization")};
61608a663cSPhilip Lassen   Option<bool> clTestForwardSlicingAnalysis{
62608a663cSPhilip Lassen       *this, "forward-slicing",
63608a663cSPhilip Lassen       llvm::cl::desc(
64608a663cSPhilip Lassen           "Enable testing forward static slicing and topological sort "
65608a663cSPhilip Lassen           "functionalities")};
66608a663cSPhilip Lassen   Option<bool> clTestBackwardSlicingAnalysis{
67608a663cSPhilip Lassen       *this, "backward-slicing",
68608a663cSPhilip Lassen       llvm::cl::desc("Enable testing backward static slicing and "
69608a663cSPhilip Lassen                      "topological sort functionalities")};
70608a663cSPhilip Lassen   Option<bool> clTestSlicingAnalysis{
71608a663cSPhilip Lassen       *this, "slicing",
72608a663cSPhilip Lassen       llvm::cl::desc("Enable testing static slicing and topological sort "
73608a663cSPhilip Lassen                      "functionalities")};
74608a663cSPhilip Lassen   Option<bool> clTestComposeMaps{
75608a663cSPhilip Lassen       *this, "compose-maps",
76608a663cSPhilip Lassen       llvm::cl::desc("Enable testing the composition of AffineMap where each "
77608a663cSPhilip Lassen                      "AffineMap in the composition is specified as the "
78608a663cSPhilip Lassen                      "affine_map attribute "
79608a663cSPhilip Lassen                      "in a constant op.")};
80608a663cSPhilip Lassen   Option<bool> clTestVecAffineLoopNest{
81608a663cSPhilip Lassen       *this, "vectorize-affine-loop-nest",
82608a663cSPhilip Lassen       llvm::cl::desc(
83608a663cSPhilip Lassen           "Enable testing for the 'vectorizeAffineLoopNest' utility by "
84608a663cSPhilip Lassen           "vectorizing the outermost loops found")};
85608a663cSPhilip Lassen 
8641574554SRiver Riddle   void runOnOperation() override;
87b8737614SUday Bondhugula   void testVectorShapeRatio(llvm::raw_ostream &outs);
88b8737614SUday Bondhugula   void testForwardSlicing(llvm::raw_ostream &outs);
89b8737614SUday Bondhugula   void testBackwardSlicing(llvm::raw_ostream &outs);
90b8737614SUday Bondhugula   void testSlicing(llvm::raw_ostream &outs);
91b8737614SUday Bondhugula   void testComposeMaps(llvm::raw_ostream &outs);
9293936da9SDiego Caballero 
9393936da9SDiego Caballero   /// Test for 'vectorizeAffineLoopNest' utility.
9410cff75fSKai Sasaki   void testVecAffineLoopNest(llvm::raw_ostream &outs);
95b8737614SUday Bondhugula };
96b8737614SUday Bondhugula 
97be0a7e9fSMehdi Amini } // namespace
98b8737614SUday Bondhugula 
99b8737614SUday Bondhugula void VectorizerTestPass::testVectorShapeRatio(llvm::raw_ostream &outs) {
10041574554SRiver Riddle   auto f = getOperation();
1014c48f016SMatthias Springer   using affine::matcher::Op;
102b8737614SUday Bondhugula   SmallVector<int64_t, 8> shape(clTestVectorShapeRatio.begin(),
103b8737614SUday Bondhugula                                 clTestVectorShapeRatio.end());
104*f023da12SMatthias Springer   auto subVectorType = VectorType::get(shape, Float32Type::get(f.getContext()));
105b8737614SUday Bondhugula   // Only filter operations that operate on a strict super-vector and have one
106b8737614SUday Bondhugula   // return. This makes testing easier.
107b8737614SUday Bondhugula   auto filter = [&](Operation &op) {
108b8737614SUday Bondhugula     assert(subVectorType.getElementType().isF32() &&
109b8737614SUday Bondhugula            "Only f32 supported for now");
1104c48f016SMatthias Springer     if (!mlir::matcher::operatesOnSuperVectorsOf(op, subVectorType)) {
111b8737614SUday Bondhugula       return false;
112b8737614SUday Bondhugula     }
113b8737614SUday Bondhugula     if (op.getNumResults() != 1) {
114b8737614SUday Bondhugula       return false;
115b8737614SUday Bondhugula     }
116b8737614SUday Bondhugula     return true;
117b8737614SUday Bondhugula   };
118b8737614SUday Bondhugula   auto pat = Op(filter);
119b8737614SUday Bondhugula   SmallVector<NestedMatch, 8> matches;
120b8737614SUday Bondhugula   pat.match(f, &matches);
121b8737614SUday Bondhugula   for (auto m : matches) {
122b8737614SUday Bondhugula     auto *opInst = m.getMatchedOperation();
123b8737614SUday Bondhugula     // This is a unit test that only checks and prints shape ratio.
124b8737614SUday Bondhugula     // As a consequence we write only Ops with a single return type for the
125b8737614SUday Bondhugula     // purpose of this test. If we need to test more intricate behavior in the
126b8737614SUday Bondhugula     // future we can always extend.
1275550c821STres Popp     auto superVectorType = cast<VectorType>(opInst->getResult(0).getType());
1287a69a9d7SNicolas Vasilache     auto ratio =
1297a69a9d7SNicolas Vasilache         computeShapeRatio(superVectorType.getShape(), subVectorType.getShape());
130037f0995SKazu Hirata     if (!ratio) {
131b8737614SUday Bondhugula       opInst->emitRemark("NOT MATCHED");
132b8737614SUday Bondhugula     } else {
133b8737614SUday Bondhugula       outs << "\nmatched: " << *opInst << " with shape ratio: ";
1342f21a579SRiver Riddle       llvm::interleaveComma(MutableArrayRef<int64_t>(*ratio), outs);
135b8737614SUday Bondhugula     }
136b8737614SUday Bondhugula   }
137b8737614SUday Bondhugula }
138b8737614SUday Bondhugula 
139b8737614SUday Bondhugula static NestedPattern patternTestSlicingOps() {
1404c48f016SMatthias Springer   using affine::matcher::Op;
141b8737614SUday Bondhugula   // Match all operations with the kTestSlicingOpName name.
142b8737614SUday Bondhugula   auto filter = [](Operation &op) {
143b8737614SUday Bondhugula     // Just use a custom op name for this test, it makes life easier.
144b8737614SUday Bondhugula     return op.getName().getStringRef() == "slicing-test-op";
145b8737614SUday Bondhugula   };
146b8737614SUday Bondhugula   return Op(filter);
147b8737614SUday Bondhugula }
148b8737614SUday Bondhugula 
149b8737614SUday Bondhugula void VectorizerTestPass::testBackwardSlicing(llvm::raw_ostream &outs) {
15041574554SRiver Riddle   auto f = getOperation();
151b8737614SUday Bondhugula   outs << "\n" << f.getName();
152b8737614SUday Bondhugula 
153b8737614SUday Bondhugula   SmallVector<NestedMatch, 8> matches;
154b8737614SUday Bondhugula   patternTestSlicingOps().match(f, &matches);
155b8737614SUday Bondhugula   for (auto m : matches) {
156b8737614SUday Bondhugula     SetVector<Operation *> backwardSlice;
157b8737614SUday Bondhugula     getBackwardSlice(m.getMatchedOperation(), &backwardSlice);
158b8737614SUday Bondhugula     outs << "\nmatched: " << *m.getMatchedOperation()
159b8737614SUday Bondhugula          << " backward static slice: ";
160b8737614SUday Bondhugula     for (auto *op : backwardSlice)
161b8737614SUday Bondhugula       outs << "\n" << *op;
162b8737614SUday Bondhugula   }
163b8737614SUday Bondhugula }
164b8737614SUday Bondhugula 
165b8737614SUday Bondhugula void VectorizerTestPass::testForwardSlicing(llvm::raw_ostream &outs) {
16641574554SRiver Riddle   auto f = getOperation();
167b8737614SUday Bondhugula   outs << "\n" << f.getName();
168b8737614SUday Bondhugula 
169b8737614SUday Bondhugula   SmallVector<NestedMatch, 8> matches;
170b8737614SUday Bondhugula   patternTestSlicingOps().match(f, &matches);
171b8737614SUday Bondhugula   for (auto m : matches) {
172b8737614SUday Bondhugula     SetVector<Operation *> forwardSlice;
173b8737614SUday Bondhugula     getForwardSlice(m.getMatchedOperation(), &forwardSlice);
174b8737614SUday Bondhugula     outs << "\nmatched: " << *m.getMatchedOperation()
175b8737614SUday Bondhugula          << " forward static slice: ";
176b8737614SUday Bondhugula     for (auto *op : forwardSlice)
177b8737614SUday Bondhugula       outs << "\n" << *op;
178b8737614SUday Bondhugula   }
179b8737614SUday Bondhugula }
180b8737614SUday Bondhugula 
181b8737614SUday Bondhugula void VectorizerTestPass::testSlicing(llvm::raw_ostream &outs) {
18241574554SRiver Riddle   auto f = getOperation();
183b8737614SUday Bondhugula   outs << "\n" << f.getName();
184b8737614SUday Bondhugula 
185b8737614SUday Bondhugula   SmallVector<NestedMatch, 8> matches;
186b8737614SUday Bondhugula   patternTestSlicingOps().match(f, &matches);
187b8737614SUday Bondhugula   for (auto m : matches) {
188b8737614SUday Bondhugula     SetVector<Operation *> staticSlice = getSlice(m.getMatchedOperation());
189b8737614SUday Bondhugula     outs << "\nmatched: " << *m.getMatchedOperation() << " static slice: ";
190b8737614SUday Bondhugula     for (auto *op : staticSlice)
191b8737614SUday Bondhugula       outs << "\n" << *op;
192b8737614SUday Bondhugula   }
193b8737614SUday Bondhugula }
194b8737614SUday Bondhugula 
195b8737614SUday Bondhugula static bool customOpWithAffineMapAttribute(Operation &op) {
196b8737614SUday Bondhugula   return op.getName().getStringRef() ==
197b8737614SUday Bondhugula          VectorizerTestPass::kTestAffineMapOpName;
198b8737614SUday Bondhugula }
199b8737614SUday Bondhugula 
200b8737614SUday Bondhugula void VectorizerTestPass::testComposeMaps(llvm::raw_ostream &outs) {
20141574554SRiver Riddle   auto f = getOperation();
202b8737614SUday Bondhugula 
2034c48f016SMatthias Springer   using affine::matcher::Op;
204b8737614SUday Bondhugula   auto pattern = Op(customOpWithAffineMapAttribute);
205b8737614SUday Bondhugula   SmallVector<NestedMatch, 8> matches;
206b8737614SUday Bondhugula   pattern.match(f, &matches);
207b8737614SUday Bondhugula   SmallVector<AffineMap, 4> maps;
208b8737614SUday Bondhugula   maps.reserve(matches.size());
209b8737614SUday Bondhugula   for (auto m : llvm::reverse(matches)) {
210b8737614SUday Bondhugula     auto *opInst = m.getMatchedOperation();
211830b9b07SMehdi Amini     auto map =
212830b9b07SMehdi Amini         cast<AffineMapAttr>(opInst->getDiscardableAttr(
213830b9b07SMehdi Amini                                 VectorizerTestPass::kTestAffineMapAttrName))
214b8737614SUday Bondhugula             .getValue();
215b8737614SUday Bondhugula     maps.push_back(map);
216b8737614SUday Bondhugula   }
2173d7383d7SKai Sasaki   if (maps.empty())
2183d7383d7SKai Sasaki     // Nothing to compose
2193d7383d7SKai Sasaki     return;
220b8737614SUday Bondhugula   AffineMap res;
221b8737614SUday Bondhugula   for (auto m : maps) {
222b8737614SUday Bondhugula     res = res ? res.compose(m) : m;
223b8737614SUday Bondhugula   }
224b8737614SUday Bondhugula   simplifyAffineMap(res).print(outs << "\nComposed map: ");
225b8737614SUday Bondhugula }
226b8737614SUday Bondhugula 
22793936da9SDiego Caballero /// Test for 'vectorizeAffineLoopNest' utility.
22810cff75fSKai Sasaki void VectorizerTestPass::testVecAffineLoopNest(llvm::raw_ostream &outs) {
22993936da9SDiego Caballero   std::vector<SmallVector<AffineForOp, 2>> loops;
23041574554SRiver Riddle   gatherLoops(getOperation(), loops);
231b8737614SUday Bondhugula 
23293936da9SDiego Caballero   // Expected only one loop nest.
23393936da9SDiego Caballero   if (loops.empty() || loops[0].size() != 1)
23493936da9SDiego Caballero     return;
23593936da9SDiego Caballero 
23693936da9SDiego Caballero   // We vectorize the outermost loop found with VF=4.
23793936da9SDiego Caballero   AffineForOp outermostLoop = loops[0][0];
23893936da9SDiego Caballero   VectorizationStrategy strategy;
23993936da9SDiego Caballero   strategy.vectorSizes.push_back(4 /*vectorization factor*/);
24093936da9SDiego Caballero   strategy.loopToVectorDim[outermostLoop] = 0;
24110cff75fSKai Sasaki 
24210cff75fSKai Sasaki   ReductionLoopMap reductionLoops;
24310cff75fSKai Sasaki   SmallVector<LoopReduction, 2> reductions;
24410cff75fSKai Sasaki   if (!isLoopParallel(outermostLoop, &reductions)) {
24510cff75fSKai Sasaki     outs << "Outermost loop cannot be parallel\n";
24610cff75fSKai Sasaki     return;
24710cff75fSKai Sasaki   }
24893936da9SDiego Caballero   std::vector<SmallVector<AffineForOp, 2>> loopsToVectorize;
24993936da9SDiego Caballero   loopsToVectorize.push_back({outermostLoop});
250e21adfa3SRiver Riddle   (void)vectorizeAffineLoopNest(loopsToVectorize, strategy);
25193936da9SDiego Caballero }
25293936da9SDiego Caballero 
25341574554SRiver Riddle void VectorizerTestPass::runOnOperation() {
254b8737614SUday Bondhugula   // Only support single block functions at this point.
25558ceae95SRiver Riddle   func::FuncOp f = getOperation();
2562eaadfc4SRahul Joshi   if (!llvm::hasSingleElement(f))
257b8737614SUday Bondhugula     return;
258b8737614SUday Bondhugula 
259b8737614SUday Bondhugula   std::string str;
260b8737614SUday Bondhugula   llvm::raw_string_ostream outs(str);
261b8737614SUday Bondhugula 
26293936da9SDiego Caballero   { // Tests that expect a NestedPatternContext to be allocated externally.
26393936da9SDiego Caballero     NestedPatternContext mlContext;
26493936da9SDiego Caballero 
265b8737614SUday Bondhugula     if (!clTestVectorShapeRatio.empty())
266b8737614SUday Bondhugula       testVectorShapeRatio(outs);
267b8737614SUday Bondhugula 
268b8737614SUday Bondhugula     if (clTestForwardSlicingAnalysis)
269b8737614SUday Bondhugula       testForwardSlicing(outs);
270b8737614SUday Bondhugula 
271b8737614SUday Bondhugula     if (clTestBackwardSlicingAnalysis)
272b8737614SUday Bondhugula       testBackwardSlicing(outs);
273b8737614SUday Bondhugula 
274b8737614SUday Bondhugula     if (clTestSlicingAnalysis)
275b8737614SUday Bondhugula       testSlicing(outs);
276b8737614SUday Bondhugula 
277b8737614SUday Bondhugula     if (clTestComposeMaps)
278b8737614SUday Bondhugula       testComposeMaps(outs);
27993936da9SDiego Caballero   }
28093936da9SDiego Caballero 
28193936da9SDiego Caballero   if (clTestVecAffineLoopNest)
28210cff75fSKai Sasaki     testVecAffineLoopNest(outs);
283b8737614SUday Bondhugula 
284b8737614SUday Bondhugula   if (!outs.str().empty()) {
285b8737614SUday Bondhugula     emitRemark(UnknownLoc::get(&getContext()), outs.str());
286b8737614SUday Bondhugula   }
287b8737614SUday Bondhugula }
288b8737614SUday Bondhugula 
289b8737614SUday Bondhugula namespace mlir {
290b5e22e6dSMehdi Amini void registerVectorizerTestPass() { PassRegistration<VectorizerTestPass>(); }
291b8737614SUday Bondhugula } // namespace mlir
292