17905da65SAmit Sabne //===- LoopInvariantCodeMotion.cpp - Code to perform loop fusion-----------===// 27905da65SAmit Sabne // 37905da65SAmit Sabne // Copyright 2019 The MLIR Authors. 47905da65SAmit Sabne // 57905da65SAmit Sabne // Licensed under the Apache License, Version 2.0 (the "License"); 67905da65SAmit Sabne // you may not use this file except in compliance with the License. 77905da65SAmit Sabne // You may obtain a copy of the License at 87905da65SAmit Sabne // 97905da65SAmit Sabne // http://www.apache.org/licenses/LICENSE-2.0 107905da65SAmit Sabne // 117905da65SAmit Sabne // Unless required by applicable law or agreed to in writing, software 127905da65SAmit Sabne // distributed under the License is distributed on an "AS IS" BASIS, 137905da65SAmit Sabne // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 147905da65SAmit Sabne // See the License for the specific language governing permissions and 157905da65SAmit Sabne // limitations under the License. 167905da65SAmit Sabne // ============================================================================= 177905da65SAmit Sabne // 187905da65SAmit Sabne // This file implements loop invariant code motion. 197905da65SAmit Sabne // 207905da65SAmit Sabne //===----------------------------------------------------------------------===// 217905da65SAmit Sabne 227905da65SAmit Sabne #include "mlir/Transforms/Passes.h" 23*b843cc5dSStephan Herhut 24*b843cc5dSStephan Herhut #include "mlir/IR/Builders.h" 25*b843cc5dSStephan Herhut #include "mlir/IR/Function.h" 26*b843cc5dSStephan Herhut #include "mlir/Pass/Pass.h" 27*b843cc5dSStephan Herhut #include "mlir/Transforms/LoopLikeInterface.h" 28*b843cc5dSStephan Herhut #include "mlir/Transforms/SideEffectsInterface.h" 297a43da60SAmit Sabne #include "llvm/ADT/SmallPtrSet.h" 307905da65SAmit Sabne #include "llvm/Support/CommandLine.h" 317905da65SAmit Sabne #include "llvm/Support/Debug.h" 327905da65SAmit Sabne 337905da65SAmit Sabne #define DEBUG_TYPE "licm" 347905da65SAmit Sabne 357905da65SAmit Sabne using namespace mlir; 367905da65SAmit Sabne 377905da65SAmit Sabne namespace { 387905da65SAmit Sabne 39*b843cc5dSStephan Herhut using SideEffecting = SideEffectsInterface::SideEffecting; 40*b843cc5dSStephan Herhut 417905da65SAmit Sabne /// Loop invariant code motion (LICM) pass. 42*b843cc5dSStephan Herhut struct LoopInvariantCodeMotion : public OperationPass<LoopInvariantCodeMotion> { 43*b843cc5dSStephan Herhut public: 44*b843cc5dSStephan Herhut void runOnOperation() override; 457905da65SAmit Sabne }; 467905da65SAmit Sabne 47*b843cc5dSStephan Herhut // Checks whether the given op can be hoisted by checking that 48*b843cc5dSStephan Herhut // - the op and any of its contained operations do not depend on SSA values 49*b843cc5dSStephan Herhut // defined inside of the loop (by means of calling definedOutside). 50*b843cc5dSStephan Herhut // - the op has no side-effects. If sideEffecting is Never, sideeffects of this 51*b843cc5dSStephan Herhut // op and its nested ops are ignored. 52*b843cc5dSStephan Herhut static bool canBeHoisted(Operation *op, 53*b843cc5dSStephan Herhut llvm::function_ref<bool(Value *)> definedOutside, 54*b843cc5dSStephan Herhut SideEffecting sideEffecting, 55*b843cc5dSStephan Herhut SideEffectsInterface &interface) { 56*b843cc5dSStephan Herhut // Check that dependencies are defined outside of loop. 57*b843cc5dSStephan Herhut if (!llvm::all_of(op->getOperands(), definedOutside)) 58*b843cc5dSStephan Herhut return false; 59*b843cc5dSStephan Herhut // Check whether this op is side-effect free. If we already know that there 60*b843cc5dSStephan Herhut // can be no side-effects because the surrounding op has claimed so, we can 61*b843cc5dSStephan Herhut // (and have to) skip this step. 62*b843cc5dSStephan Herhut auto thisOpIsSideEffecting = sideEffecting; 63*b843cc5dSStephan Herhut if (thisOpIsSideEffecting != SideEffecting::Never) { 64*b843cc5dSStephan Herhut thisOpIsSideEffecting = interface.isSideEffecting(op); 65*b843cc5dSStephan Herhut // If the op always has sideeffects, we cannot hoist. 66*b843cc5dSStephan Herhut if (thisOpIsSideEffecting == SideEffecting::Always) 677a43da60SAmit Sabne return false; 687a43da60SAmit Sabne } 69*b843cc5dSStephan Herhut // Recurse into the regions for this op and check whether the contained ops 70*b843cc5dSStephan Herhut // can be hoisted. 71*b843cc5dSStephan Herhut for (auto ®ion : op->getRegions()) { 72*b843cc5dSStephan Herhut for (auto &block : region.getBlocks()) { 73*b843cc5dSStephan Herhut for (auto &innerOp : block) { 74*b843cc5dSStephan Herhut if (innerOp.isKnownTerminator()) 75*b843cc5dSStephan Herhut continue; 76*b843cc5dSStephan Herhut if (!canBeHoisted(&innerOp, definedOutside, thisOpIsSideEffecting, 77*b843cc5dSStephan Herhut interface)) 787a43da60SAmit Sabne return false; 797a43da60SAmit Sabne } 807a43da60SAmit Sabne } 817a43da60SAmit Sabne } 827a43da60SAmit Sabne return true; 837a43da60SAmit Sabne } 847a43da60SAmit Sabne 85*b843cc5dSStephan Herhut static LogicalResult moveLoopInvariantCode(LoopLikeOpInterface looplike, 86*b843cc5dSStephan Herhut SideEffectsInterface &interface) { 87*b843cc5dSStephan Herhut auto &loopBody = looplike.getLoopBody(); 887a43da60SAmit Sabne 89*b843cc5dSStephan Herhut // We use two collections here as we need to preserve the order for insertion 90*b843cc5dSStephan Herhut // and this is easiest. 91*b843cc5dSStephan Herhut SmallPtrSet<Operation *, 8> willBeMovedSet; 927905da65SAmit Sabne SmallVector<Operation *, 8> opsToMove; 937905da65SAmit Sabne 94*b843cc5dSStephan Herhut // Helper to check whether an operation is loop invariant wrt. SSA properties. 95*b843cc5dSStephan Herhut auto isDefinedOutsideOfBody = [&](Value *value) { 96*b843cc5dSStephan Herhut auto definingOp = value->getDefiningOp(); 97*b843cc5dSStephan Herhut return (definingOp && !!willBeMovedSet.count(definingOp)) || 98*b843cc5dSStephan Herhut looplike.isDefinedOutsideOfLoop(value); 99*b843cc5dSStephan Herhut }; 100*b843cc5dSStephan Herhut 101*b843cc5dSStephan Herhut // Do not use walk here, as we do not want to go into nested regions and hoist 102*b843cc5dSStephan Herhut // operations from there. These regions might have semantics unknown to this 103*b843cc5dSStephan Herhut // rewriting. If the nested regions are loops, they will have been processed. 104*b843cc5dSStephan Herhut for (auto &block : loopBody) { 105*b843cc5dSStephan Herhut for (auto &op : block.without_terminator()) { 106*b843cc5dSStephan Herhut if (canBeHoisted(&op, isDefinedOutsideOfBody, 107*b843cc5dSStephan Herhut mlir::SideEffectsDialectInterface::Recursive, 108*b843cc5dSStephan Herhut interface)) { 1097905da65SAmit Sabne opsToMove.push_back(&op); 110*b843cc5dSStephan Herhut willBeMovedSet.insert(&op); 1117905da65SAmit Sabne } 1127a43da60SAmit Sabne } 1137a43da60SAmit Sabne } 1147905da65SAmit Sabne 115*b843cc5dSStephan Herhut // For all instructions that we found to be invariant, move outside of the 116*b843cc5dSStephan Herhut // loop. 117*b843cc5dSStephan Herhut auto result = looplike.moveOutOfLoop(opsToMove); 118*b843cc5dSStephan Herhut LLVM_DEBUG(looplike.print(llvm::dbgs() << "Modified loop\n")); 119*b843cc5dSStephan Herhut return result; 1207905da65SAmit Sabne } 1217905da65SAmit Sabne 122*b843cc5dSStephan Herhut } // end anonymous namespace 1237905da65SAmit Sabne 124*b843cc5dSStephan Herhut void LoopInvariantCodeMotion::runOnOperation() { 125*b843cc5dSStephan Herhut SideEffectsInterface interface(&getContext()); 1260134b5dfSChris Lattner // Walk through all loops in a function in innermost-loop-first order. This 1270134b5dfSChris Lattner // way, we first LICM from the inner loop, and place the ops in 1280134b5dfSChris Lattner // the outer loop, which in turn can be further LICM'ed. 129*b843cc5dSStephan Herhut getOperation()->walk([&](Operation *op) { 130*b843cc5dSStephan Herhut if (auto looplike = dyn_cast<LoopLikeOpInterface>(op)) { 131*b843cc5dSStephan Herhut LLVM_DEBUG(op->print(llvm::dbgs() << "\nOriginal loop\n")); 132*b843cc5dSStephan Herhut if (failed(moveLoopInvariantCode(looplike, interface))) 133*b843cc5dSStephan Herhut signalPassFailure(); 134*b843cc5dSStephan Herhut } 1350134b5dfSChris Lattner }); 1367905da65SAmit Sabne } 1377905da65SAmit Sabne 138*b843cc5dSStephan Herhut // Include the generated code for the loop-like interface here, as it otherwise 139*b843cc5dSStephan Herhut // has no compilation unit. This works as loop-invariant code motion is the 140*b843cc5dSStephan Herhut // only user of that interface. 141*b843cc5dSStephan Herhut #include "mlir/Transforms/LoopLikeInterface.cpp.inc" 142*b843cc5dSStephan Herhut 143*b843cc5dSStephan Herhut std::unique_ptr<Pass> mlir::createLoopInvariantCodeMotionPass() { 144*b843cc5dSStephan Herhut return std::make_unique<LoopInvariantCodeMotion>(); 145*b843cc5dSStephan Herhut } 146*b843cc5dSStephan Herhut 1477905da65SAmit Sabne static PassRegistration<LoopInvariantCodeMotion> 148*b843cc5dSStephan Herhut pass("loop-invariant-code-motion", 1497905da65SAmit Sabne "Hoist loop invariant instructions outside of the loop"); 150