1 | //===- LoopUnrollAndJam.cpp - Code to perform loop unroll and jam ---------===// |
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 loop unroll and jam. Unroll and jam is a transformation |
10 | // that improves locality, in particular, register reuse, while also improving |
11 | // operation level parallelism. The example below shows what it does in nearly |
12 | // the general case. Loop unroll and jam currently works if the bounds of the |
13 | // loops inner to the loop being unroll-jammed do not depend on the latter. |
14 | // |
15 | // Before After unroll and jam of i by factor 2: |
16 | // |
17 | // for i, step = 2 |
18 | // for i S1(i); |
19 | // S1; S2(i); |
20 | // S2; S1(i+1); |
21 | // for j S2(i+1); |
22 | // S3; for j |
23 | // S4; S3(i, j); |
24 | // S5; S4(i, j); |
25 | // S6; S3(i+1, j) |
26 | // S4(i+1, j) |
27 | // S5(i); |
28 | // S6(i); |
29 | // S5(i+1); |
30 | // S6(i+1); |
31 | // |
32 | // Note: 'if/else' blocks are not jammed. So, if there are loops inside if |
33 | // op's, bodies of those loops will not be jammed. |
34 | //===----------------------------------------------------------------------===// |
35 | |
36 | #include "mlir/Dialect/Affine/Passes.h" |
37 | |
38 | #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h" |
39 | #include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h" |
40 | #include "mlir/Dialect/Affine/IR/AffineOps.h" |
41 | #include "mlir/Dialect/Affine/LoopUtils.h" |
42 | #include "mlir/Dialect/Func/IR/FuncOps.h" |
43 | #include "mlir/IR/AffineExpr.h" |
44 | #include "mlir/IR/AffineMap.h" |
45 | #include "mlir/IR/Builders.h" |
46 | #include "mlir/IR/IRMapping.h" |
47 | #include "llvm/ADT/DenseMap.h" |
48 | #include "llvm/Support/CommandLine.h" |
49 | #include <optional> |
50 | |
51 | namespace mlir { |
52 | namespace affine { |
53 | #define GEN_PASS_DEF_AFFINELOOPUNROLLANDJAM |
54 | #include "mlir/Dialect/Affine/Passes.h.inc" |
55 | } // namespace affine |
56 | } // namespace mlir |
57 | |
58 | #define DEBUG_TYPE "affine-loop-unroll-jam" |
59 | |
60 | using namespace mlir; |
61 | using namespace mlir::affine; |
62 | |
63 | namespace { |
64 | /// Loop unroll jam pass. Currently, this just unroll jams the first |
65 | /// outer loop in a Function. |
66 | struct LoopUnrollAndJam |
67 | : public affine::impl::AffineLoopUnrollAndJamBase<LoopUnrollAndJam> { |
68 | explicit LoopUnrollAndJam( |
69 | std::optional<unsigned> unrollJamFactor = std::nullopt) { |
70 | if (unrollJamFactor) |
71 | this->unrollJamFactor = *unrollJamFactor; |
72 | } |
73 | |
74 | void runOnOperation() override; |
75 | }; |
76 | } // namespace |
77 | |
78 | std::unique_ptr<OperationPass<func::FuncOp>> |
79 | mlir::affine::createLoopUnrollAndJamPass(int unrollJamFactor) { |
80 | return std::make_unique<LoopUnrollAndJam>( |
81 | args: unrollJamFactor == -1 ? std::nullopt |
82 | : std::optional<unsigned>(unrollJamFactor)); |
83 | } |
84 | |
85 | void LoopUnrollAndJam::runOnOperation() { |
86 | if (getOperation().isExternal()) |
87 | return; |
88 | |
89 | // Currently, just the outermost loop from the first loop nest is |
90 | // unroll-and-jammed by this pass. However, runOnAffineForOp can be called on |
91 | // any for operation. |
92 | auto &entryBlock = getOperation().front(); |
93 | if (auto forOp = dyn_cast<AffineForOp>(entryBlock.front())) |
94 | (void)loopUnrollJamByFactor(forOp, unrollJamFactor); |
95 | } |
96 | |