22#define GEN_PASS_DEF_SCFPARALLELLOOPTILING
23#include "mlir/Dialect/SCF/Transforms/Passes.h.inc"
57std::pair<ParallelOp, ParallelOp>
59 bool noMinMaxBounds) {
63 tileSizeConstants.reserve(op.getUpperBound().size());
64 for (
size_t i = 0, end = op.getUpperBound().size(); i != end; ++i) {
65 if (i < tileSizes.size())
66 tileSizeConstants.push_back(
70 tileSizeConstants.push_back(
76 newSteps.reserve(op.getStep().size());
77 for (
auto step : llvm::zip(op.getStep(), tileSizeConstants)) {
78 newSteps.push_back(arith::MulIOp::create(
b, op.getLoc(), std::get<0>(step),
81 auto outerLoop = ParallelOp::create(
b, op.getLoc(), op.getLowerBound(),
82 op.getUpperBound(), newSteps);
83 outerLoop.setUnsignedCmp(op.getUnsignedCmp());
84 b.setInsertionPointToStart(outerLoop.getBody());
96 newBounds.reserve(op.getUpperBound().size());
97 bool needInboundCheck =
false;
98 for (
auto [lowerBound, upperBound, newStep, iv, step, tileSizeConstant] :
99 llvm::zip(outerLoop.getLowerBound(), outerLoop.getUpperBound(),
100 outerLoop.getStep(), outerLoop.getInductionVars(),
101 op.getStep(), tileSizeConstants)) {
103 auto lowerBoundConstant =
105 auto upperBoundConstant =
109 cast<arith::ConstantIndexOp>(tileSizeConstant.getDefiningOp()).
value();
113 if (lowerBoundConstant && upperBoundConstant && stepConstant) {
114 auto numIterations = llvm::divideCeil(upperBoundConstant.
value() -
115 lowerBoundConstant.value(),
116 stepConstant.
value());
117 if (numIterations % tileSize == 0) {
118 newBounds.push_back(newStep);
124 if (noMinMaxBounds) {
125 newBounds.push_back(newStep);
126 needInboundCheck =
true;
133 affine::AffineMinOp::create(
b, op.getLoc(),
b.getIndexType(), minMap,
136 auto innerLoop = ParallelOp::create(
139 innerLoop.setUnsignedCmp(op.getUnsignedCmp());
141 if (noMinMaxBounds && needInboundCheck) {
142 b.setInsertionPointToStart(innerLoop.getBody());
146 arith::CmpIPredicate predicate = op.getUnsignedCmp()
147 ? arith::CmpIPredicate::ult
148 : arith::CmpIPredicate::slt;
149 for (
auto [outerUpperBound, outerIV, innerIV, innerStep] :
150 llvm::zip(outerLoop.getUpperBound(), outerLoop.getInductionVars(),
151 innerLoop.getInductionVars(), innerLoop.getStep())) {
156 arith::MulIOp::create(
b, op.getLoc(), innerIV, innerStep), outerIV);
157 Value dimInbound = arith::CmpIOp::create(
b, op.getLoc(), predicate,
index,
159 inbound = arith::AndIOp::create(
b, op.getLoc(), inbound, dimInbound);
161 auto ifInbound = IfOp::create(
b, op.getLoc(),
164 ifInbound.getThenRegion().takeBody(op.getRegion());
165 Block &thenBlock = ifInbound.getThenRegion().
front();
168 b.setInsertionPointToEnd(&thenBlock);
169 scf::YieldOp::create(
b, reduceOp->
getLoc());
171 b.setInsertionPointToStart(innerLoop.getBody());
172 for (
const auto &ivs : llvm::enumerate(llvm::zip(
173 innerLoop.getInductionVars(), outerLoop.getInductionVars()))) {
174 auto newIndex = arith::AddIOp::create(
175 b, op.getLoc(), std::get<0>(ivs.value()), std::get<1>(ivs.value()));
181 innerLoop.getRegion().takeBody(op.getRegion());
182 b.setInsertionPointToStart(innerLoop.getBody());
183 for (
auto ivs : llvm::zip(innerLoop.getInductionVars(),
184 outerLoop.getInductionVars())) {
185 Value innerIndex = std::get<0>(ivs);
186 auto newIndex = arith::AddIOp::create(
b, op.getLoc(), std::get<0>(ivs),
193 return std::make_pair(outerLoop, innerLoop);
197struct ParallelLoopTiling
199 ParallelLoopTiling() =
default;
200 explicit ParallelLoopTiling(ArrayRef<int64_t> tileSizes,
201 bool noMinMaxBounds =
false) {
202 this->tileSizes = tileSizes;
203 this->noMinMaxBounds = noMinMaxBounds;
206 void runOnOperation()
override {
207 for (
auto tileSize : tileSizes)
210 "tile size cannot be 0");
211 return signalPassFailure();
213 auto *parentOp = getOperation();
214 SmallVector<ParallelOp, 2> innermostPloops;
216 for (ParallelOp ploop : innermostPloops) {
218 if (ploop.getNumReductions() == 0)
227 bool noMinMaxBounds) {
228 return std::make_unique<ParallelLoopTiling>(tileSizes, noMinMaxBounds);
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
Operation * getTerminator()
Get the terminator operation of this block.
void eraseArguments(unsigned start, unsigned num)
Erases 'num' arguments from the index 'start'.
This class helps build Operations.
Operation is the basic unit of execution within MLIR.
Location getLoc()
The source location the operation was defined or derived from.
void erase()
Remove this operation from its parent block and delete it.
MLIRContext & getContext()
Return the MLIR context for the current operation being transformed.
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
void replaceAllUsesExcept(Value newValue, const SmallPtrSetImpl< Operation * > &exceptions)
Replace all uses of 'this' value with 'newValue', updating anything in the IR that uses 'this' to use...
Specialization of arith.constant op that returns an integer of index type.
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
static ConstantIntOp create(OpBuilder &builder, Location location, int64_t value, unsigned width)
std::pair< ParallelOp, ParallelOp > tileParallelLoop(ParallelOp op, llvm::ArrayRef< int64_t > tileSizes, bool noMinMaxBounds)
Tile a parallel loop of the form scf.parallel (i0, i1) = (arg0, arg1) to (arg2, arg3) step (arg4,...
Include the generated interface declarations.
std::unique_ptr< Pass > createParallelLoopTilingPass(llvm::ArrayRef< int64_t > tileSize={}, bool noMinMaxBounds=false)
Creates a pass which tiles innermost parallel loops.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
bool getInnermostParallelLoops(Operation *rootOp, SmallVectorImpl< scf::ParallelOp > &result)
Get a list of innermost parallel loops contained in rootOp.
AffineExpr getAffineDimExpr(unsigned position, MLIRContext *context)
These free functions allow clients of the API to not use classes in detail.