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