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 &region : 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