26#include "llvm/ADT/TypeSwitch.h"
29#define GEN_PASS_DEF_CONVERTLINALGTOAFFINELOOPSPASS
30#define GEN_PASS_DEF_CONVERTLINALGTOLOOPSPASS
31#define GEN_PASS_DEF_CONVERTLINALGTOPARALLELLOOPSPASS
32#include "mlir/Dialect/Linalg/Passes.h.inc"
52 res.push_back(affine::AffineApplyOp::create(
b, loc, exprMap, operands));
57template <
typename LoadOpTy,
typename StoreOpTy,
typename OpType>
62 auto &block = op->getRegion(0).front();
64 map.
map(block.getArguments(), indexedValues);
65 for (
auto &op : block.without_terminator()) {
66 auto *newOp =
b.clone(op, map);
67 map.
map(op.getResults(), newOp->getResults());
70 Operation *terminator = block.getTerminator();
73 StoreOpTy::create(
b, loc, toStore,
74 outputBuffers[operand.getOperandNumber()],
75 indexing[operand.getOperandNumber()]);
109template <
typename LoadOpTy,
typename StoreOpTy>
113 assert(linalgOp.hasPureBufferSemantics() &&
114 "expected linalg op with buffer semantics");
116 indexedValues.reserve(linalgOp->getNumOperands());
123 for (
OpOperand *inputOperand : linalgOp.getDpsInputOperands()) {
124 if (linalgOp.isScalar(inputOperand)) {
125 indexedValues.push_back(inputOperand->get());
129 b, loc, linalgOp.getMatchingIndexingMap(inputOperand), allIvsPlusDims);
130 indexedValues.push_back(
131 LoadOpTy::create(
b, loc, inputOperand->get(), indexing));
134 for (
OpOperand &outputOperand : linalgOp.getDpsInitsMutable()) {
136 b, loc, linalgOp.getMatchingIndexingMap(&outputOperand),
138 indexedValues.push_back(
139 LoadOpTy::create(
b, loc, outputOperand.get(), indexing));
147 for (
OpOperand &outputOperand : linalgOp.getDpsInitsMutable()) {
148 if (!isa<MemRefType>(outputOperand.get().getType()))
151 b, loc, linalgOp.getMatchingIndexingMap(&outputOperand),
153 outputBuffers.push_back(outputOperand.get());
156 indexing, outputBuffers);
168 .Case([&](scf::ParallelOp parallelOp) {
169 allIvs.append(parallelOp.getInductionVars());
171 .Case([&](scf::ForOp forOp) {
172 allIvs.push_back(forOp.getInductionVar());
174 .Case([&](affine::AffineForOp affineForOp) {
175 allIvs.push_back(affineForOp.getInductionVar());
177 .DefaultUnreachable(
"unexpected op");
179 assert(linalgOp.getNumLoops() == allIvs.size() &&
180 "expected the number of loops and induction variables to match");
182 if (!loopOps.empty()) {
183 auto loopOp = cast<LoopLikeOpInterface>(loopOps.back());
184 for (
Region *r : loopOp.getLoopRegions())
185 for (IndexOp indexOp : llvm::make_early_inc_range(r->getOps<IndexOp>()))
186 rewriter.
replaceOp(indexOp, allIvs[indexOp.getDim()]);
190template <
typename LoopTy>
194 std::conditional_t<std::is_same<LoopTy, affine::AffineForOp>::value,
195 affine::AffineLoadOp, memref::LoadOp>;
197 std::conditional_t<std::is_same<LoopTy, affine::AffineForOp>::value,
198 affine::AffineStoreOp, memref::StoreOp>;
202 assert(linalgOp.hasPureBufferSemantics() &&
203 "expected linalg op with buffer semantics");
205 auto loopRanges = linalgOp.createLoopRanges(rewriter, linalgOp.getLoc());
206 auto iteratorTypes = linalgOp.getIteratorTypesArray();
210 rewriter, linalgOp.getLoc(), loopRanges, linalgOp, iteratorTypes,
213 assert(operandValuesToUse == linalgOp->getOperands() &&
214 "expect operands are captured and not passed by loop argument");
215 allIvs.append(ivs.begin(), ivs.end());
216 emitScalarImplementation<LoadOpTy, StoreOpTy>(b, loc, allIvs, linalgOp);
217 return scf::ValueVector{};
222 for (
Value iv : allIvs) {
239template <
typename LoopType>
242 LinalgRewritePattern(MLIRContext *context)
243 : RewritePattern(MatchAnyOpTypeTag(), 1, context) {}
245 LogicalResult matchAndRewrite(Operation *op,
246 PatternRewriter &rewriter)
const override {
247 auto linalgOp = dyn_cast<LinalgOp>(op);
248 if (!isa<LinalgOp>(op) || !linalgOp.hasPureBufferSemantics()) {
250 op,
"expected linalg op with buffer semantics");
270 FoldAffineOp(MLIRContext *context)
271 : RewritePattern(affine::AffineApplyOp::getOperationName(), 0, context) {}
273 LogicalResult matchAndRewrite(Operation *op,
274 PatternRewriter &rewriter)
const override {
275 auto affineApplyOp = cast<affine::AffineApplyOp>(op);
276 auto map = affineApplyOp.getAffineMap();
277 if (map.getNumResults() != 1 || map.getNumInputs() > 1)
280 AffineExpr expr = map.getResult(0);
281 if (map.getNumInputs() == 0) {
282 if (
auto val = dyn_cast<AffineConstantExpr>(expr)) {
288 if (isa<AffineDimExpr, AffineSymbolExpr>(expr)) {
296template <
typename LoopType>
297static void lowerLinalgToLoopsImpl(
Operation *enclosingOp) {
300 patterns.add<LinalgRewritePattern<LoopType>>(context);
301 memref::DimOp::getCanonicalizationPatterns(patterns, context);
302 tensor::DimOp::getCanonicalizationPatterns(patterns, context);
303 affine::AffineApplyOp::getCanonicalizationPatterns(patterns, context);
304 patterns.add<FoldAffineOp>(context);
309struct LowerToAffineLoops
310 :
public impl::ConvertLinalgToAffineLoopsPassBase<LowerToAffineLoops> {
311 using impl::ConvertLinalgToAffineLoopsPassBase<
312 LowerToAffineLoops>::ConvertLinalgToAffineLoopsPassBase;
313 void getDependentDialects(DialectRegistry ®istry)
const override {
314 registry.
insert<memref::MemRefDialect>();
316 void runOnOperation()
override {
317 lowerLinalgToLoopsImpl<affine::AffineForOp>(getOperation());
321struct LowerToLoops :
public impl::ConvertLinalgToLoopsPassBase<LowerToLoops> {
322 using impl::ConvertLinalgToLoopsPassBase<
323 LowerToLoops>::ConvertLinalgToLoopsPassBase;
324 void getDependentDialects(DialectRegistry ®istry)
const override {
325 registry.
insert<memref::MemRefDialect, scf::SCFDialect>();
327 void runOnOperation()
override {
328 lowerLinalgToLoopsImpl<scf::ForOp>(getOperation());
332struct LowerToParallelLoops
333 :
public impl::ConvertLinalgToParallelLoopsPassBase<LowerToParallelLoops> {
334 using impl::ConvertLinalgToParallelLoopsPassBase<
335 LowerToParallelLoops>::ConvertLinalgToParallelLoopsPassBase;
336 void runOnOperation()
override {
337 lowerLinalgToLoopsImpl<scf::ParallelOp>(getOperation());
344FailureOr<LinalgLoops>
356FailureOr<LinalgLoops>
static SmallVector< Value > makeCanonicalAffineApplies(OpBuilder &b, Location loc, AffineMap map, ArrayRef< Value > vals)
static void replaceIndexOpsByInductionVariables(RewriterBase &rewriter, LinalgOp linalgOp, ArrayRef< Operation * > loopOps)
Replace the index operations in the body of the loop nest by the matching induction variables.
static void inlineRegionAndEmitStore(OpBuilder &b, Location loc, OpType op, ArrayRef< Value > indexedValues, ArrayRef< SmallVector< Value > > indexing, ArrayRef< Value > outputBuffers)
static FailureOr< LinalgLoops > linalgOpToLoopsImpl(RewriterBase &rewriter, LinalgOp linalgOp)
static void emitScalarImplementation(OpBuilder &b, Location loc, ArrayRef< Value > allIvs, LinalgOp linalgOp)
Emits the MLIR for the scalar part of the generic op by:
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
static AffineMap get(MLIRContext *context)
Returns a zero result affine map with no dimensions or symbols: () -> ().
bool isEmpty() const
Returns true if this affine map is an empty map, i.e., () -> ().
unsigned getNumSymbols() const
unsigned getNumDims() const
ArrayRef< AffineExpr > getResults() const
unsigned getNumResults() const
unsigned getNumInputs() const
This class represents an argument of a Block.
Block * getOwner() const
Returns the block that owns this argument.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
This is a utility class for mapping one set of IR entities to another.
auto lookupOrDefault(T from) const
Lookup a mapped value within the map.
void map(Value from, Value to)
Inserts a new mapping for 'from' to 'to'.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
This class helps build Operations.
This class represents an operand of an operation.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
MutableArrayRef< OpOperand > getOpOperands()
MLIRContext * getContext()
Return the context this operation is associated with.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
RewritePattern is the common base class for all DAG to DAG replacements.
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
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.
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,...
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...
void canonicalizeMapAndOperands(AffineMap *map, SmallVectorImpl< Value > *operands)
Modifies both map and operands in-place so as to:
SmallVector< Operation *, 4 > LinalgLoops
FailureOr< LinalgLoops > linalgOpToLoops(RewriterBase &rewriter, LinalgOp linalgOp)
Emit a loop nest of scf.for with the proper body for linalgOp.
FailureOr< LinalgLoops > linalgOpToAffineLoops(RewriterBase &rewriter, LinalgOp linalgOp)
Emit a loop nest of affine.for with the proper body for linalgOp.
FailureOr< LinalgLoops > linalgOpToParallelLoops(RewriterBase &rewriter, LinalgOp linalgOp)
Emit a loop nest of scf.parallel with the proper body for linalgOp.
SmallVector< Value > ValueVector
An owning vector of values, handy to return from functions.
Include the generated interface declarations.
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...
llvm::SetVector< T, Vector, Set, N > SetVector
static void doit(OpBuilder &b, Location loc, ArrayRef< Range > loopRanges, LinalgOp linalgOp, ArrayRef< utils::IteratorType > iteratorTypes, function_ref< scf::ValueVector(OpBuilder &, Location, ValueRange, ValueRange)> bodyBuilderFn, ArrayRef< linalg::ProcInfo > procInfo={})