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