blob: 18c82578acb1247b69d07d91619cfb0fde70920b [file] [log] [blame]
Amit Sabne7905da62019-04-17 19:18:371//===- LoopInvariantCodeMotion.cpp - Code to perform loop fusion-----------===//
2//
Mehdi Amini30857102020-01-26 03:58:303// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
Mehdi Amini56222a02019-12-23 17:35:364// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
Amit Sabne7905da62019-04-17 19:18:376//
Mehdi Amini56222a02019-12-23 17:35:367//===----------------------------------------------------------------------===//
Amit Sabne7905da62019-04-17 19:18:378//
9// This file implements loop invariant code motion.
10//
11//===----------------------------------------------------------------------===//
12
River Riddle1834ad4a2020-04-07 20:58:1213#include "PassDetail.h"
Amit Sabne7905da62019-04-17 19:18:3714#include "mlir/Transforms/Passes.h"
Stephan Herhutb843cc52019-10-16 11:28:1315
16#include "mlir/IR/Builders.h"
17#include "mlir/IR/Function.h"
River Riddle43959a22020-03-14 20:36:4218#include "mlir/Interfaces/LoopLikeInterface.h"
Stephen Neuendorffereb623ae2020-05-13 17:27:1919#include "mlir/Interfaces/SideEffectInterfaces.h"
Nicolas Vasilache6953cf62020-06-05 10:35:4620#include "mlir/Transforms/LoopUtils.h"
Amit Sabne7a43da62019-05-31 20:56:4721#include "llvm/ADT/SmallPtrSet.h"
Amit Sabne7905da62019-04-17 19:18:3722#include "llvm/Support/CommandLine.h"
23#include "llvm/Support/Debug.h"
Amit Sabne7905da62019-04-17 19:18:3724
25#define DEBUG_TYPE "licm"
26
Amit Sabne7905da62019-04-17 19:18:3727using namespace mlir;
28
29namespace {
Amit Sabne7905da62019-04-17 19:18:3730/// Loop invariant code motion (LICM) pass.
River Riddle80aca1e2020-04-07 20:56:1631struct LoopInvariantCodeMotion
River Riddle1834ad4a2020-04-07 20:58:1232 : public LoopInvariantCodeMotionBase<LoopInvariantCodeMotion> {
Stephan Herhutb843cc52019-10-16 11:28:1333 void runOnOperation() override;
Amit Sabne7905da62019-04-17 19:18:3734};
Rahul Joshi60f914e2020-06-24 03:21:4235} // end anonymous namespace
Amit Sabne7905da62019-04-17 19:18:3736
Stephan Herhutb843cc52019-10-16 11:28:1337// Checks whether the given op can be hoisted by checking that
38// - the op and any of its contained operations do not depend on SSA values
39// defined inside of the loop (by means of calling definedOutside).
40// - the op has no side-effects. If sideEffecting is Never, sideeffects of this
41// op and its nested ops are ignored.
Rahul Joshi60f914e2020-06-24 03:21:4242static bool canBeHoisted(Operation *op,
43 function_ref<bool(Value)> definedOutside) {
Stephan Herhutb843cc52019-10-16 11:28:1344 // Check that dependencies are defined outside of loop.
45 if (!llvm::all_of(op->getOperands(), definedOutside))
46 return false;
47 // Check whether this op is side-effect free. If we already know that there
48 // can be no side-effects because the surrounding op has claimed so, we can
49 // (and have to) skip this step.
River Riddleb10c6622020-03-09 23:01:4150 if (auto memInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
51 if (!memInterface.hasNoEffect())
Amit Sabne7a43da62019-05-31 20:56:4752 return false;
River Riddle0ddba0b2020-03-12 21:06:4153 // If the operation doesn't have side effects and it doesn't recursively
54 // have side effects, it can always be hoisted.
55 if (!op->hasTrait<OpTrait::HasRecursiveSideEffects>())
56 return true;
57
58 // Otherwise, if the operation doesn't provide the memory effect interface
59 // and it doesn't have recursive side effects we treat it conservatively as
60 // side-effecting.
61 } else if (!op->hasTrait<OpTrait::HasRecursiveSideEffects>()) {
River Riddleb10c6622020-03-09 23:01:4162 return false;
Stephan Herhutb843cc52019-10-16 11:28:1363 }
River Riddleb10c6622020-03-09 23:01:4164
Stephan Herhutb843cc52019-10-16 11:28:1365 // Recurse into the regions for this op and check whether the contained ops
66 // can be hoisted.
67 for (auto &region : op->getRegions()) {
Rahul Joshid1506622020-06-19 19:33:2168 for (auto &block : region) {
River Riddleb10c6622020-03-09 23:01:4169 for (auto &innerOp : block.without_terminator())
70 if (!canBeHoisted(&innerOp, definedOutside))
Amit Sabne7a43da62019-05-31 20:56:4771 return false;
Amit Sabne7905da62019-04-17 19:18:3772 }
73 }
Amit Sabne7a43da62019-05-31 20:56:4774 return true;
75}
76
Rahul Joshie7f71372020-06-24 00:23:3577
Nicolas Vasilache6953cf62020-06-05 10:35:4678LogicalResult mlir::moveLoopInvariantCode(LoopLikeOpInterface looplike) {
Stephan Herhutb843cc52019-10-16 11:28:1379 auto &loopBody = looplike.getLoopBody();
Amit Sabne7a43da62019-05-31 20:56:4780
Stephan Herhutb843cc52019-10-16 11:28:1381 // We use two collections here as we need to preserve the order for insertion
82 // and this is easiest.
83 SmallPtrSet<Operation *, 8> willBeMovedSet;
Amit Sabne7a43da62019-05-31 20:56:4784 SmallVector<Operation *, 8> opsToMove;
85
Stephan Herhutb843cc52019-10-16 11:28:1386 // Helper to check whether an operation is loop invariant wrt. SSA properties.
River Riddlee62a6952019-12-23 22:45:0187 auto isDefinedOutsideOfBody = [&](Value value) {
River Riddle2bdf33c2020-01-11 16:54:0488 auto definingOp = value.getDefiningOp();
Stephan Herhutb843cc52019-10-16 11:28:1389 return (definingOp && !!willBeMovedSet.count(definingOp)) ||
90 looplike.isDefinedOutsideOfLoop(value);
91 };
92
93 // Do not use walk here, as we do not want to go into nested regions and hoist
94 // operations from there. These regions might have semantics unknown to this
95 // rewriting. If the nested regions are loops, they will have been processed.
96 for (auto &block : loopBody) {
97 for (auto &op : block.without_terminator()) {
River Riddleb10c6622020-03-09 23:01:4198 if (canBeHoisted(&op, isDefinedOutsideOfBody)) {
Stephan Herhutb843cc52019-10-16 11:28:1399 opsToMove.push_back(&op);
100 willBeMovedSet.insert(&op);
Amit Sabne7a43da62019-05-31 20:56:47101 }
102 }
103 }
104
Stephan Herhutb843cc52019-10-16 11:28:13105 // For all instructions that we found to be invariant, move outside of the
106 // loop.
107 auto result = looplike.moveOutOfLoop(opsToMove);
Rahul Joshie7f71372020-06-24 00:23:35108 LLVM_DEBUG(looplike.print(llvm::dbgs() << "\n\nModified loop:\n"));
Stephan Herhutb843cc52019-10-16 11:28:13109 return result;
Amit Sabne7905da62019-04-17 19:18:37110}
111
Stephan Herhutb843cc52019-10-16 11:28:13112void LoopInvariantCodeMotion::runOnOperation() {
Stephan Herhutb843cc52019-10-16 11:28:13113 // Walk through all loops in a function in innermost-loop-first order. This
Chris Lattner0134b5d2019-05-11 15:28:15114 // way, we first LICM from the inner loop, and place the ops in
115 // the outer loop, which in turn can be further LICM'ed.
River Riddleb10c6622020-03-09 23:01:41116 getOperation()->walk([&](LoopLikeOpInterface loopLike) {
Rahul Joshie7f71372020-06-24 00:23:35117 LLVM_DEBUG(loopLike.print(llvm::dbgs() << "\nOriginal loop:\n"));
River Riddleb10c6622020-03-09 23:01:41118 if (failed(moveLoopInvariantCode(loopLike)))
119 signalPassFailure();
Chris Lattner0134b5d2019-05-11 15:28:15120 });
Amit Sabne7905da62019-04-17 19:18:37121}
122
Stephan Herhutb843cc52019-10-16 11:28:13123std::unique_ptr<Pass> mlir::createLoopInvariantCodeMotionPass() {
124 return std::make_unique<LoopInvariantCodeMotion>();
125}