#include "PassDetail.h"
#include "mlir/Dialect/Arithmetic/IR/Arithmetic.h"
#include "mlir/Dialect/SCF/IR/SCF.h"
#include "mlir/Dialect/SCF/Transforms/Patterns.h"
#include "mlir/Dialect/SCF/Transforms/Transforms.h"
#include "mlir/Dialect/SCF/Utils/Utils.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Support/MathExtras.h"
using namespace mlir;
using namespace mlir::scf;
namespace {
struct LoopPipelinerInternal {
struct LiverangeInfo {
unsigned lastUseStage = 0;
unsigned defStage = 0;
};
protected:
ForOp forOp;
unsigned maxStage = 0;
DenseMap<Operation *, unsigned> stages;
std::vector<Operation *> opOrder;
int64_t ub;
int64_t lb;
int64_t step;
PipeliningOption::AnnotationlFnType annotateFn = nullptr;
bool peelEpilogue;
PipeliningOption::PredicateOpFn predicateFn = nullptr;
DenseMap<Value, llvm::SmallVector<Value>> valueMapping;
void setValueMapping(Value key, Value el, int64_t idx);
public:
bool initializeLoopInfo(ForOp op, const PipeliningOption &options);
void emitPrologue(PatternRewriter &rewriter);
llvm::MapVector<Value, LiverangeInfo> analyzeCrossStageValues();
scf::ForOp createKernelLoop(
const llvm::MapVector<Value, LiverangeInfo> &crossStageValues,
PatternRewriter &rewriter,
llvm::DenseMap<std::pair<Value, unsigned>, unsigned> &loopArgMap);
void createKernel(
scf::ForOp newForOp,
const llvm::MapVector<Value, LiverangeInfo> &crossStageValues,
const llvm::DenseMap<std::pair<Value, unsigned>, unsigned> &loopArgMap,
PatternRewriter &rewriter);
llvm::SmallVector<Value> emitEpilogue(PatternRewriter &rewriter);
};
bool LoopPipelinerInternal::initializeLoopInfo(
ForOp op, const PipeliningOption &options) {
forOp = op;
auto upperBoundCst =
forOp.getUpperBound().getDefiningOp<arith::ConstantIndexOp>();
auto lowerBoundCst =
forOp.getLowerBound().getDefiningOp<arith::ConstantIndexOp>();
auto stepCst = forOp.getStep().getDefiningOp<arith::ConstantIndexOp>();
if (!upperBoundCst || !lowerBoundCst || !stepCst)
return false;
ub = upperBoundCst.value();
lb = lowerBoundCst.value();
step = stepCst.value();
peelEpilogue = options.peelEpilogue;
predicateFn = options.predicateFn;
if (!peelEpilogue && predicateFn == nullptr)
return false;
int64_t numIteration = ceilDiv(ub - lb, step);
std::vector<std::pair<Operation *, unsigned>> schedule;
options.getScheduleFn(forOp, schedule);
if (schedule.empty())
return false;
opOrder.reserve(schedule.size());
for (auto &opSchedule : schedule) {
maxStage = std::max(maxStage, opSchedule.second);
stages[opSchedule.first] = opSchedule.second;
opOrder.push_back(opSchedule.first);
}
if (numIteration <= maxStage)
return false;
if (forOp
.walk([this](Operation *op) {
if (op != forOp.getOperation() && !isa<scf::YieldOp>(op) &&
stages.find(op) == stages.end())
return WalkResult::interrupt();
return WalkResult::advance();
})
.wasInterrupted())
return false;
if (llvm::any_of(forOp.getBody()->getTerminator()->getOperands(),
[this](Value operand) {
Operation *def = operand.getDefiningOp();
return !def || stages.find(def) == stages.end();
}))
return false;
annotateFn = options.annotateFn;
return true;
}
void LoopPipelinerInternal::emitPrologue(PatternRewriter &rewriter) {
for (BlockArgument &arg : forOp.getRegionIterArgs()) {
OpOperand &operand = forOp.getOpOperandForRegionIterArg(arg);
setValueMapping(arg, operand.get(), 0);
}
auto yield = cast<scf::YieldOp>(forOp.getBody()->getTerminator());
for (int64_t i = 0; i < maxStage; i++) {
Value iv =
rewriter.create<arith::ConstantIndexOp>(forOp.getLoc(), lb + i * step);
setValueMapping(forOp.getInductionVar(), iv, i);
for (Operation *op : opOrder) {
if (stages[op] > i)
continue;
Operation *newOp = rewriter.clone(*op);
for (unsigned opIdx = 0; opIdx < op->getNumOperands(); opIdx++) {
auto it = valueMapping.find(op->getOperand(opIdx));
if (it != valueMapping.end())
newOp->setOperand(opIdx, it->second[i - stages[op]]);
}
if (annotateFn)
annotateFn(newOp, PipeliningOption::PipelinerPart::Prologue, i);
for (unsigned destId : llvm::seq(unsigned(0), op->getNumResults())) {
setValueMapping(op->getResult(destId), newOp->getResult(destId),
i - stages[op]);
for (OpOperand &operand : yield->getOpOperands()) {
if (operand.get() != op->getResult(destId))
continue;
setValueMapping(forOp.getRegionIterArgs()[operand.getOperandNumber()],
newOp->getResult(destId), i - stages[op] + 1);
}
}
}
}
}
llvm::MapVector<Value, LoopPipelinerInternal::LiverangeInfo>
LoopPipelinerInternal::analyzeCrossStageValues() {
llvm::MapVector<Value, LoopPipelinerInternal::LiverangeInfo> crossStageValues;
for (Operation *op : opOrder) {
unsigned stage = stages[op];
for (OpOperand &operand : op->getOpOperands()) {
Operation *def = operand.get().getDefiningOp();
if (!def)
continue;
auto defStage = stages.find(def);
if (defStage == stages.end() || defStage->second == stage)
continue;
assert(stage > defStage->second);
LiverangeInfo &info = crossStageValues[operand.get()];
info.defStage = defStage->second;
info.lastUseStage = std::max(info.lastUseStage, stage);
}
}
return crossStageValues;
}
scf::ForOp LoopPipelinerInternal::createKernelLoop(
const llvm::MapVector<Value, LoopPipelinerInternal::LiverangeInfo>
&crossStageValues,
PatternRewriter &rewriter,
llvm::DenseMap<std::pair<Value, unsigned>, unsigned> &loopArgMap) {
llvm::SmallVector<Value> newLoopArg;
for (const auto &retVal :
llvm::enumerate(forOp.getBody()->getTerminator()->getOperands())) {
Operation *def = retVal.value().getDefiningOp();
assert(def && "Only support loop carried dependencies of distance 1");
unsigned defStage = stages[def];
Value valueVersion = valueMapping[forOp.getRegionIterArgs()[retVal.index()]]
[maxStage - defStage];
assert(valueVersion);
newLoopArg.push_back(valueVersion);
}
for (auto escape : crossStageValues) {
LiverangeInfo &info = escape.second;
Value value = escape.first;
for (unsigned stageIdx = 0; stageIdx < info.lastUseStage - info.defStage;
stageIdx++) {
Value valueVersion =
valueMapping[value][maxStage - info.lastUseStage + stageIdx];
assert(valueVersion);
newLoopArg.push_back(valueVersion);
loopArgMap[std::make_pair(value, info.lastUseStage - info.defStage -
stageIdx)] = newLoopArg.size() - 1;
}
}
Value newUb = forOp.getUpperBound();
if (peelEpilogue)
newUb = rewriter.create<arith::ConstantIndexOp>(forOp.getLoc(),
ub - maxStage * step);
auto newForOp =
rewriter.create<scf::ForOp>(forOp.getLoc(), forOp.getLowerBound(), newUb,
forOp.getStep(), newLoopArg);
return newForOp;
}
void LoopPipelinerInternal::createKernel(
scf::ForOp newForOp,
const llvm::MapVector<Value, LoopPipelinerInternal::LiverangeInfo>
&crossStageValues,
const llvm::DenseMap<std::pair<Value, unsigned>, unsigned> &loopArgMap,
PatternRewriter &rewriter) {
valueMapping.clear();
rewriter.setInsertionPoint(newForOp.getBody(), newForOp.getBody()->begin());
BlockAndValueMapping mapping;
mapping.map(forOp.getInductionVar(), newForOp.getInductionVar());
for (const auto &arg : llvm::enumerate(forOp.getRegionIterArgs())) {
mapping.map(arg.value(), newForOp.getRegionIterArgs()[arg.index()]);
}
SmallVector<Value> predicates(maxStage + 1, nullptr);
if (!peelEpilogue) {
for (unsigned i = 0; i < maxStage; i++) {
Value c = rewriter.create<arith::ConstantIndexOp>(
newForOp.getLoc(), ub - (maxStage - i) * step);
Value pred = rewriter.create<arith::CmpIOp>(
newForOp.getLoc(), arith::CmpIPredicate::slt,
newForOp.getInductionVar(), c);
predicates[i] = pred;
}
}
for (Operation *op : opOrder) {
int64_t useStage = stages[op];
auto *newOp = rewriter.clone(*op, mapping);
for (OpOperand &operand : op->getOpOperands()) {
if (operand.get() == forOp.getInductionVar()) {
rewriter.setInsertionPoint(newOp);
Value offset = rewriter.create<arith::ConstantIndexOp>(
forOp.getLoc(), (maxStage - stages[op]) * step);
Value iv = rewriter.create<arith::AddIOp>(
forOp.getLoc(), newForOp.getInductionVar(), offset);
newOp->setOperand(operand.getOperandNumber(), iv);
rewriter.setInsertionPointAfter(newOp);
continue;
}
auto arg = operand.get().dyn_cast<BlockArgument>();
if (arg && arg.getOwner() == forOp.getBody()) {
Value ret = forOp.getBody()->getTerminator()->getOperand(
arg.getArgNumber() - 1);
Operation *dep = ret.getDefiningOp();
if (!dep)
continue;
auto stageDep = stages.find(dep);
if (stageDep == stages.end() || stageDep->second == useStage)
continue;
assert(stageDep->second == useStage + 1);
newOp->setOperand(operand.getOperandNumber(),
mapping.lookupOrDefault(ret));
continue;
}
Operation *def = operand.get().getDefiningOp();
if (!def)
continue;
auto stageDef = stages.find(def);
if (stageDef == stages.end() || stageDef->second == useStage)
continue;
auto remap = loopArgMap.find(
std::make_pair(operand.get(), useStage - stageDef->second));
assert(remap != loopArgMap.end());
newOp->setOperand(operand.getOperandNumber(),
newForOp.getRegionIterArgs()[remap->second]);
}
if (predicates[useStage]) {
newOp = predicateFn(newOp, predicates[useStage], rewriter);
for (auto values : llvm::zip(op->getResults(), newOp->getResults()))
mapping.map(std::get<0>(values), std::get<1>(values));
}
rewriter.setInsertionPointAfter(newOp);
if (annotateFn)
annotateFn(newOp, PipeliningOption::PipelinerPart::Kernel, 0);
}
llvm::SmallVector<Value> yieldOperands;
for (Value retVal : forOp.getBody()->getTerminator()->getOperands()) {
yieldOperands.push_back(mapping.lookupOrDefault(retVal));
}
for (auto &it : crossStageValues) {
int64_t version = maxStage - it.second.lastUseStage + 1;
unsigned numVersionReturned = it.second.lastUseStage - it.second.defStage;
for (unsigned i = 1; i < numVersionReturned; i++) {
setValueMapping(it.first, newForOp->getResult(yieldOperands.size()),
version++);
yieldOperands.push_back(
newForOp.getBody()->getArguments()[yieldOperands.size() + 1 +
newForOp.getNumInductionVars()]);
}
setValueMapping(it.first, newForOp->getResult(yieldOperands.size()),
version++);
yieldOperands.push_back(mapping.lookupOrDefault(it.first));
}
for (const auto &retVal :
llvm::enumerate(forOp.getBody()->getTerminator()->getOperands())) {
Operation *def = retVal.value().getDefiningOp();
assert(def && "Only support loop carried dependencies of distance 1");
unsigned defStage = stages[def];
setValueMapping(forOp.getRegionIterArgs()[retVal.index()],
newForOp->getResult(retVal.index()),
maxStage - defStage + 1);
}
rewriter.create<scf::YieldOp>(forOp.getLoc(), yieldOperands);
}
llvm::SmallVector<Value>
LoopPipelinerInternal::emitEpilogue(PatternRewriter &rewriter) {
llvm::SmallVector<Value> returnValues(forOp->getNumResults());
for (int64_t i = 0; i < maxStage; i++) {
Value newlastIter = rewriter.create<arith::ConstantIndexOp>(
forOp.getLoc(), lb + step * ((((ub - 1) - lb) / step) - i));
setValueMapping(forOp.getInductionVar(), newlastIter, maxStage - i);
}
for (int64_t i = 1; i <= maxStage; i++) {
for (Operation *op : opOrder) {
if (stages[op] < i)
continue;
Operation *newOp = rewriter.clone(*op);
for (unsigned opIdx = 0; opIdx < op->getNumOperands(); opIdx++) {
auto it = valueMapping.find(op->getOperand(opIdx));
if (it != valueMapping.end()) {
Value v = it->second[maxStage - stages[op] + i];
assert(v);
newOp->setOperand(opIdx, v);
}
}
if (annotateFn)
annotateFn(newOp, PipeliningOption::PipelinerPart::Epilogue, i - 1);
for (unsigned destId : llvm::seq(unsigned(0), op->getNumResults())) {
setValueMapping(op->getResult(destId), newOp->getResult(destId),
maxStage - stages[op] + i);
for (OpOperand &operand :
forOp.getBody()->getTerminator()->getOpOperands()) {
if (operand.get() != op->getResult(destId))
continue;
unsigned version = maxStage - stages[op] + i + 1;
if (version > maxStage) {
returnValues[operand.getOperandNumber()] = newOp->getResult(destId);
continue;
}
setValueMapping(forOp.getRegionIterArgs()[operand.getOperandNumber()],
newOp->getResult(destId), version);
}
}
}
}
return returnValues;
}
void LoopPipelinerInternal::setValueMapping(Value key, Value el, int64_t idx) {
auto it = valueMapping.find(key);
if (it == valueMapping.end())
it =
valueMapping
.insert(std::make_pair(key, llvm::SmallVector<Value>(maxStage + 1)))
.first;
it->second[idx] = el;
}
}
FailureOr<ForOp> ForLoopPipeliningPattern::returningMatchAndRewrite(
ForOp forOp, PatternRewriter &rewriter) const {
LoopPipelinerInternal pipeliner;
if (!pipeliner.initializeLoopInfo(forOp, options))
return failure();
pipeliner.emitPrologue(rewriter);
llvm::MapVector<Value, LoopPipelinerInternal::LiverangeInfo>
crossStageValues = pipeliner.analyzeCrossStageValues();
llvm::DenseMap<std::pair<Value, unsigned>, unsigned> loopArgMap;
ForOp newForOp =
pipeliner.createKernelLoop(crossStageValues, rewriter, loopArgMap);
pipeliner.createKernel(newForOp, crossStageValues, loopArgMap, rewriter);
llvm::SmallVector<Value> returnValues =
newForOp.getResults().take_front(forOp->getNumResults());
if (options.peelEpilogue) {
rewriter.setInsertionPointAfter(newForOp);
returnValues = pipeliner.emitEpilogue(rewriter);
}
if (forOp->getNumResults() > 0)
rewriter.replaceOp(forOp, returnValues);
else
rewriter.eraseOp(forOp);
return newForOp;
}
void mlir::scf::populateSCFLoopPipeliningPatterns(
RewritePatternSet &patterns, const PipeliningOption &options) {
patterns.add<ForLoopPipeliningPattern>(options, patterns.getContext());
}