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