24#include "llvm/ADT/SmallVector.h"
27#define GEN_PASS_DEF_RAISESCFTOAFFINEPASS
28#include "mlir/Conversion/Passes.h.inc"
41 void runOnOperation()
override;
51 using OpRewritePattern<scf::ForOp>::OpRewritePattern;
53 LogicalResult matchAndRewrite(scf::ForOp op,
54 PatternRewriter &rewriter)
const override;
62 bool canRaiseToAffine(scf::ForOp op)
const;
69 void castBoundsToIndex(scf::ForOp op, PatternRewriter &rewriter)
const;
93 std::pair<affine::AffineForOp, Value>
94 createAffineFor(scf::ForOp op, PatternRewriter &rewriter)
const;
96 std::pair<affine::AffineForOp, Value>
97 createAffineForWithConstantStep(scf::ForOp op, int64_t step,
98 PatternRewriter &rewriter)
const;
100 std::pair<affine::AffineForOp, Value>
101 createAffineForWithDynamicStep(scf::ForOp op,
102 PatternRewriter &rewriter)
const;
108 "expected one operand per affine map input");
111 [&](
Value value) { return affine::isValidDim(value, scope); }) &&
113 return affine::isValidSymbol(value, scope);
117bool indexBoundsRaisable(scf::ForOp op) {
118 Value lb = op.getLowerBound();
120 IntegerAttr constAttr;
131 areValidAffineMapOperands(lbMaxOp.getAffineMap(),
132 lbMaxOp->getOperands(), scope));
133 auto ubMinOp =
ub.getDefiningOp<affine::AffineMinOp>();
136 (ubMinOp && areValidAffineMapOperands(ubMinOp.getAffineMap(),
137 ubMinOp->getOperands(), scope));
140 return lbOK && ubOK && stepOK;
147bool intBoundsRaisable(scf::ForOp op, IntegerType intType) {
149 .getTypeSizeInBits(IndexType::get(op.getContext()))
153 uint64_t requiredWidth = intType.getWidth() + (op.getUnsignedCmp() ? 1 : 0);
154 if (requiredWidth > indexWidth)
167bool ForOpRewrite::canRaiseToAffine(scf::ForOp op)
const {
168 Type type = op.getInductionVar().getType();
169 if (isa<IndexType>(type))
170 return indexBoundsRaisable(op);
171 if (
auto intType = dyn_cast<IntegerType>(type))
172 return intBoundsRaisable(op, intType);
176LogicalResult ForOpRewrite::matchAndRewrite(scf::ForOp op,
177 PatternRewriter &rewriter)
const {
178 if (!canRaiseToAffine(op)) {
182 if (!isa<IndexType>(op.getInductionVar().getType()))
183 castBoundsToIndex(op, rewriter);
185 auto [affineFor, oldIV] = createAffineFor(op, rewriter);
186 Block *affineBody = affineFor.getBody();
191 assert(isa<affine::AffineYieldOp>(terminator) &&
192 "expected affine.yield if there *might* be terminator");
196 SmallVector<Value> argValues;
197 argValues.push_back(oldIV);
198 llvm::append_range(argValues, affineFor.getRegionIterArgs());
202 auto scfYieldOp = cast<scf::YieldOp>(affineBody->
getTerminator());
205 scfYieldOp->getOperands());
211std::pair<affine::AffineForOp, Value>
212ForOpRewrite::createAffineFor(scf::ForOp op, PatternRewriter &rewriter)
const {
213 IntegerAttr constAttr;
215 int64_t step = constAttr.getInt();
216 assert(step > 0 &&
"scf.for has positive step");
217 return createAffineForWithConstantStep(op, step, rewriter);
219 return createAffineForWithDynamicStep(op, rewriter);
222std::pair<affine::AffineForOp, Value>
223ForOpRewrite::createAffineForWithConstantStep(scf::ForOp op, int64_t step,
224 PatternRewriter &rewriter)
const {
225 Value lb = op.getLowerBound();
226 Value ub = op.getUpperBound();
234 if (
auto ubMinOp = ub.
getDefiningOp<affine::AffineMinOp>()) {
235 ubOperands = ubMinOp->getOperands();
236 ubMap = ubMinOp.getAffineMap();
239 if (
auto lbMaxOp = lb.
getDefiningOp<affine::AffineMaxOp>()) {
240 lbOperands = lbMaxOp->getOperands();
241 lbMap = lbMaxOp.getAffineMap();
245 affine::AffineForOp::create(rewriter, op.getLoc(), lbOperands, lbMap,
246 ubOperands, ubMap, step, op.getInits());
248 return std::make_pair(affineFor, affineFor.getInductionVar());
251std::pair<affine::AffineForOp, Value>
252ForOpRewrite::createAffineForWithDynamicStep(scf::ForOp op,
253 PatternRewriter &rewriter)
const {
254 Value lb = op.getLowerBound();
255 Value ub = op.getUpperBound();
256 Value step = op.getStep();
259 "dynamic-step lower bound must be a valid affine dim");
266 llvm::SmallVector<Value, 3> ubOperands = {lb, ub, step};
271 AffineMap ubMap =
AffineMap::get(2, 1, (d1 - d0 + s0 - 1).floorDiv(s0));
273 if (
auto ubMinOp = ub.
getDefiningOp<affine::AffineMinOp>()) {
274 AffineMap origUbMap = ubMinOp.getAffineMap();
281 SmallVector<AffineExpr> ubExprs;
283 for (AffineExpr ubI : origUbMap.
getResults()) {
284 ubExprs.push_back((ubI - lbDim + stepSym - 1).floorDiv(stepSym));
294 SmallVector<Value> combined;
295 combined.append(ubOps.begin(), ubOps.begin() + ubDims);
296 combined.push_back(lb);
297 combined.append(ubOps.begin() + ubDims, ubOps.end());
298 combined.push_back(op.getStep());
299 ubOperands = std::move(combined);
302 auto affineFor = affine::AffineForOp::create(
303 rewriter, op.getLoc(), {}, zeroMap, ubOperands, ubMap, 1, op.getInits());
308 llvm::SmallVector<Value, 3> ivOperands = {lb, affineFor.getInductionVar(),
313 affine::AffineApplyOp::create(rewriter, op.getLoc(), ivMap, ivOperands);
315 return std::make_pair(affineFor, oldIV);
318void ForOpRewrite::castBoundsToIndex(scf::ForOp loop,
319 PatternRewriter &rewriter)
const {
320 OpBuilder::InsertionGuard guard(rewriter);
322 Value lb = loop.getLowerBound();
323 Value ub = loop.getUpperBound();
324 Value step = loop.getStep();
325 Type originalType = step.
getType();
327 assert(lb.
getType() == originalType && ub.
getType() == originalType &&
328 "expected lb, ub, and step to have the same type");
330 auto createIndexCast = [&](Type out, Value in) -> Value {
331 Location loc = loop.getLoc();
332 if (loop.getUnsignedCmp())
333 return arith::IndexCastUIOp::create(rewriter, loc, out, in);
334 return arith::IndexCastOp::create(rewriter, loc, out, in);
341 Operation *anchor = loop;
346 Value newLb = createIndexCast(rewriter.
getIndexType(), lb);
347 Value newUb = createIndexCast(rewriter.
getIndexType(), ub);
348 Value newStep = createIndexCast(rewriter.
getIndexType(), step);
351 loop.setLowerBound(newLb);
352 loop.setUpperBound(newUb);
353 loop.setStep(newStep);
355 Value originalIV = loop.getInductionVar();
356 Value iv = loop.getBody()->insertArgument(
360 Value castIV = createIndexCast(originalType, iv);
364 loop.getBody()->eraseArgument(1);
372void SCFToAffinePass::runOnOperation() {
374 RewritePatternSet patterns(&ctx);
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap getMultiDimIdentityMap(unsigned numDims, MLIRContext *context)
Returns an AffineMap with 'numDims' identity result dim exprs.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
unsigned getNumInputs() const
Operation * getTerminator()
Get the terminator operation of this block.
bool mightHaveTerminator()
Return "true" if this block might have a terminator.
AffineExpr getAffineSymbolExpr(unsigned position)
AffineExpr getAffineDimExpr(unsigned position)
AffineMap getConstantAffineMap(int64_t val)
Returns a single constant result affine map with 0 dimensions and 0 symbols.
MLIRContext * getContext() const
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
void setInsertionPointToEnd(Block *block)
Sets the insertion point to the end of the specified block.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Region * getParentRegion()
Returns the region to which the instruction belongs.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
virtual void inlineBlockBefore(Block *source, Block *dest, Block::iterator before, ValueRange argValues={})
Inline the operations of block 'source' into block 'dest' before the given position.
std::enable_if_t<!std::is_convertible< CallbackT, Twine >::value, LogicalResult > notifyMatchFailure(Location loc, CallbackT &&reasonCallback)
Used to notify the listener that the IR failed to be rewritten because of a match failure,...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
virtual void replaceAllUsesWith(Value from, Value to)
Find uses of from and replace them with to.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
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...
Type getType() const
Return the type of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
bool isValidDim(Value value)
Returns true if the given Value can be used as a dimension id in the region of the closest surroundin...
bool isTopLevelValue(Value value)
A utility function to check if a value is defined at the top level of an op with trait AffineScope or...
bool isValidSymbol(Value value)
Returns true if the given value can be used as a symbol in the region of the closest surrounding op t...
Region * getAffineScope(Operation *op)
Returns the closest region enclosing op that is held by an operation with trait AffineScope; nullptr ...
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
LogicalResult applyPatternsGreedily(Region ®ion, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
void populateSCFToAffineConversionPatterns(RewritePatternSet &patterns)
Collect a set of patterns to convert SCF operations to Affine operations.
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
OpRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting against an...