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 
131834ad4aSRiver Riddle #include "PassDetail.h"
14e7084713SRob Suderman #include "mlir/Analysis/SliceAnalysis.h"
15755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h"
16755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/AffineStructures.h"
17755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/LoopAnalysis.h"
18755dc07dSRiver Riddle #include "mlir/Dialect/Affine/Analysis/Utils.h"
19e7084713SRob Suderman #include "mlir/Dialect/Affine/IR/AffineOps.h"
20a70aa7bbSRiver Riddle #include "mlir/Dialect/Affine/LoopUtils.h"
21b8737614SUday Bondhugula #include "mlir/Dialect/Affine/Passes.h"
22a70aa7bbSRiver Riddle #include "mlir/Dialect/Affine/Utils.h"
23a54f4eaeSMogball #include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
24e7084713SRob Suderman #include "mlir/IR/AffineExpr.h"
25e7084713SRob Suderman #include "mlir/IR/AffineMap.h"
26e7084713SRob Suderman #include "mlir/IR/Builders.h"
271f971e23SRiver Riddle #include "mlir/IR/Matchers.h"
28e7084713SRob Suderman #include "llvm/ADT/DenseMap.h"
29e7084713SRob Suderman #include "llvm/ADT/DenseSet.h"
30e7084713SRob Suderman #include "llvm/ADT/SmallPtrSet.h"
31e7084713SRob Suderman #include "llvm/Support/CommandLine.h"
32e7084713SRob Suderman #include "llvm/Support/Debug.h"
33e7084713SRob Suderman #include "llvm/Support/raw_ostream.h"
34e7084713SRob Suderman 
35e7084713SRob Suderman #define DEBUG_TYPE "licm"
36e7084713SRob Suderman 
37e7084713SRob Suderman using namespace mlir;
38e7084713SRob Suderman 
39e7084713SRob Suderman namespace {
40e7084713SRob Suderman 
41e7084713SRob Suderman /// Loop invariant code motion (LICM) pass.
429db53a18SRiver Riddle /// TODO: The pass is missing zero-trip tests.
439db53a18SRiver Riddle /// TODO: Check for the presence of side effects before hoisting.
44e7084713SRob Suderman /// TODO: This code should be removed once the new LICM pass can handle its
45e7084713SRob Suderman ///       uses.
4680aca1eaSRiver Riddle struct LoopInvariantCodeMotion
471834ad4aSRiver Riddle     : public AffineLoopInvariantCodeMotionBase<LoopInvariantCodeMotion> {
4841574554SRiver Riddle   void runOnOperation() override;
49e7084713SRob Suderman   void runOnAffineForOp(AffineForOp forOp);
50e7084713SRob Suderman };
51be0a7e9fSMehdi Amini } // namespace
52e7084713SRob Suderman 
53e7084713SRob Suderman static bool
54eff269fcSVinayaka Bandishti checkInvarianceOfNestedIfOps(Operation *op, Value indVar, ValueRange iterArgs,
55f56791aeSSumesh Udayakumaran                              SmallPtrSetImpl<Operation *> &opsWithUsers,
56e7084713SRob Suderman                              SmallPtrSetImpl<Operation *> &opsToHoist);
57eff269fcSVinayaka Bandishti static bool isOpLoopInvariant(Operation &op, Value indVar, ValueRange iterArgs,
58f56791aeSSumesh Udayakumaran                               SmallPtrSetImpl<Operation *> &opsWithUsers,
59e7084713SRob Suderman                               SmallPtrSetImpl<Operation *> &opsToHoist);
60e7084713SRob Suderman 
61e7084713SRob Suderman static bool
62e7084713SRob Suderman areAllOpsInTheBlockListInvariant(Region &blockList, Value indVar,
63eff269fcSVinayaka Bandishti                                  ValueRange iterArgs,
64f56791aeSSumesh Udayakumaran                                  SmallPtrSetImpl<Operation *> &opsWithUsers,
65e7084713SRob Suderman                                  SmallPtrSetImpl<Operation *> &opsToHoist);
66e7084713SRob Suderman 
67e7084713SRob Suderman // Returns true if the individual op is loop invariant.
isOpLoopInvariant(Operation & op,Value indVar,ValueRange iterArgs,SmallPtrSetImpl<Operation * > & opsWithUsers,SmallPtrSetImpl<Operation * > & opsToHoist)68eff269fcSVinayaka Bandishti bool isOpLoopInvariant(Operation &op, Value indVar, ValueRange iterArgs,
69f56791aeSSumesh Udayakumaran                        SmallPtrSetImpl<Operation *> &opsWithUsers,
70e7084713SRob Suderman                        SmallPtrSetImpl<Operation *> &opsToHoist) {
71e7084713SRob Suderman   LLVM_DEBUG(llvm::dbgs() << "iterating on op: " << op;);
72e7084713SRob Suderman 
73e7084713SRob Suderman   if (isa<AffineIfOp>(op)) {
74eff269fcSVinayaka Bandishti     if (!checkInvarianceOfNestedIfOps(&op, indVar, iterArgs, opsWithUsers,
75eff269fcSVinayaka Bandishti                                       opsToHoist)) {
76e7084713SRob Suderman       return false;
77e7084713SRob Suderman     }
7891940716SAmy Zhuang   } else if (auto forOp = dyn_cast<AffineForOp>(op)) {
79eff269fcSVinayaka Bandishti     if (!areAllOpsInTheBlockListInvariant(forOp.getLoopBody(), indVar, iterArgs,
8091940716SAmy Zhuang                                           opsWithUsers, opsToHoist)) {
81e7084713SRob Suderman       return false;
8291940716SAmy Zhuang     }
83ee394e68SRahul Joshi   } else if (isa<AffineDmaStartOp, AffineDmaWaitOp>(op)) {
849db53a18SRiver Riddle     // TODO: Support DMA ops.
85e7084713SRob Suderman     return false;
861f971e23SRiver Riddle   } else if (!matchPattern(&op, m_Constant())) {
87f56791aeSSumesh Udayakumaran     // Register op in the set of ops that have users.
88f56791aeSSumesh Udayakumaran     opsWithUsers.insert(&op);
8999c0458fSAdam Straw     if (isa<AffineMapAccessInterface>(op)) {
90553bfc8fSDiego Caballero       Value memref = isa<AffineReadOpInterface>(op)
91553bfc8fSDiego Caballero                          ? cast<AffineReadOpInterface>(op).getMemRef()
92553bfc8fSDiego Caballero                          : cast<AffineWriteOpInterface>(op).getMemRef();
93e7084713SRob Suderman       for (auto *user : memref.getUsers()) {
94e7084713SRob Suderman         // If this memref has a user that is a DMA, give up because these
95e7084713SRob Suderman         // operations write to this memref.
96ee394e68SRahul Joshi         if (isa<AffineDmaStartOp, AffineDmaWaitOp>(op)) {
97e7084713SRob Suderman           return false;
98e7084713SRob Suderman         }
99e7084713SRob Suderman         // If the memref used by the load/store is used in a store elsewhere in
100e7084713SRob Suderman         // the loop nest, we do not hoist. Similarly, if the memref used in a
101e7084713SRob Suderman         // load is also being stored too, we do not hoist the load.
102553bfc8fSDiego Caballero         if (isa<AffineWriteOpInterface>(user) ||
103553bfc8fSDiego Caballero             (isa<AffineReadOpInterface>(user) &&
104553bfc8fSDiego Caballero              isa<AffineWriteOpInterface>(op))) {
105e7084713SRob Suderman           if (&op != user) {
106e7084713SRob Suderman             SmallVector<AffineForOp, 8> userIVs;
107e7084713SRob Suderman             getLoopIVs(*user, &userIVs);
108e7084713SRob Suderman             // Check that userIVs don't contain the for loop around the op.
109e7084713SRob Suderman             if (llvm::is_contained(userIVs, getForInductionVarOwner(indVar))) {
110e7084713SRob Suderman               return false;
111e7084713SRob Suderman             }
112e7084713SRob Suderman           }
113e7084713SRob Suderman         }
114e7084713SRob Suderman       }
115e7084713SRob Suderman     }
116e7084713SRob Suderman 
1172ede8918SJeremy Bruestle     if (op.getNumOperands() == 0 && !isa<AffineYieldOp>(op)) {
118e7084713SRob Suderman       LLVM_DEBUG(llvm::dbgs() << "\nNon-constant op with 0 operands\n");
119e7084713SRob Suderman       return false;
120e7084713SRob Suderman     }
12191940716SAmy Zhuang   }
12291940716SAmy Zhuang 
12391940716SAmy Zhuang   // Check operands.
124e7084713SRob Suderman   for (unsigned int i = 0; i < op.getNumOperands(); ++i) {
125e7084713SRob Suderman     auto *operandSrc = op.getOperand(i).getDefiningOp();
126e7084713SRob Suderman 
127e7084713SRob Suderman     LLVM_DEBUG(
128e7084713SRob Suderman         op.getOperand(i).print(llvm::dbgs() << "\nIterating on operand\n"));
129e7084713SRob Suderman 
130e7084713SRob Suderman     // If the loop IV is the operand, this op isn't loop invariant.
131e7084713SRob Suderman     if (indVar == op.getOperand(i)) {
132e7084713SRob Suderman       LLVM_DEBUG(llvm::dbgs() << "\nLoop IV is the operand\n");
133e7084713SRob Suderman       return false;
134e7084713SRob Suderman     }
135e7084713SRob Suderman 
136eff269fcSVinayaka Bandishti     // If the one of the iter_args is the operand, this op isn't loop invariant.
137eff269fcSVinayaka Bandishti     if (llvm::is_contained(iterArgs, op.getOperand(i))) {
138eff269fcSVinayaka Bandishti       LLVM_DEBUG(llvm::dbgs() << "\nOne of the iter_args is the operand\n");
139eff269fcSVinayaka Bandishti       return false;
140eff269fcSVinayaka Bandishti     }
141eff269fcSVinayaka Bandishti 
142e7084713SRob Suderman     if (operandSrc != nullptr) {
14391940716SAmy Zhuang       LLVM_DEBUG(llvm::dbgs() << *operandSrc << "\nIterating on operand src\n");
144e7084713SRob Suderman 
145e7084713SRob Suderman       // If the value was defined in the loop (outside of the
146e7084713SRob Suderman       // if/else region), and that operation itself wasn't meant to
147e7084713SRob Suderman       // be hoisted, then mark this operation loop dependent.
14891940716SAmy Zhuang       if (opsWithUsers.count(operandSrc) && opsToHoist.count(operandSrc) == 0) {
149e7084713SRob Suderman         return false;
150e7084713SRob Suderman       }
151e7084713SRob Suderman     }
152e7084713SRob Suderman   }
153e7084713SRob Suderman 
154e7084713SRob Suderman   // If no operand was loop variant, mark this op for motion.
155e7084713SRob Suderman   opsToHoist.insert(&op);
156e7084713SRob Suderman   return true;
157e7084713SRob Suderman }
158e7084713SRob Suderman 
159e7084713SRob Suderman // Checks if all ops in a region (i.e. list of blocks) are loop invariant.
areAllOpsInTheBlockListInvariant(Region & blockList,Value indVar,ValueRange iterArgs,SmallPtrSetImpl<Operation * > & opsWithUsers,SmallPtrSetImpl<Operation * > & opsToHoist)160e7084713SRob Suderman bool areAllOpsInTheBlockListInvariant(
161eff269fcSVinayaka Bandishti     Region &blockList, Value indVar, ValueRange iterArgs,
162eff269fcSVinayaka Bandishti     SmallPtrSetImpl<Operation *> &opsWithUsers,
163e7084713SRob Suderman     SmallPtrSetImpl<Operation *> &opsToHoist) {
164e7084713SRob Suderman 
165e7084713SRob Suderman   for (auto &b : blockList) {
166e7084713SRob Suderman     for (auto &op : b) {
167eff269fcSVinayaka Bandishti       if (!isOpLoopInvariant(op, indVar, iterArgs, opsWithUsers, opsToHoist)) {
168e7084713SRob Suderman         return false;
169e7084713SRob Suderman       }
170e7084713SRob Suderman     }
171e7084713SRob Suderman   }
172e7084713SRob Suderman 
173e7084713SRob Suderman   return true;
174e7084713SRob Suderman }
175e7084713SRob Suderman 
176e7084713SRob Suderman // Returns true if the affine.if op can be hoisted.
checkInvarianceOfNestedIfOps(Operation * op,Value indVar,ValueRange iterArgs,SmallPtrSetImpl<Operation * > & opsWithUsers,SmallPtrSetImpl<Operation * > & opsToHoist)177e7084713SRob Suderman bool checkInvarianceOfNestedIfOps(Operation *op, Value indVar,
178eff269fcSVinayaka Bandishti                                   ValueRange iterArgs,
179f56791aeSSumesh Udayakumaran                                   SmallPtrSetImpl<Operation *> &opsWithUsers,
180e7084713SRob Suderman                                   SmallPtrSetImpl<Operation *> &opsToHoist) {
181e7084713SRob Suderman   assert(isa<AffineIfOp>(op));
182e7084713SRob Suderman   auto ifOp = cast<AffineIfOp>(op);
183e7084713SRob Suderman 
184*04235d07SJacques Pienaar   if (!areAllOpsInTheBlockListInvariant(ifOp.getThenRegion(), indVar, iterArgs,
185eff269fcSVinayaka Bandishti                                         opsWithUsers, opsToHoist)) {
186e7084713SRob Suderman     return false;
187e7084713SRob Suderman   }
188e7084713SRob Suderman 
189*04235d07SJacques Pienaar   if (!areAllOpsInTheBlockListInvariant(ifOp.getElseRegion(), indVar, iterArgs,
190eff269fcSVinayaka Bandishti                                         opsWithUsers, opsToHoist)) {
191e7084713SRob Suderman     return false;
192e7084713SRob Suderman   }
193e7084713SRob Suderman 
194e7084713SRob Suderman   return true;
195e7084713SRob Suderman }
196e7084713SRob Suderman 
runOnAffineForOp(AffineForOp forOp)197e7084713SRob Suderman void LoopInvariantCodeMotion::runOnAffineForOp(AffineForOp forOp) {
198e7084713SRob Suderman   auto *loopBody = forOp.getBody();
199e7084713SRob Suderman   auto indVar = forOp.getInductionVar();
2005d001f58SAmy Zhuang   ValueRange iterArgs = forOp.getRegionIterArgs();
201e7084713SRob Suderman 
202e7084713SRob Suderman   // This is the place where hoisted instructions would reside.
203e7084713SRob Suderman   OpBuilder b(forOp.getOperation());
204e7084713SRob Suderman 
205e7084713SRob Suderman   SmallPtrSet<Operation *, 8> opsToHoist;
206e7084713SRob Suderman   SmallVector<Operation *, 8> opsToMove;
207f56791aeSSumesh Udayakumaran   SmallPtrSet<Operation *, 8> opsWithUsers;
208e7084713SRob Suderman 
209e7084713SRob Suderman   for (auto &op : *loopBody) {
210f56791aeSSumesh Udayakumaran     // Register op in the set of ops that have users. This set is used
211f56791aeSSumesh Udayakumaran     // to prevent hoisting ops that depend on these ops that are
212f56791aeSSumesh Udayakumaran     // not being hoisted.
213f56791aeSSumesh Udayakumaran     if (!op.use_empty())
214f56791aeSSumesh Udayakumaran       opsWithUsers.insert(&op);
2152ede8918SJeremy Bruestle     if (!isa<AffineYieldOp>(op)) {
216eff269fcSVinayaka Bandishti       if (isOpLoopInvariant(op, indVar, iterArgs, opsWithUsers, opsToHoist)) {
217e7084713SRob Suderman         opsToMove.push_back(&op);
218e7084713SRob Suderman       }
219e7084713SRob Suderman     }
220e7084713SRob Suderman   }
221e7084713SRob Suderman 
222e7084713SRob Suderman   // For all instructions that we found to be invariant, place sequentially
223e7084713SRob Suderman   // right before the for loop.
224e7084713SRob Suderman   for (auto *op : opsToMove) {
225e7084713SRob Suderman     op->moveBefore(forOp);
226e7084713SRob Suderman   }
227e7084713SRob Suderman 
228c4a04059SChristian Sigg   LLVM_DEBUG(forOp->print(llvm::dbgs() << "Modified loop\n"));
229e7084713SRob Suderman }
230e7084713SRob Suderman 
runOnOperation()23141574554SRiver Riddle void LoopInvariantCodeMotion::runOnOperation() {
232e7084713SRob Suderman   // Walk through all loops in a function in innermost-loop-first order.  This
233e7084713SRob Suderman   // way, we first LICM from the inner loop, and place the ops in
234e7084713SRob Suderman   // the outer loop, which in turn can be further LICM'ed.
23541574554SRiver Riddle   getOperation().walk([&](AffineForOp op) {
236c4a04059SChristian Sigg     LLVM_DEBUG(op->print(llvm::dbgs() << "\nOriginal loop\n"));
237e7084713SRob Suderman     runOnAffineForOp(op);
238e7084713SRob Suderman   });
239e7084713SRob Suderman }
240e7084713SRob Suderman 
24158ceae95SRiver Riddle std::unique_ptr<OperationPass<func::FuncOp>>
createAffineLoopInvariantCodeMotionPass()242e7084713SRob Suderman mlir::createAffineLoopInvariantCodeMotionPass() {
243e7084713SRob Suderman   return std::make_unique<LoopInvariantCodeMotion>();
244e7084713SRob Suderman }
245