xref: /llvm-project/mlir/lib/Dialect/Affine/Transforms/LoopUnrollAndJam.cpp (revision 4c48f016effde67d500fc95290096aec9f3bdb70)
1b8737614SUday Bondhugula //===- LoopUnrollAndJam.cpp - Code to perform loop unroll and jam ---------===//
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 loop unroll and jam. Unroll and jam is a transformation
10b8737614SUday Bondhugula // that improves locality, in particular, register reuse, while also improving
11b8737614SUday Bondhugula // operation level parallelism. The example below shows what it does in nearly
12b8737614SUday Bondhugula // the general case. Loop unroll and jam currently works if the bounds of the
13b8737614SUday Bondhugula // loops inner to the loop being unroll-jammed do not depend on the latter.
14b8737614SUday Bondhugula //
15b8737614SUday Bondhugula // Before      After unroll and jam of i by factor 2:
16b8737614SUday Bondhugula //
17b8737614SUday Bondhugula //             for i, step = 2
18b8737614SUday Bondhugula // for i         S1(i);
19b8737614SUday Bondhugula //   S1;         S2(i);
20b8737614SUday Bondhugula //   S2;         S1(i+1);
21b8737614SUday Bondhugula //   for j       S2(i+1);
22b8737614SUday Bondhugula //     S3;       for j
23b8737614SUday Bondhugula //     S4;         S3(i, j);
24b8737614SUday Bondhugula //   S5;           S4(i, j);
25b8737614SUday Bondhugula //   S6;           S3(i+1, j)
26b8737614SUday Bondhugula //                 S4(i+1, j)
27b8737614SUday Bondhugula //               S5(i);
28b8737614SUday Bondhugula //               S6(i);
29b8737614SUday Bondhugula //               S5(i+1);
30b8737614SUday Bondhugula //               S6(i+1);
31b8737614SUday Bondhugula //
32b8737614SUday Bondhugula // Note: 'if/else' blocks are not jammed. So, if there are loops inside if
33b8737614SUday Bondhugula // op's, bodies of those loops will not be jammed.
34b8737614SUday Bondhugula //===----------------------------------------------------------------------===//
351834ad4aSRiver Riddle 
3667d0d7acSMichele Scuttari #include "mlir/Dialect/Affine/Passes.h"
3767d0d7acSMichele Scuttari 
38755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h"
39755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h"
40b8737614SUday Bondhugula #include "mlir/Dialect/Affine/IR/AffineOps.h"
41a70aa7bbSRiver Riddle #include "mlir/Dialect/Affine/LoopUtils.h"
4267d0d7acSMichele Scuttari #include "mlir/Dialect/Func/IR/FuncOps.h"
43b8737614SUday Bondhugula #include "mlir/IR/AffineExpr.h"
44b8737614SUday Bondhugula #include "mlir/IR/AffineMap.h"
45b8737614SUday Bondhugula #include "mlir/IR/Builders.h"
464d67b278SJeff Niu #include "mlir/IR/IRMapping.h"
47b8737614SUday Bondhugula #include "llvm/ADT/DenseMap.h"
48b8737614SUday Bondhugula #include "llvm/Support/CommandLine.h"
49a1fe1f5fSKazu Hirata #include <optional>
50b8737614SUday Bondhugula 
5167d0d7acSMichele Scuttari namespace mlir {
52*4c48f016SMatthias Springer namespace affine {
5367d0d7acSMichele Scuttari #define GEN_PASS_DEF_AFFINELOOPUNROLLANDJAM
5467d0d7acSMichele Scuttari #include "mlir/Dialect/Affine/Passes.h.inc"
55*4c48f016SMatthias Springer } // namespace affine
5667d0d7acSMichele Scuttari } // namespace mlir
57b8737614SUday Bondhugula 
58b8737614SUday Bondhugula #define DEBUG_TYPE "affine-loop-unroll-jam"
59b8737614SUday Bondhugula 
6067d0d7acSMichele Scuttari using namespace mlir;
61*4c48f016SMatthias Springer using namespace mlir::affine;
6267d0d7acSMichele Scuttari 
63b8737614SUday Bondhugula namespace {
64b8737614SUday Bondhugula /// Loop unroll jam pass. Currently, this just unroll jams the first
65b8737614SUday Bondhugula /// outer loop in a Function.
6667d0d7acSMichele Scuttari struct LoopUnrollAndJam
67*4c48f016SMatthias Springer     : public affine::impl::AffineLoopUnrollAndJamBase<LoopUnrollAndJam> {
LoopUnrollAndJam__anonddc5f8580111::LoopUnrollAndJam680a81ace0SKazu Hirata   explicit LoopUnrollAndJam(
690a81ace0SKazu Hirata       std::optional<unsigned> unrollJamFactor = std::nullopt) {
70400ad6f9SRiver Riddle     if (unrollJamFactor)
71400ad6f9SRiver Riddle       this->unrollJamFactor = *unrollJamFactor;
72400ad6f9SRiver Riddle   }
73b8737614SUday Bondhugula 
7441574554SRiver Riddle   void runOnOperation() override;
75b8737614SUday Bondhugula };
76be0a7e9fSMehdi Amini } // namespace
77b8737614SUday Bondhugula 
7858ceae95SRiver Riddle std::unique_ptr<OperationPass<func::FuncOp>>
createLoopUnrollAndJamPass(int unrollJamFactor)79*4c48f016SMatthias Springer mlir::affine::createLoopUnrollAndJamPass(int unrollJamFactor) {
80b8737614SUday Bondhugula   return std::make_unique<LoopUnrollAndJam>(
811a36588eSKazu Hirata       unrollJamFactor == -1 ? std::nullopt
820a81ace0SKazu Hirata                             : std::optional<unsigned>(unrollJamFactor));
83b8737614SUday Bondhugula }
84b8737614SUday Bondhugula 
runOnOperation()8541574554SRiver Riddle void LoopUnrollAndJam::runOnOperation() {
8641574554SRiver Riddle   if (getOperation().isExternal())
8741574554SRiver Riddle     return;
8841574554SRiver Riddle 
89b8737614SUday Bondhugula   // Currently, just the outermost loop from the first loop nest is
90b8737614SUday Bondhugula   // unroll-and-jammed by this pass. However, runOnAffineForOp can be called on
91b8737614SUday Bondhugula   // any for operation.
9241574554SRiver Riddle   auto &entryBlock = getOperation().front();
93b8737614SUday Bondhugula   if (auto forOp = dyn_cast<AffineForOp>(entryBlock.front()))
94e21adfa3SRiver Riddle     (void)loopUnrollJamByFactor(forOp, unrollJamFactor);
95b8737614SUday Bondhugula }
96