blob: e097bb3cec8ddc9f024802ed8d38d680e9378bcb [file]
//===- LoopRangeFolding.cpp - Code to perform loop range folding-----------===//
//
// Part of the LLVM 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 range folding.
//
//===----------------------------------------------------------------------===//
#include "mlir/Dialect/SCF/Transforms/Passes.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/Dialect/SCF/Transforms/Transforms.h"
#include "mlir/Dialect/SCF/Utils/Utils.h"
#include "mlir/Dialect/Utils/StaticValueUtils.h"
#include "mlir/IR/IRMapping.h"
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
namespace mlir {
#define GEN_PASS_DEF_SCFFORLOOPRANGEFOLDING
#include "mlir/Dialect/SCF/Transforms/Passes.h.inc"
} // namespace mlir
using namespace mlir;
using namespace mlir::scf;
namespace {
struct ForLoopRangeFoldingPattern : public OpRewritePattern<ForOp> {
using Base::Base;
void initialize() { setHasBoundedRewriteRecursion(); }
LogicalResult matchAndRewrite(ForOp op,
PatternRewriter &rewriter) const override {
Value indVar = op.getInductionVar();
auto canBeFolded = [&](Value value) {
return op.isDefinedOutsideOfLoop(value) || value == indVar;
};
// If the induction variable is used by more than one operation, we can't
// fold its arith ops into the loop range. Note that a single operation
// may use the induction variable several times (e.g. `arith.addi %i,
// %i`), so check for a single user rather than a single use.
if (indVar.use_empty())
return failure();
Operation *user = *indVar.getUsers().begin();
if (!llvm::all_of(indVar.getUsers(),
[&](Operation *u) { return u == user; }))
return failure();
if (!isa<arith::AddIOp, arith::MulIOp>(user))
return failure();
if (!llvm::all_of(user->getOperands(), canBeFolded))
return failure();
if (auto mulOp = dyn_cast<arith::MulIOp>(user)) {
// Only fold if the multiplier is a known strictly positive constant.
// Multiplying by zero or a negative value would produce an invalid
// step (scf.for requires a strictly positive step).
Value multiplier =
(mulOp.getLhs() == indVar) ? mulOp.getRhs() : mulOp.getLhs();
std::optional<int64_t> multiplierVal = getConstantIntValue(multiplier);
if (!multiplierVal || *multiplierVal <= 0)
return failure();
}
rewriter.setInsertionPoint(op);
IRMapping lbMap;
lbMap.map(indVar, op.getLowerBound());
IRMapping ubMap;
ubMap.map(indVar, op.getUpperBound());
IRMapping stepMap;
stepMap.map(indVar, op.getStep());
Operation *lbFold = rewriter.clone(*user, lbMap);
Operation *ubFold = rewriter.clone(*user, ubMap);
Operation *stepFold = nullptr;
if (auto addOp = dyn_cast<arith::AddIOp>(user)) {
// `arith.addi %i, %i` is `2 * %i`, so the step has to be doubled too.
if (addOp.getLhs() == indVar && addOp.getRhs() == indVar)
stepFold = rewriter.clone(*user, stepMap);
} else {
stepFold = rewriter.clone(*user, stepMap);
}
rewriter.modifyOpInPlace(op, [&]() {
op.setLowerBound(lbFold->getResult(0));
op.setUpperBound(ubFold->getResult(0));
if (stepFold)
op.setStep(stepFold->getResult(0));
});
rewriter.replaceOp(user, indVar);
return success();
}
};
struct ForLoopRangeFolding
: public impl::SCFForLoopRangeFoldingBase<ForLoopRangeFolding> {
void runOnOperation() override;
};
} // namespace
void ForLoopRangeFolding::runOnOperation() {
RewritePatternSet patterns(&getContext());
patterns.add<ForLoopRangeFoldingPattern>(&getContext());
GreedyRewriteConfig config;
config.setStrictness(GreedyRewriteStrictness::ExistingOps)
.enableFolding(false)
.enableConstantCSE(false)
.setRegionSimplificationLevel(GreedySimplifyRegionLevel::Disabled);
if (failed(
applyPatternsGreedily(getOperation(), std::move(patterns), config)))
signalPassFailure();
}
std::unique_ptr<Pass> mlir::createForLoopRangeFoldingPass() {
return std::make_unique<ForLoopRangeFolding>();
}