1 //===- TestAffineDataCopy.cpp - Test affine data copy utility -------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements a pass to test affine data copy utility functions and
10 // options.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "mlir/Analysis/Utils.h"
15 #include "mlir/Dialect/Affine/IR/AffineOps.h"
16 #include "mlir/Dialect/MemRef/IR/MemRef.h"
17 #include "mlir/Pass/Pass.h"
18 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
19 #include "mlir/Transforms/LoopUtils.h"
20 #include "mlir/Transforms/Passes.h"
21 
22 #define PASS_NAME "test-affine-data-copy"
23 
24 using namespace mlir;
25 
26 static llvm::cl::OptionCategory clOptionsCategory(PASS_NAME " options");
27 
28 namespace {
29 
30 struct TestAffineDataCopy
31     : public PassWrapper<TestAffineDataCopy, FunctionPass> {
32   StringRef getArgument() const final { return PASS_NAME; }
33   StringRef getDescription() const final {
34     return "Tests affine data copy utility functions.";
35   }
36   TestAffineDataCopy() = default;
37   TestAffineDataCopy(const TestAffineDataCopy &pass){};
38 
39   void getDependentDialects(DialectRegistry &registry) const override {
40     registry.insert<memref::MemRefDialect>();
41   }
42   void runOnFunction() override;
43 
44 private:
45   Option<bool> clMemRefFilter{
46       *this, "memref-filter",
47       llvm::cl::desc(
48           "Enable memref filter testing in affine data copy optimization"),
49       llvm::cl::init(false)};
50   Option<bool> clTestGenerateCopyForMemRegion{
51       *this, "for-memref-region",
52       llvm::cl::desc("Test copy generation for a single memref region"),
53       llvm::cl::init(false)};
54 };
55 
56 } // end anonymous namespace
57 
58 void TestAffineDataCopy::runOnFunction() {
59   // Gather all AffineForOps by loop depth.
60   std::vector<SmallVector<AffineForOp, 2>> depthToLoops;
61   gatherLoops(getFunction(), depthToLoops);
62   assert(depthToLoops.size() && "Loop nest not found");
63 
64   // Only support tests with a single loop nest and a single innermost loop
65   // for now.
66   unsigned innermostLoopIdx = depthToLoops.size() - 1;
67   if (depthToLoops[0].size() != 1 || depthToLoops[innermostLoopIdx].size() != 1)
68     return;
69 
70   auto loopNest = depthToLoops[0][0];
71   auto innermostLoop = depthToLoops[innermostLoopIdx][0];
72   AffineLoadOp load;
73   if (clMemRefFilter || clTestGenerateCopyForMemRegion) {
74     // Gather MemRef filter. For simplicity, we use the first loaded memref
75     // found in the innermost loop.
76     for (auto &op : *innermostLoop.getBody()) {
77       if (auto ld = dyn_cast<AffineLoadOp>(op)) {
78         load = ld;
79         break;
80       }
81     }
82   }
83   if (!load)
84     return;
85 
86   AffineCopyOptions copyOptions = {/*generateDma=*/false,
87                                    /*slowMemorySpace=*/0,
88                                    /*fastMemorySpace=*/0,
89                                    /*tagMemorySpace=*/0,
90                                    /*fastMemCapacityBytes=*/32 * 1024 * 1024UL};
91   DenseSet<Operation *> copyNests;
92   if (clMemRefFilter) {
93     affineDataCopyGenerate(loopNest, copyOptions, load.getMemRef(), copyNests);
94   } else if (clTestGenerateCopyForMemRegion) {
95     CopyGenerateResult result;
96     MemRefRegion region(loopNest.getLoc());
97     (void)region.compute(load, /*loopDepth=*/0);
98     (void)generateCopyForMemRegion(region, loopNest, copyOptions, result);
99   }
100 
101   // Promote any single iteration loops in the copy nests and simplify
102   // load/stores.
103   SmallVector<Operation *, 4> copyOps;
104   for (Operation *nest : copyNests) {
105     // With a post order walk, the erasure of loops does not affect
106     // continuation of the walk or the collection of load/store ops.
107     nest->walk([&](Operation *op) {
108       if (auto forOp = dyn_cast<AffineForOp>(op))
109         (void)promoteIfSingleIteration(forOp);
110       else if (auto loadOp = dyn_cast<AffineLoadOp>(op))
111         copyOps.push_back(loadOp);
112       else if (auto storeOp = dyn_cast<AffineStoreOp>(op))
113         copyOps.push_back(storeOp);
114     });
115   }
116 
117   // Promoting single iteration loops could lead to simplification of
118   // generated load's/store's, and the latter could anyway also be
119   // canonicalized.
120   RewritePatternSet patterns(&getContext());
121   for (Operation *op : copyOps) {
122     patterns.clear();
123     if (isa<AffineLoadOp>(op)) {
124       AffineLoadOp::getCanonicalizationPatterns(patterns, &getContext());
125     } else {
126       assert(isa<AffineStoreOp>(op) && "expected affine store op");
127       AffineStoreOp::getCanonicalizationPatterns(patterns, &getContext());
128     }
129   }
130   (void)applyOpPatternsAndFold(copyOps, std::move(patterns), /*strict=*/true);
131 }
132 
133 namespace mlir {
134 void registerTestAffineDataCopyPass() {
135   PassRegistration<TestAffineDataCopy>();
136 }
137 } // namespace mlir
138