diff options
Diffstat (limited to 'mlir/lib/Transforms/LoopInvariantCodeMotion.cpp')
-rw-r--r-- | mlir/lib/Transforms/LoopInvariantCodeMotion.cpp | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/mlir/lib/Transforms/LoopInvariantCodeMotion.cpp b/mlir/lib/Transforms/LoopInvariantCodeMotion.cpp new file mode 100644 index 00000000000..fb3d0c0b45c --- /dev/null +++ b/mlir/lib/Transforms/LoopInvariantCodeMotion.cpp @@ -0,0 +1,140 @@ +//===- LoopInvariantCodeMotion.cpp - Code to perform loop fusion-----------===// +// +// Part of the MLIR Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements loop invariant code motion. +// +//===----------------------------------------------------------------------===// + +#include "mlir/Transforms/Passes.h" + +#include "mlir/IR/Builders.h" +#include "mlir/IR/Function.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/LoopLikeInterface.h" +#include "mlir/Transforms/SideEffectsInterface.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "licm" + +using namespace mlir; + +namespace { + +using SideEffecting = SideEffectsInterface::SideEffecting; + +/// Loop invariant code motion (LICM) pass. +struct LoopInvariantCodeMotion : public OperationPass<LoopInvariantCodeMotion> { +public: + void runOnOperation() override; +}; + +// Checks whether the given op can be hoisted by checking that +// - the op and any of its contained operations do not depend on SSA values +// defined inside of the loop (by means of calling definedOutside). +// - the op has no side-effects. If sideEffecting is Never, sideeffects of this +// op and its nested ops are ignored. +static bool canBeHoisted(Operation *op, + function_ref<bool(Value)> definedOutside, + SideEffecting sideEffecting, + SideEffectsInterface &interface) { + // Check that dependencies are defined outside of loop. + if (!llvm::all_of(op->getOperands(), definedOutside)) + return false; + // Check whether this op is side-effect free. If we already know that there + // can be no side-effects because the surrounding op has claimed so, we can + // (and have to) skip this step. + auto thisOpIsSideEffecting = sideEffecting; + if (thisOpIsSideEffecting != SideEffecting::Never) { + thisOpIsSideEffecting = interface.isSideEffecting(op); + // If the op always has sideeffects, we cannot hoist. + if (thisOpIsSideEffecting == SideEffecting::Always) + return false; + } + // Recurse into the regions for this op and check whether the contained ops + // can be hoisted. + for (auto ®ion : op->getRegions()) { + for (auto &block : region.getBlocks()) { + for (auto &innerOp : block) { + if (innerOp.isKnownTerminator()) + continue; + if (!canBeHoisted(&innerOp, definedOutside, thisOpIsSideEffecting, + interface)) + return false; + } + } + } + return true; +} + +static LogicalResult moveLoopInvariantCode(LoopLikeOpInterface looplike, + SideEffectsInterface &interface) { + auto &loopBody = looplike.getLoopBody(); + + // We use two collections here as we need to preserve the order for insertion + // and this is easiest. + SmallPtrSet<Operation *, 8> willBeMovedSet; + SmallVector<Operation *, 8> opsToMove; + + // Helper to check whether an operation is loop invariant wrt. SSA properties. + auto isDefinedOutsideOfBody = [&](Value value) { + auto definingOp = value->getDefiningOp(); + return (definingOp && !!willBeMovedSet.count(definingOp)) || + looplike.isDefinedOutsideOfLoop(value); + }; + + // Do not use walk here, as we do not want to go into nested regions and hoist + // operations from there. These regions might have semantics unknown to this + // rewriting. If the nested regions are loops, they will have been processed. + for (auto &block : loopBody) { + for (auto &op : block.without_terminator()) { + if (canBeHoisted(&op, isDefinedOutsideOfBody, + mlir::SideEffectsDialectInterface::Recursive, + interface)) { + opsToMove.push_back(&op); + willBeMovedSet.insert(&op); + } + } + } + + // For all instructions that we found to be invariant, move outside of the + // loop. + auto result = looplike.moveOutOfLoop(opsToMove); + LLVM_DEBUG(looplike.print(llvm::dbgs() << "Modified loop\n")); + return result; +} + +} // end anonymous namespace + +void LoopInvariantCodeMotion::runOnOperation() { + SideEffectsInterface interface(&getContext()); + // Walk through all loops in a function in innermost-loop-first order. This + // way, we first LICM from the inner loop, and place the ops in + // the outer loop, which in turn can be further LICM'ed. + getOperation()->walk([&](Operation *op) { + if (auto looplike = dyn_cast<LoopLikeOpInterface>(op)) { + LLVM_DEBUG(op->print(llvm::dbgs() << "\nOriginal loop\n")); + if (failed(moveLoopInvariantCode(looplike, interface))) + signalPassFailure(); + } + }); +} + +// Include the generated code for the loop-like interface here, as it otherwise +// has no compilation unit. This works as loop-invariant code motion is the +// only user of that interface. +#include "mlir/Transforms/LoopLikeInterface.cpp.inc" + +std::unique_ptr<Pass> mlir::createLoopInvariantCodeMotionPass() { + return std::make_unique<LoopInvariantCodeMotion>(); +} + +static PassRegistration<LoopInvariantCodeMotion> + pass("loop-invariant-code-motion", + "Hoist loop invariant instructions outside of the loop"); |