xref: /llvm-project/mlir/lib/Dialect/Affine/Transforms/AffineLoopInvariantCodeMotion.cpp (revision 132de3a71f581dcb008a124d52c83ccca8158d98)
1e7084713SRob Suderman //===- AffineLoopInvariantCodeMotion.cpp - Code to perform loop fusion-----===//
2e7084713SRob Suderman //
3e7084713SRob Suderman // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e7084713SRob Suderman // See https://llvm.org/LICENSE.txt for license information.
5e7084713SRob Suderman // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e7084713SRob Suderman //
7e7084713SRob Suderman //===----------------------------------------------------------------------===//
8e7084713SRob Suderman //
9e7084713SRob Suderman // This file implements loop invariant code motion.
10e7084713SRob Suderman //
11e7084713SRob Suderman //===----------------------------------------------------------------------===//
12e7084713SRob Suderman 
1367d0d7acSMichele Scuttari #include "mlir/Dialect/Affine/Passes.h"
1467d0d7acSMichele Scuttari 
15755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/Utils.h"
1667d0d7acSMichele Scuttari #include "mlir/Dialect/Func/IR/FuncOps.h"
173d287a75SUday Bondhugula #include "mlir/Interfaces/SideEffectInterfaces.h"
18e7084713SRob Suderman #include "llvm/Support/Debug.h"
19e7084713SRob Suderman #include "llvm/Support/raw_ostream.h"
20e7084713SRob Suderman 
2167d0d7acSMichele Scuttari namespace mlir {
224c48f016SMatthias Springer namespace affine {
2367d0d7acSMichele Scuttari #define GEN_PASS_DEF_AFFINELOOPINVARIANTCODEMOTION
2467d0d7acSMichele Scuttari #include "mlir/Dialect/Affine/Passes.h.inc"
254c48f016SMatthias Springer } // namespace affine
2667d0d7acSMichele Scuttari } // namespace mlir
2767d0d7acSMichele Scuttari 
28*132de3a7SUday Bondhugula #define DEBUG_TYPE "affine-licm"
29e7084713SRob Suderman 
30e7084713SRob Suderman using namespace mlir;
314c48f016SMatthias Springer using namespace mlir::affine;
32e7084713SRob Suderman 
33e7084713SRob Suderman namespace {
34e7084713SRob Suderman 
35d1ceb740SUday Bondhugula /// Affine loop invariant code motion (LICM) pass.
36*132de3a7SUday Bondhugula /// TODO: The pass is missing zero tripcount tests.
37*132de3a7SUday Bondhugula /// TODO: When compared to the other standard LICM pass, this pass
38*132de3a7SUday Bondhugula /// has some special handling for affine read/write ops but such handling
39*132de3a7SUday Bondhugula /// requires aliasing to be sound, and as such this pass is unsound. In
40*132de3a7SUday Bondhugula /// addition, this handling is nothing particular to affine memory ops but would
41*132de3a7SUday Bondhugula /// apply to any memory read/write effect ops. Either aliasing should be handled
42*132de3a7SUday Bondhugula /// or this pass can be removed and the standard LICM can be used.
4380aca1eaSRiver Riddle struct LoopInvariantCodeMotion
444c48f016SMatthias Springer     : public affine::impl::AffineLoopInvariantCodeMotionBase<
454c48f016SMatthias Springer           LoopInvariantCodeMotion> {
4641574554SRiver Riddle   void runOnOperation() override;
47e7084713SRob Suderman   void runOnAffineForOp(AffineForOp forOp);
48e7084713SRob Suderman };
49be0a7e9fSMehdi Amini } // namespace
50e7084713SRob Suderman 
51e7084713SRob Suderman static bool
52*132de3a7SUday Bondhugula checkInvarianceOfNestedIfOps(AffineIfOp ifOp, AffineForOp loop,
53f56791aeSSumesh Udayakumaran                              SmallPtrSetImpl<Operation *> &opsWithUsers,
54e7084713SRob Suderman                              SmallPtrSetImpl<Operation *> &opsToHoist);
55*132de3a7SUday Bondhugula static bool isOpLoopInvariant(Operation &op, AffineForOp loop,
56f56791aeSSumesh Udayakumaran                               SmallPtrSetImpl<Operation *> &opsWithUsers,
57e7084713SRob Suderman                               SmallPtrSetImpl<Operation *> &opsToHoist);
58e7084713SRob Suderman 
59e7084713SRob Suderman static bool
60*132de3a7SUday Bondhugula areAllOpsInTheBlockListInvariant(Region &blockList, AffineForOp loop,
61f56791aeSSumesh Udayakumaran                                  SmallPtrSetImpl<Operation *> &opsWithUsers,
62e7084713SRob Suderman                                  SmallPtrSetImpl<Operation *> &opsToHoist);
63e7084713SRob Suderman 
64*132de3a7SUday Bondhugula /// Returns true if `op` is invariant on `loop`.
65*132de3a7SUday Bondhugula static bool isOpLoopInvariant(Operation &op, AffineForOp loop,
66f56791aeSSumesh Udayakumaran                               SmallPtrSetImpl<Operation *> &opsWithUsers,
67e7084713SRob Suderman                               SmallPtrSetImpl<Operation *> &opsToHoist) {
68*132de3a7SUday Bondhugula   Value iv = loop.getInductionVar();
69e7084713SRob Suderman 
70aa7aac9aSUday Bondhugula   if (auto ifOp = dyn_cast<AffineIfOp>(op)) {
71*132de3a7SUday Bondhugula     if (!checkInvarianceOfNestedIfOps(ifOp, loop, opsWithUsers, opsToHoist))
72e7084713SRob Suderman       return false;
7391940716SAmy Zhuang   } else if (auto forOp = dyn_cast<AffineForOp>(op)) {
74*132de3a7SUday Bondhugula     if (!areAllOpsInTheBlockListInvariant(forOp.getRegion(), loop, opsWithUsers,
75*132de3a7SUday Bondhugula                                           opsToHoist))
76e7084713SRob Suderman       return false;
775f9cd099SUday Bondhugula   } else if (auto parOp = dyn_cast<AffineParallelOp>(op)) {
78*132de3a7SUday Bondhugula     if (!areAllOpsInTheBlockListInvariant(parOp.getRegion(), loop, opsWithUsers,
79*132de3a7SUday Bondhugula                                           opsToHoist))
805f9cd099SUday Bondhugula       return false;
813d287a75SUday Bondhugula   } else if (!isMemoryEffectFree(&op) &&
82*132de3a7SUday Bondhugula              !isa<AffineReadOpInterface, AffineWriteOpInterface>(&op)) {
833d287a75SUday Bondhugula     // Check for side-effecting ops. Affine read/write ops are handled
843d287a75SUday Bondhugula     // separately below.
85e7084713SRob Suderman     return false;
86*132de3a7SUday Bondhugula   } else if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op)) {
87f56791aeSSumesh Udayakumaran     // Register op in the set of ops that have users.
88f56791aeSSumesh Udayakumaran     opsWithUsers.insert(&op);
89*132de3a7SUday Bondhugula     SmallVector<AffineForOp, 8> userIVs;
90aa7aac9aSUday Bondhugula     auto read = dyn_cast<AffineReadOpInterface>(op);
91*132de3a7SUday Bondhugula     Value memref =
92*132de3a7SUday Bondhugula         read ? read.getMemRef() : cast<AffineWriteOpInterface>(op).getMemRef();
93e7084713SRob Suderman     for (auto *user : memref.getUsers()) {
94e7084713SRob Suderman       // If the memref used by the load/store is used in a store elsewhere in
95e7084713SRob Suderman       // the loop nest, we do not hoist. Similarly, if the memref used in a
96e7084713SRob Suderman       // load is also being stored too, we do not hoist the load.
97*132de3a7SUday Bondhugula       // FIXME: This is missing checking aliases.
98*132de3a7SUday Bondhugula       if (&op == user)
99*132de3a7SUday Bondhugula         continue;
100*132de3a7SUday Bondhugula       if (hasEffect<MemoryEffects::Write>(user, memref) ||
101*132de3a7SUday Bondhugula           (hasEffect<MemoryEffects::Read>(user, memref) &&
102553bfc8fSDiego Caballero            isa<AffineWriteOpInterface>(op))) {
103*132de3a7SUday Bondhugula         userIVs.clear();
10423bcd6b8SUday Bondhugula         getAffineForIVs(*user, &userIVs);
105e7084713SRob Suderman         // Check that userIVs don't contain the for loop around the op.
106*132de3a7SUday Bondhugula         if (llvm::is_contained(userIVs, loop))
107e7084713SRob Suderman           return false;
108e7084713SRob Suderman       }
109e7084713SRob Suderman     }
110e7084713SRob Suderman   }
11191940716SAmy Zhuang 
11291940716SAmy Zhuang   // Check operands.
113*132de3a7SUday Bondhugula   ValueRange iterArgs = loop.getRegionIterArgs();
114e7084713SRob Suderman   for (unsigned int i = 0; i < op.getNumOperands(); ++i) {
115e7084713SRob Suderman     auto *operandSrc = op.getOperand(i).getDefiningOp();
116e7084713SRob Suderman 
117e7084713SRob Suderman     // If the loop IV is the operand, this op isn't loop invariant.
118*132de3a7SUday Bondhugula     if (iv == op.getOperand(i))
119e7084713SRob Suderman       return false;
120e7084713SRob Suderman 
121eff269fcSVinayaka Bandishti     // If the one of the iter_args is the operand, this op isn't loop invariant.
122*132de3a7SUday Bondhugula     if (llvm::is_contained(iterArgs, op.getOperand(i)))
123eff269fcSVinayaka Bandishti       return false;
124eff269fcSVinayaka Bandishti 
125aa7aac9aSUday Bondhugula     if (operandSrc) {
126aa7aac9aSUday Bondhugula       // If the value was defined in the loop (outside of the if/else region),
127aa7aac9aSUday Bondhugula       // and that operation itself wasn't meant to be hoisted, then mark this
128aa7aac9aSUday Bondhugula       // operation loop dependent.
129aa7aac9aSUday Bondhugula       if (opsWithUsers.count(operandSrc) && opsToHoist.count(operandSrc) == 0)
130e7084713SRob Suderman         return false;
131e7084713SRob Suderman     }
132e7084713SRob Suderman   }
133e7084713SRob Suderman 
134e7084713SRob Suderman   // If no operand was loop variant, mark this op for motion.
135e7084713SRob Suderman   opsToHoist.insert(&op);
136e7084713SRob Suderman   return true;
137e7084713SRob Suderman }
138e7084713SRob Suderman 
139e7084713SRob Suderman // Checks if all ops in a region (i.e. list of blocks) are loop invariant.
1404c48f016SMatthias Springer static bool
141*132de3a7SUday Bondhugula areAllOpsInTheBlockListInvariant(Region &blockList, AffineForOp loop,
142eff269fcSVinayaka Bandishti                                  SmallPtrSetImpl<Operation *> &opsWithUsers,
143e7084713SRob Suderman                                  SmallPtrSetImpl<Operation *> &opsToHoist) {
144e7084713SRob Suderman 
145e7084713SRob Suderman   for (auto &b : blockList) {
146e7084713SRob Suderman     for (auto &op : b) {
147*132de3a7SUday Bondhugula       if (!isOpLoopInvariant(op, loop, opsWithUsers, opsToHoist))
148e7084713SRob Suderman         return false;
149e7084713SRob Suderman     }
150e7084713SRob Suderman   }
151e7084713SRob Suderman 
152e7084713SRob Suderman   return true;
153e7084713SRob Suderman }
154e7084713SRob Suderman 
155e7084713SRob Suderman // Returns true if the affine.if op can be hoisted.
1564c48f016SMatthias Springer static bool
157*132de3a7SUday Bondhugula checkInvarianceOfNestedIfOps(AffineIfOp ifOp, AffineForOp loop,
158f56791aeSSumesh Udayakumaran                              SmallPtrSetImpl<Operation *> &opsWithUsers,
159e7084713SRob Suderman                              SmallPtrSetImpl<Operation *> &opsToHoist) {
160*132de3a7SUday Bondhugula   if (!areAllOpsInTheBlockListInvariant(ifOp.getThenRegion(), loop,
161aa7aac9aSUday Bondhugula                                         opsWithUsers, opsToHoist))
162e7084713SRob Suderman     return false;
163e7084713SRob Suderman 
164*132de3a7SUday Bondhugula   if (!areAllOpsInTheBlockListInvariant(ifOp.getElseRegion(), loop,
165aa7aac9aSUday Bondhugula                                         opsWithUsers, opsToHoist))
166e7084713SRob Suderman     return false;
167e7084713SRob Suderman 
168e7084713SRob Suderman   return true;
169e7084713SRob Suderman }
170e7084713SRob Suderman 
171e7084713SRob Suderman void LoopInvariantCodeMotion::runOnAffineForOp(AffineForOp forOp) {
172e7084713SRob Suderman   // This is the place where hoisted instructions would reside.
173e7084713SRob Suderman   OpBuilder b(forOp.getOperation());
174e7084713SRob Suderman 
175e7084713SRob Suderman   SmallPtrSet<Operation *, 8> opsToHoist;
176e7084713SRob Suderman   SmallVector<Operation *, 8> opsToMove;
177f56791aeSSumesh Udayakumaran   SmallPtrSet<Operation *, 8> opsWithUsers;
178e7084713SRob Suderman 
179*132de3a7SUday Bondhugula   for (Operation &op : *forOp.getBody()) {
180f56791aeSSumesh Udayakumaran     // Register op in the set of ops that have users. This set is used
181f56791aeSSumesh Udayakumaran     // to prevent hoisting ops that depend on these ops that are
182f56791aeSSumesh Udayakumaran     // not being hoisted.
183f56791aeSSumesh Udayakumaran     if (!op.use_empty())
184f56791aeSSumesh Udayakumaran       opsWithUsers.insert(&op);
1852ede8918SJeremy Bruestle     if (!isa<AffineYieldOp>(op)) {
186*132de3a7SUday Bondhugula       if (isOpLoopInvariant(op, forOp, opsWithUsers, opsToHoist)) {
187e7084713SRob Suderman         opsToMove.push_back(&op);
188e7084713SRob Suderman       }
189e7084713SRob Suderman     }
190e7084713SRob Suderman   }
191e7084713SRob Suderman 
192e7084713SRob Suderman   // For all instructions that we found to be invariant, place sequentially
193e7084713SRob Suderman   // right before the for loop.
194e7084713SRob Suderman   for (auto *op : opsToMove) {
195e7084713SRob Suderman     op->moveBefore(forOp);
196e7084713SRob Suderman   }
197e7084713SRob Suderman }
198e7084713SRob Suderman 
19941574554SRiver Riddle void LoopInvariantCodeMotion::runOnOperation() {
200e7084713SRob Suderman   // Walk through all loops in a function in innermost-loop-first order.  This
201e7084713SRob Suderman   // way, we first LICM from the inner loop, and place the ops in
202e7084713SRob Suderman   // the outer loop, which in turn can be further LICM'ed.
203*132de3a7SUday Bondhugula   getOperation().walk([&](AffineForOp op) { runOnAffineForOp(op); });
204e7084713SRob Suderman }
205e7084713SRob Suderman 
20658ceae95SRiver Riddle std::unique_ptr<OperationPass<func::FuncOp>>
2074c48f016SMatthias Springer mlir::affine::createAffineLoopInvariantCodeMotionPass() {
208e7084713SRob Suderman   return std::make_unique<LoopInvariantCodeMotion>();
209e7084713SRob Suderman }
210