1 //===- Fusion.cpp - Implementation of linalg Fusion -----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements the linalg dialect Fusion pass.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "PassDetail.h"
14 #include "mlir/Dialect/Affine/IR/AffineOps.h"
15 #include "mlir/Dialect/Linalg/Analysis/DependenceAnalysis.h"
16 #include "mlir/Dialect/Linalg/IR/LinalgOps.h"
17 #include "mlir/Dialect/Linalg/IR/LinalgTypes.h"
18 #include "mlir/Dialect/Linalg/Passes.h"
19 #include "mlir/Dialect/Linalg/Transforms/Transforms.h"
20 #include "mlir/Dialect/Linalg/Utils/Utils.h"
21 #include "mlir/Dialect/MemRef/EDSC/Intrinsics.h"
22 #include "mlir/Dialect/MemRef/IR/MemRef.h"
23 #include "mlir/Dialect/StandardOps/EDSC/Intrinsics.h"
24 #include "mlir/Dialect/Tensor/IR/Tensor.h"
25 #include "mlir/IR/AffineExpr.h"
26 #include "mlir/IR/AffineMap.h"
27 #include "mlir/IR/Dominance.h"
28 #include "mlir/Support/LLVM.h"
29 #include "mlir/Transforms/GreedyPatternRewriteDriver.h"
30 #include "mlir/Transforms/RegionUtils.h"
31 #include "llvm/ADT/MapVector.h"
32 #include "llvm/ADT/ScopeExit.h"
33 #include "llvm/Support/CommandLine.h"
34 #include "llvm/Support/Debug.h"
35 
36 #include <set>
37 
38 #define DEBUG_TYPE "linalg-fusion"
39 
40 using namespace mlir;
41 using namespace mlir::edsc;
42 using namespace mlir::edsc::intrinsics;
43 using namespace mlir::linalg;
44 
45 using llvm::dbgs;
46 
47 /// Implements a simple high-level fusion pass on linalg structured operations.
48 ///
49 /// In each block, linalg ops are processed in reverse textual order.
50 /// Given a linalg op `O`, fusion occurs by:
51 ///   1. inspecting the linalg ops that write into the views read by `O`. There
52 ///      are 2 cases:
53 ///      a) buffer case: use the SSA value of the views and a simple alias
54 ///         analysis on subview ops to determine producer-consumer dependences;
55 ///      b) tensor case: use SSA use-def chains on subtensor ops;
56 ///   2. greedily fuse the linalg ops that produce the subview/subtensor.
57 ///   3. inspect the fused ops and determine whether they have other remaining
58 ///      LinalgOp uses. If not, then erase the original producing linalg op.
59 ///
60 /// More advanced use cases, analyses as well as profitability heuristics are
61 /// left for future work.
62 
63 struct ShapeDimension {
64   Value shape;
65   unsigned dimension;
66 };
67 
68 // Given an `op`, returns the first (`shape`, `dimension`) pair that identifies
69 // the loop range at `loopDepth`. The semantics of the loopToOperandRangesMaps
70 // guarantees at least one such dimension is found. If multiple candidates exist
71 // they must agree by construction (i.e. have the same size) and we just return
72 // the first one.
73 static ShapeDimension
74 getShapeDefiningLoopRange(LinalgOp op, unsigned loopDepth,
75                           bool fromSubViewOpOnly = false) {
76   auto maps = op.indexing_maps();
77   // Iterate over the inputs and outputs in order.
78   // Extract the subranges from the linearized ranges.
79   for (auto en : llvm::enumerate(op.getShapedOperands())) {
80     // The method `getRangeFromOperandShape` requires using SubViewOp or
81     // SubTensorOps. If the value isnt defined from there continue.
82     // todo: The method should be adapted to get the values from
83     // `ViewInterface`. The interface needs a `getOrCreateRanges` method which
84     // currently returns a `linalg.range`. The fix here is to move this op to
85     // `std` dialect and add the method to `ViewInterface`.
86     if (fromSubViewOpOnly && !isa_and_nonnull<memref::SubViewOp, SubTensorOp>(
87                                  en.value().getDefiningOp()))
88       continue;
89 
90     unsigned idx = en.index();
91     auto map = maps[idx].cast<AffineMapAttr>().getValue();
92     LLVM_DEBUG(llvm::dbgs()
93                << "getShapeDefiningLoopRange I/O idx: " << idx << "\n");
94     LLVM_DEBUG(llvm::dbgs()
95                << "getShapeDefiningLoopRange map: " << map << "\n");
96     Value shape = en.value();
97     SmallVector<Value, 8> shapeRanges(map.getNumResults(), nullptr);
98     for (auto en2 : llvm::enumerate(map.getResults())) {
99       auto dimExpr = en2.value().dyn_cast<AffineDimExpr>();
100       if (!dimExpr)
101         continue;
102       if (loopDepth == en2.value().cast<AffineDimExpr>().getPosition()) {
103         LLVM_DEBUG(llvm::dbgs() << "getShapeDefiningLoopRange loopDepth: "
104                                 << loopDepth << "\n");
105         LLVM_DEBUG(llvm::dbgs()
106                    << "getShapeDefiningLoopRange shape: " << shape << "\n");
107         return ShapeDimension{shape, static_cast<unsigned>(en2.index())};
108       }
109     }
110   }
111   llvm_unreachable("Expect to be able to extract a shape defining loop range");
112 }
113 
114 /// Fuses the producer by cloning the `producer`. The `fusedLoopsAndRanges`
115 /// provides the loop range information for the fused loops. The rest are
116 /// obtained from the producer itself, since they are not tiled + fused.
117 static LinalgOp fuse(OpBuilder &builder, LinalgOp producer,
118                      const DenseMap<unsigned, Range> &fusedLoopsAndRanges) {
119   SmallVector<Value, 8> ivs, tileSizes, sizeBounds;
120   SmallVector<Range, 8> loopRanges;
121   auto zero = std_constant_index(0);
122   auto one = std_constant_index(1);
123   Location loc = producer.getLoc();
124 
125   for (unsigned i = 0, e = producer.getNumLoops(); i < e; ++i) {
126     auto it = fusedLoopsAndRanges.find(i);
127     if (it != fusedLoopsAndRanges.end()) {
128       ivs.push_back(it->second.offset);
129       tileSizes.push_back(it->second.size);
130       sizeBounds.push_back(nullptr);
131       loopRanges.push_back(it->second);
132       LLVM_DEBUG(llvm::dbgs() << "tiled loop#" << i << " with LoopRange "
133                               << loopRanges.back() << "\n");
134     } else {
135       auto shapeDim = getShapeDefiningLoopRange(producer, i);
136       Value dim = memref_dim(shapeDim.shape, shapeDim.dimension);
137       tileSizes.push_back(zero);
138       sizeBounds.push_back(dim);
139       loopRanges.push_back(Range{zero, dim, one});
140       LLVM_DEBUG(llvm::dbgs() << "full loop#" << i << " with LoopRange "
141                               << loopRanges.back() << "\n");
142     }
143   }
144 
145   SmallVector<Value, 8> clonedShapes;
146   clonedShapes.reserve(producer.getNumShapedOperands());
147 
148   // Compute subranges for all tensor input/output operands.
149   auto tiledOperands = llvm::to_vector<4>(producer.getShapedOperands());
150   clonedShapes.append(makeTiledShapes(builder, loc, producer, tiledOperands,
151                                       ivs, tileSizes, sizeBounds));
152 
153   // Append the other operands.
154   auto operands = producer.getAssumedNonShapedOperands();
155   clonedShapes.append(operands.begin(), operands.end());
156 
157   // Iterate over the results in order.
158   // Extract the subtensor type from the linearized range.
159   // Since we do not enforce any canonicalizations on the fly, this is always
160   // fully dynamic at construction time.
161   SmallVector<Type, 4> resultTypes;
162   resultTypes.reserve(producer->getNumResults());
163   for (RankedTensorType t : producer.getOutputTensorTypes()) {
164     unsigned rank = t.getRank();
165     SmallVector<int64_t, 4> staticOffsetsVector(
166         rank, ShapedType::kDynamicStrideOrOffset);
167     SmallVector<int64_t, 4> staticSizesVector(rank, ShapedType::kDynamicSize);
168     SmallVector<int64_t, 4> staticStridesVector(
169         rank, ShapedType::kDynamicStrideOrOffset);
170     resultTypes.push_back(SubTensorOp::inferResultType(
171         t.cast<RankedTensorType>(), staticOffsetsVector, staticSizesVector,
172         staticStridesVector));
173   }
174 
175   Operation *clonedOp = producer.clone(builder, loc, resultTypes, clonedShapes);
176   // When the producer has index semantics, we have to transform the indices of
177   // the producer according to the tiling of the consumer, i.e. offset them by
178   // the values computed in `loopRanges`.
179   assert(!isa<IndexedGenericOp>(producer) && "unexpected op");
180   if (producer.hasIndexSemantics()) {
181     assert(clonedOp->getNumRegions() == 1 &&
182            clonedOp->getRegion(0).getBlocks().size() == 1 &&
183            "expected producer to have one block.");
184     // Shift all indices by the tile offset.
185     Block &block = clonedOp->getRegion(0).front();
186     for (IndexOp indexOp : block.getOps<IndexOp>()) {
187       OpBuilder::InsertionGuard g(builder);
188       builder.setInsertionPointAfter(indexOp);
189       AffineExpr index, offset;
190       bindDims(builder.getContext(), index, offset);
191       AffineApplyOp applyOp = builder.create<AffineApplyOp>(
192           indexOp.getLoc(), index + offset,
193           ValueRange{indexOp.getResult(), loopRanges[indexOp.dim()].offset});
194       indexOp.getResult().replaceAllUsesExcept(
195           applyOp, SmallPtrSet<Operation *, 1>{applyOp});
196     }
197   }
198 
199   return clonedOp;
200 }
201 
202 /// Get the loop range for a dimension `dim` based on the `shapedOperand`. It is
203 /// expected to be defined by a subview op or a subtensor op.
204 static Range getRangeFromOperandShape(OpBuilder &b, Location loc,
205                                       Value shapedOperand, unsigned dim) {
206   Operation *shapeProducingOp = shapedOperand.getDefiningOp();
207   if (auto subViewOp = dyn_cast<memref::SubViewOp>(shapeProducingOp))
208     return subViewOp.getOrCreateRanges(b, loc)[dim];
209   if (auto subTensorOp = dyn_cast<SubTensorOp>(shapeProducingOp))
210     return subTensorOp.getOrCreateRanges(b, loc)[dim];
211   llvm_unreachable("SubviewOp or SubTensorOp expected");
212 }
213 
214 /// Fuses the producer of `producerIdx` into the loop immediately enclosing
215 /// `consumer`. This is achieved by "recomputing" the `producer` at the time it
216 /// is needed just before the `consumer.
217 ///
218 /// Depending on the type of `consumer.getShapedOperand(consumerIdx)`, there are
219 /// 2 cases:
220 ///   1. Buffer case: `producerIdx` is the index of the buffer in
221 ///      `producer.getOutputBuffers()`.
222 ///   2. Tensor case: `producerIdx` is the index of the tensor in
223 ///      `producer.getResults()`.
224 static LinalgOp fuse(OpBuilder &b, LinalgOp producerOp, AffineMap producerMap,
225                      OpOperand &consumerOpOperand) {
226   LLVM_DEBUG(llvm::dbgs() << "Producer map: " << producerMap << "\n");
227   DenseMap<unsigned, Range> fusedLoopsAndRanges;
228   Value shapedOperand = consumerOpOperand.get();
229   for (auto en : llvm::enumerate(producerMap.getResults())) {
230     unsigned posInProducerLoop = en.value().cast<AffineDimExpr>().getPosition();
231     fusedLoopsAndRanges[posInProducerLoop] = getRangeFromOperandShape(
232         b, consumerOpOperand.getOwner()->getLoc(), shapedOperand, en.index());
233   }
234   return fuse(b, producerOp, fusedLoopsAndRanges);
235 }
236 
237 // Encode structural fusion safety preconditions.
238 // Some of these will be lifted in the future with better analysis.
239 static bool isStructurallyFusableProducer(LinalgOp producer, Value consumedView,
240                                           LinalgOp consumer) {
241   assert(producer.hasBufferSemantics() &&
242          "expected linalg op with buffer semantics");
243   assert(consumer.hasBufferSemantics() &&
244          "expected linalg op with buffer semantics");
245   if (producer.getNumOutputs() != 1) {
246     LLVM_DEBUG(llvm::dbgs() << "\nNot structurally fusable (multi-output)");
247     return false;
248   }
249   // Only fuse when the producer block dominates.
250   DominanceInfo dom(producer.getOperation());
251   if (!dom.dominates(producer->getBlock(), consumer->getBlock())) {
252     LLVM_DEBUG(
253         llvm::dbgs()
254         << "\nNot structurally fusable (producer block does not dominate)");
255     return false;
256   }
257   return true;
258 }
259 
260 bool mlir::linalg::isProducerLastWriteOfView(const LinalgDependenceGraph &graph,
261                                              LinalgOp consumer,
262                                              Value consumedView,
263                                              LinalgOp producer) {
264   assert(producer.hasBufferSemantics() &&
265          "expected linalg op with buffer semantics");
266   assert(consumer.hasBufferSemantics() &&
267          "expected linalg op with buffer semantics");
268   // Make some simple structural checks that alleviate the need for more
269   // complex analyses.
270   if (!isStructurallyFusableProducer(producer, consumedView, consumer)) {
271     LLVM_DEBUG(llvm::dbgs() << "\n***Not static last write due to structure:\t"
272                             << *producer.getOperation());
273     return false;
274   }
275   // Check for any interleaved write to consumedView.
276   if (!graph.findCoveringWrites(producer, consumer, consumedView).empty()) {
277     LLVM_DEBUG(llvm::dbgs() << "\n***Not fusable due to interleaved write:\t"
278                             << *producer.getOperation());
279     return false;
280   }
281   return true;
282 }
283 
284 bool mlir::linalg::isFusableInto(const LinalgDependenceGraph &graph,
285                                  LinalgOp consumer, Value consumedView,
286                                  LinalgOp producer) {
287   assert(producer.hasBufferSemantics() &&
288          "expected linalg op with buffer semantics");
289   assert(consumer.hasBufferSemantics() &&
290          "expected linalg op with buffer semantics");
291   if (!isProducerLastWriteOfView(graph, consumer, consumedView, producer))
292     return false;
293   // Check for any fusion-preventing dependence to any shape read/written that
294   // would violate dependences.
295   if (!graph.findCoveringDependences(producer, consumer).empty()) {
296     LLVM_DEBUG(llvm::dbgs()
297                << "\n***Not fusable due to an interleaved dependence:\t"
298                << *producer.getOperation());
299     return false;
300   }
301   if (auto convOp = dyn_cast<linalg::ConvOp>(producer.getOperation())) {
302     // TODO: add a level of indirection to linalg.generic.
303     if (convOp.padding())
304       return false;
305   }
306   if (auto convOp = dyn_cast<linalg::ConvOp>(consumer.getOperation())) {
307     // TODO: add a level of indirection to linalg.generic.
308     if (convOp.padding())
309       return false;
310   }
311   return true;
312 }
313 
314 /// For `consumer` with buffer semantics, find the Linalg operation on buffers
315 /// that is the last writer of `consumerOpOperand`. For now the fusable
316 /// dependence is returned as an instance of the `dependenceGraph`.
317 static Optional<LinalgDependenceGraph::LinalgDependenceGraphElem>
318 findFusableProducer(OpOperand &consumerOpOperand,
319                     const LinalgDependenceGraph &dependenceGraph) {
320   LLVM_DEBUG(llvm::dbgs() << "findFusableProducer for: "
321                           << consumerOpOperand.get() << " @"
322                           << consumerOpOperand.getOperandNumber() << " in "
323                           << *consumerOpOperand.getOwner() << "\n");
324   LinalgOp consumerOp = dyn_cast<LinalgOp>(consumerOpOperand.getOwner());
325   if (!consumerOp)
326     return {};
327 
328   // Only consider RAW and WAW atm.
329   for (auto depType : {
330            LinalgDependenceGraph::DependenceType::RAW,
331            LinalgDependenceGraph::DependenceType::WAW,
332        }) {
333     LLVM_DEBUG(llvm::dbgs()
334                << "Dependencies into: " << *consumerOp.getOperation() << "\n");
335     for (auto dependence : llvm::make_filter_range(
336              dependenceGraph.getDependencesInto(consumerOp, depType),
337              [&](LinalgDependenceGraph::LinalgDependenceGraphElem elem) {
338                LLVM_DEBUG(llvm::dbgs() << "Inspect dependence btw: "
339                                        << elem.getIndexingValue() << " and "
340                                        << elem.getDependentValue() << "\n");
341                Value v = elem.getIndexingValue();
342                Optional<unsigned> operandNum =
343                    elem.getIndexingOpViewOperandNum();
344                return isa<LinalgOp>(elem.getDependentOp()) &&
345                       v == consumerOpOperand.get() && operandNum &&
346                       operandNum.getValue() ==
347                           consumerOpOperand.getOperandNumber();
348              })) {
349       // Consumer consumes this view, `isStructurallyFusableProducer` also
350       // checks whether it is a strict subview of the producer view.
351       auto producer = cast<LinalgOp>(dependence.getDependentOp());
352       LLVM_DEBUG(llvm::dbgs()
353                  << "\n"
354                  << LinalgDependenceGraph::getDependenceTypeStr(depType)
355                  << "producer: " << *dependence.getDependentOp()
356                  << " view: " << dependence.getDependentValue() << "\n");
357 
358       // If the producer and consumer have tensor semantics, the only dependence
359       // between them is through a RAW dependence and they are fusable by
360       // construction. For buffer semantics need additional checks.
361       if (producer.hasBufferSemantics() && consumerOp.hasBufferSemantics() &&
362           isFusableInto(dependenceGraph, consumerOp, consumerOpOperand.get(),
363                         producer))
364         return dependence;
365       if (producer.hasTensorSemantics() && consumerOp.hasTensorSemantics()) {
366         assert(dependence.dependenceType ==
367                LinalgDependenceGraph::DependenceType::RAW);
368         return dependence;
369       }
370     }
371   }
372   return {};
373 }
374 
375 Optional<FusionInfo>
376 mlir::linalg::fuseProducerOfBuffer(OpBuilder &b, OpOperand &consumerOpOperand,
377                                    const LinalgDependenceGraph &graph) {
378   Optional<LinalgDependenceGraph::LinalgDependenceGraphElem> fusableDependence =
379       findFusableProducer(consumerOpOperand, graph);
380   if (!fusableDependence)
381     return llvm::None;
382 
383   // Canonicalize indexed generic ops before fusion.
384   if (isa<IndexedGenericOp>(fusableDependence->getDependentOp()))
385     return llvm::None;
386 
387   LinalgOp producerOp = dyn_cast<LinalgOp>(fusableDependence->getDependentOp());
388   if (!producerOp)
389     return llvm::None;
390 
391   // If producer is already in the same block as consumer, we are done.
392   if (consumerOpOperand.get().getParentBlock() ==
393       fusableDependence->getDependentValue().getParentBlock())
394     return llvm::None;
395 
396   Optional<AffineMap> producerMap =
397       fusableDependence->getDependentOpViewIndexingMap();
398   if (!producerMap)
399     return llvm::None;
400 
401   // Must be a subview or a slice to guarantee there are loops we can fuse
402   // into.
403   auto subView = consumerOpOperand.get().getDefiningOp<memref::SubViewOp>();
404   if (!subView) {
405     LLVM_DEBUG(llvm::dbgs() << "\nNot fusable (not a subview)");
406     return llvm::None;
407   }
408 
409   // Fuse `producer` just before `consumer`.
410   OpBuilder::InsertionGuard g(b);
411   b.setInsertionPoint(consumerOpOperand.getOwner());
412   ScopedContext scope(b, consumerOpOperand.getOwner()->getLoc());
413   LLVM_DEBUG(llvm::dbgs() << "Fuse into consumer: "
414                           << *consumerOpOperand.getOwner() << "\n");
415 
416   auto fusedProducer = fuse(b, producerOp, *producerMap, consumerOpOperand);
417   return FusionInfo{producerOp, fusedProducer};
418 }
419 
420 /// Walk back use-def chain through scf::For yields.
421 /// Sets `producer` and `outputIndex` if it finds a producer LinalgOp
422 
423 // TODO(ravishankarm, ntv): This can be moved into the dependence graphs
424 // dependence tracking since the dependence tracking is similar to what is done
425 // w.r.t to buffers.
426 static void getProducerOfTensor(Value tensor, OpResult &opResult) {
427   if (!tensor.getType().isa<RankedTensorType>())
428     return;
429 
430   while (true) {
431     LLVM_DEBUG(llvm::dbgs() << "\ngetProducerOfTensor: " << tensor);
432     if (auto linalgOp = tensor.getDefiningOp<LinalgOp>()) {
433       opResult = tensor.cast<OpResult>();
434       return;
435     }
436     if (auto subTensorOp = tensor.getDefiningOp<SubTensorOp>()) {
437       tensor = subTensorOp.source();
438       continue;
439     }
440     if (auto blockArg = tensor.dyn_cast<BlockArgument>()) {
441       if (auto forOp = blockArg.getDefiningOp<scf::ForOp>()) {
442         tensor = *(forOp.getIterOperands().begin() + blockArg.getArgNumber());
443         continue;
444       }
445     }
446     return;
447   }
448 }
449 
450 Optional<FusionInfo>
451 mlir::linalg::fuseProducerOfTensor(OpBuilder &b, OpOperand &consumerOpOperand) {
452   Value inputTensor = consumerOpOperand.get();
453   OpResult producerOpResult;
454   getProducerOfTensor(inputTensor, producerOpResult);
455   if (!producerOpResult) {
456     LLVM_DEBUG(llvm::dbgs() << "\nUnable to find producer");
457     return {};
458   }
459   return fuseProducerOfTensor(b, producerOpResult, consumerOpOperand);
460 }
461 
462 Optional<FusionInfo>
463 mlir::linalg::fuseProducerOfTensor(OpBuilder &b, OpResult producerOpResult,
464                                    OpOperand &consumerOpOperand) {
465   // Canonicalize indexed generic ops before fusion.
466   if (isa<IndexedGenericOp>(producerOpResult.getOwner()))
467     return llvm::None;
468 
469   auto producerOp = dyn_cast<LinalgOp>(producerOpResult.getOwner());
470   if (!producerOp)
471     return llvm::None;
472 
473   LinalgOp consumerOp = dyn_cast<LinalgOp>(consumerOpOperand.getOwner());
474   if (!consumerOp)
475     return llvm::None;
476 
477   Value inputTensor = consumerOpOperand.get();
478 
479   // Must be a subtensor to guarantee there are loops we can fuse into.
480   auto subTensor = inputTensor.getDefiningOp<SubTensorOp>();
481   if (!subTensor) {
482     LLVM_DEBUG(llvm::dbgs()
483                << "\nNot fusable, not a subtensor: " << inputTensor);
484     return {};
485   }
486 
487   // If producer is already in the same block as consumer, we are done.
488   if (consumerOpOperand.get().getParentBlock() ==
489       producerOpResult.getParentBlock())
490     return {};
491 
492   // Insert fused `producer` just before `consumer`.
493   OpBuilder::InsertionGuard g(b);
494   b.setInsertionPoint(consumerOp);
495   ScopedContext scope(b, consumerOp->getLoc());
496   LLVM_DEBUG(llvm::dbgs() << "Fuse into consumer: " << *consumerOp << "\n");
497   LinalgOp fusedProducer =
498       fuse(b, producerOp,
499            producerOp.getOutputIndexingMap(producerOpResult.getResultNumber()),
500            consumerOpOperand);
501 
502   // Replace use.
503   // Canonicalizations are not guaranteed to have happened before constructing
504   // `fusedProducer`. In the tensor case this can result in temporary type
505   // mismatches. Insert a `tensor.cast` op to propagate the transformation
506   // invariant that types are compatible.
507   Value def = fusedProducer->getResult(producerOpResult.getResultNumber());
508   Type consumerType = consumerOpOperand.get().getType();
509   if (consumerType != def.getType())
510     def = b.create<tensor::CastOp>(fusedProducer.getLoc(), consumerType, def);
511   consumerOpOperand.set(def);
512   return FusionInfo{cast<LinalgOp>(producerOpResult.getOwner()), fusedProducer};
513 }
514 
515 /// Prune all dimensions that are of reduction iterator type from `map`.
516 static AffineMap pruneReductionDimsFromMap(ArrayRef<Attribute> iteratorTypes,
517                                            AffineMap map) {
518   llvm::SmallDenseSet<unsigned> projectedDims;
519   for (auto attr : llvm::enumerate(iteratorTypes)) {
520     if (!isParallelIterator(attr.value()))
521       projectedDims.insert(attr.index());
522   }
523   return getProjectedMap(map, projectedDims);
524 }
525 
526 /// Returns the mapping from iterations in the consumer that write to the same
527 /// location as the iterations in the producer. To do so use
528 /// - indexing map of the fused view in the consumer : consumerIndexMap
529 /// - indexing map of the fused view in the producer : producerIndexMap
530 ///     consumerLoopToProducerLoop =
531 ///       inverse(producerIndexMap).compose(consumerIndexMap)
532 static Optional<AffineMap> getConsumerLoopToProducerLoopMap(
533     LinalgDependenceGraph::LinalgDependenceGraphElem dependence) {
534   auto producer = dyn_cast<LinalgOp>(dependence.getDependentOp());
535   if (!producer)
536     return None;
537 
538   Optional<AffineMap> producerIndexingMap =
539       dependence.getDependentOpViewIndexingMap();
540   Optional<AffineMap> consumerIndexingMap =
541       dependence.getIndexingOpViewIndexingMap();
542   if (!producerIndexingMap || !consumerIndexingMap)
543     return None;
544 
545   AffineMap prunedProducerIndexingMap = pruneReductionDimsFromMap(
546       producer.iterator_types().getValue(), *producerIndexingMap);
547   if (!prunedProducerIndexingMap.isPermutation())
548     return None;
549 
550   if (consumerIndexingMap->getNumResults() !=
551       prunedProducerIndexingMap.getNumResults())
552     return None;
553 
554   LLVM_DEBUG({
555     llvm::dbgs() << "\t producerMap : ";
556     producerIndexingMap->print(llvm::dbgs());
557     llvm::dbgs() << "  pruned : ";
558     prunedProducerIndexingMap.print(llvm::dbgs());
559     llvm::dbgs() << "\n";
560     llvm::dbgs() << "\t consumerMap : ";
561     consumerIndexingMap->print(llvm::dbgs());
562     llvm::dbgs() << "\n";
563   });
564 
565   AffineMap invProducerIndexMap = inversePermutation(prunedProducerIndexingMap);
566   if (!invProducerIndexMap)
567     return None;
568 
569   return invProducerIndexMap.compose(*consumerIndexingMap);
570 }
571 
572 /// Given a projected permutation `map`, returns true if the map changes the
573 /// order in which the fused loop dimension appear.
574 static bool doesTransposeAccess(AffineMap map,
575                                 const std::set<unsigned> &fusableLoops) {
576   Optional<unsigned> lastFusableLoop;
577   for (unsigned pos : llvm::map_range(map.getResults(), [](AffineExpr expr) {
578          return expr.cast<AffineDimExpr>().getPosition();
579        })) {
580     if (!fusableLoops.count(pos))
581       continue;
582     if (!lastFusableLoop) {
583       lastFusableLoop = pos;
584       continue;
585     }
586     if (pos <= lastFusableLoop.getValue())
587       return true;
588     lastFusableLoop = pos;
589   }
590   return false;
591 }
592 
593 /// Returns the positions of the loop in `op` that can be tiled based on the
594 /// operations that are to be fused with it. For example, in a
595 ///
596 ///   linalg.matmul ins(%a, %b : ...) outs(%c : ...)
597 ///
598 /// if the producer of %a needs to be fused with this op, only the `i` loop of
599 /// the matmul can be tiled while fusing. If producer of %a, and %b are to be
600 /// fused, then no loops can be tiled while fusing. The conditions used are:
601 /// 1. Only parallel loops can be used for tile + fuse. Find the number of
602 ///    common outer parallel loops between the op and its producers being fused.
603 /// 2. Of the parallel loops only some can be fused. Only those loops can be
604 ///    fused such where the fusable loops iteration space only touches one tile
605 ///    of the fused operation. This is because the producer (which is writing
606 ///    the fused subview) has update semantics.
607 ///
608 /// Since an inverse computation is needed, we need to consider the projection
609 /// of the producerIndexMap w.r.t the parallel loops.  The actual fusable loops
610 /// are the dimensions of the consumerLoopToProducerLoop map that correspond to
611 /// parallel loops and appear in the result of the map
612 ///
613 /// Example 1:
614 ///   linalg.fill(%c, %cst)
615 ///   linalg.matmul ins(%a, %b) outs(%c)
616 ///     Number of parallel loops : 2
617 ///     producerIndexMap = affine_map<(i, j) ->(i , j)>
618 ///     consumerIndexMap = affine_map<(i, j, k) -> (i, j)>
619 ///     consumerLoopToProducerLoop = affine_map<(i, j, k) -> (i, j)>
620 ///     Fused dimensions : i, j
621 ///
622 /// Example 2:
623 ///   linalg.matmul ins(%a, %b) outs(%c)
624 ///   linalg.generic {indexing_maps = [affine_map<(i, j) -> (j, i)>, ...
625 ///                   iterator_types = ["parallel", "parallel"]}
626 ///     ins(%c) ...
627 ///
628 ///     Number of parallel loops = 2:
629 ///     producerIndexMap (projected to parallel loops) =
630 ///       affine_map<(i, j) -> (i, j)>
631 ///     consumerLoopToProducerLoop2 = affine_map<(i, j) -> (j, i)>
632 ///     Fused dimensions : i, j
633 ///
634 /// Example 3:
635 ///   linalg.copy(%s, %b)
636 ///   linalg.matmul ins(%a, %b) outs(%c)
637 ///
638 ///   Number of parallel loops = 2
639 ///   produceIndexMap : affine_map<(i, j) -> (i, j)>
640 ///   consumerLoopToProduceLoops = affine_map<(i, j, k) -> (k, j)>
641 ///     submap with only parallel loops = affine_map<(i, j) -> (j)>
642 ///   Fused dimensions : j
643 static std::set<unsigned>
644 collectFusableLoops(ArrayRef<LinalgOp> ops,
645                     const FusableOpDependencesTy &fusableDependences) {
646   assert(!ops.empty());
647   auto getNumOuterParallelLoops = [](LinalgOp linalgOp) {
648     return linalgOp.iterator_types()
649         .getValue()
650         .take_while([](Attribute attr) -> bool {
651           return attr.cast<StringAttr>().getValue() ==
652                  getParallelIteratorTypeName();
653         })
654         .size();
655   };
656 
657   size_t numOuterParallelLoops = getNumOuterParallelLoops(ops.back());
658   for (auto op : ops.drop_back()) {
659     numOuterParallelLoops =
660         std::min(numOuterParallelLoops, getNumOuterParallelLoops(op));
661   }
662 
663   std::set<unsigned> fusableLoops;
664   auto range = llvm::seq<unsigned>(0, numOuterParallelLoops);
665   fusableLoops.insert(range.begin(), range.end());
666 
667   for (auto op : reverse(ops)) {
668     for (auto dependence : fusableDependences.lookup(op)) {
669       LLVM_DEBUG({
670         llvm::dbgs() << "\t fusable :";
671         for (unsigned i : fusableLoops)
672           llvm::dbgs() << " " << i;
673         llvm::dbgs() << "\n";
674       });
675 
676       Optional<AffineMap> consumerLoopToProducerLoop =
677           getConsumerLoopToProducerLoopMap(dependence);
678       if (!consumerLoopToProducerLoop) {
679         op.emitRemark("failed to get map from consumer loop to producer loop");
680         return {};
681       }
682       // todo: This condition is only an implementation limitation. When fusing
683       // the operation, if the accesses in the producer/consumer are transposes
684       // of each other, the loop bounds for the tiled producer can be
685       // manipulated accordingly. This requires some additional bookkeeping in
686       // the implementation of tile+fuse that is deferred to later.
687       if (doesTransposeAccess(*consumerLoopToProducerLoop, fusableLoops)) {
688         op.emitRemark("unhandled fusion when fusion requires permutation");
689         return {};
690       }
691 
692       std::set<unsigned> candidates;
693       for (AffineExpr expr : consumerLoopToProducerLoop->getResults()) {
694         unsigned position = expr.cast<AffineDimExpr>().getPosition();
695         if (fusableLoops.count(position))
696           candidates.insert(position);
697       }
698       LLVM_DEBUG({
699         llvm::dbgs() << "\t candidates :";
700         for (unsigned i : candidates)
701           llvm::dbgs() << " " << i;
702         llvm::dbgs() << "\n";
703       });
704       if (candidates.empty())
705         return {};
706       std::swap(candidates, fusableLoops);
707     }
708   }
709 
710   return fusableLoops;
711 }
712 
713 /// Find all dependences that are fusable.
714 FusableOpDependencesTy mlir::linalg::findAllFusableDependences(
715     ArrayRef<LinalgOp> ops, const LinalgDependenceGraph &dependenceGraph) {
716   FusableOpDependencesTy fusableDependences;
717   DenseMap<Operation *, SmallVector<AffineMap, 1>> fusedProducerIndexingMap;
718   for (LinalgOp op : reverse(ops)) {
719     for (OpOperand &opOperand : op.getShapedOpOperands()) {
720       Optional<LinalgDependenceGraph::LinalgDependenceGraphElem>
721           fusableDependence = findFusableProducer(opOperand, dependenceGraph);
722       if (!fusableDependence)
723         continue;
724       // Canonicalize indexed generic ops before fusion.
725       if (isa<IndexedGenericOp>(fusableDependence->getDependentOp()))
726         continue;
727       LinalgOp producerOp =
728           dyn_cast<LinalgOp>(fusableDependence->getDependentOp());
729       if (!producerOp)
730         continue;
731       // Do not fuse dependences that are to operations not in the same basic
732       // block. This avoid moving fused operations across loops that might
733       // themselves carry dependency making the fusion illegal.
734       if (producerOp->getBlock() != op->getBlock())
735         continue;
736 
737       // Make sure that the indexing map of the view used for fusion in the
738       // producer is a projected permutation.
739       Optional<AffineMap> producerMap =
740           fusableDependence->getDependentOpViewIndexingMap();
741       Optional<AffineMap> consumerMap =
742           fusableDependence->getIndexingOpViewIndexingMap();
743       assert(
744           consumerMap &&
745           "unable to find indexing map of operand/result of indexing OpView");
746       fusedProducerIndexingMap[producerOp.getOperation()].push_back(
747           *consumerMap);
748       if (!producerMap || !producerMap->isProjectedPermutation() ||
749           !consumerMap->isProjectedPermutation())
750         continue;
751 
752       fusableDependences[producerOp.getOperation()].push_back(
753           *fusableDependence);
754     }
755   }
756   // TODO: Currently fusion would not be legal if the fusable dependence is to
757   // the same producer but different indexing map in the consumer. Fix this, but
758   // in the meanwhile disallow such a fusion.
759   for (auto useIndexingMapsList : fusedProducerIndexingMap) {
760     AffineMap map1 = useIndexingMapsList.second.front();
761     for (AffineMap map2 :
762          ArrayRef<AffineMap>(useIndexingMapsList.second).drop_front()) {
763       if (map1 != map2) {
764         fusableDependences.erase(useIndexingMapsList.first);
765         break;
766       }
767     }
768   }
769   return fusableDependences;
770 }
771 
772 /// Tile the fused loops in the root operation, by setting the tile sizes for
773 /// all other loops to zero (those will be tiled later).
774 static Optional<TiledLinalgOp> tileRootOperation(
775     OpBuilder &builder, LinalgOp op, ArrayRef<Value> tileSizeVector,
776     const LinalgTilingOptions &options, const std::set<unsigned> &fusedLoops) {
777   SmallVector<Value, 4> tileSizes(tileSizeVector.begin(), tileSizeVector.end());
778   auto zero = std_constant_index(0);
779   for (unsigned i = 0, e = tileSizes.size(); i != e; ++i)
780     if (!fusedLoops.count(i))
781       tileSizes[i] = zero;
782   LinalgTilingOptions tileFusedLoopsOptions = options;
783   tileFusedLoopsOptions.setTileSizes(tileSizes);
784   return tileLinalgOp(builder, op, tileFusedLoopsOptions);
785 }
786 
787 /// Fuse the operations in `fusionCandidates` with `tiledOp`. Latter is expected
788 /// to be a tiled operation such that it is valid to fuse all operations in
789 /// `fusionCandidates`, i.e. move the operation within the inter-tile loops of
790 /// `tiledOp`.
791 static SmallVector<LinalgOp, 1>
792 fuseOperations(OpBuilder &builder, LinalgOp rootOp, TiledLinalgOp tiledLinalgOp,
793                ArrayRef<LinalgOp> fusionCandidates,
794                const FusableOpDependencesTy &fusableDependences,
795                const std::set<unsigned> &fusedLoops) {
796   LinalgOp tiledOp = tiledLinalgOp.op;
797   OpBuilder::InsertionGuard guard(builder);
798   builder.setInsertionPoint(tiledOp);
799 
800   DenseMap<unsigned, Range> fusedLoopsAndRanges;
801   for (unsigned loop : fusedLoops) {
802     ShapeDimension shapeDim = getShapeDefiningLoopRange(tiledOp, loop, true);
803     fusedLoopsAndRanges[loop] = getRangeFromOperandShape(
804         builder, tiledOp.getLoc(), shapeDim.shape, shapeDim.dimension);
805   }
806 
807   SmallVector<LinalgOp, 1> fusedOps(fusionCandidates.size());
808   DenseMap<Operation *, LinalgOp> origOpToFusedOp;
809   origOpToFusedOp[rootOp.getOperation()] = tiledOp;
810   for (auto candidate : enumerate(llvm::reverse(fusionCandidates))) {
811     LinalgOp origOp = candidate.value();
812     LinalgOp fusedOp = fuse(builder, origOp, fusedLoopsAndRanges);
813     origOpToFusedOp[origOp.getOperation()] = fusedOp;
814     fusedOps[fusionCandidates.size() - candidate.index() - 1] = fusedOp;
815 
816     // Prepare the builder for the next insertion point.
817     auto guard =
818         llvm::make_scope_exit([&]() { builder.setInsertionPoint(fusedOp); });
819     if (!origOp.hasTensorSemantics())
820       continue;
821 
822     // If the producer consumer operations are linalg operations on tensors, the
823     // dependence is due to value produced (as a return tensor) by the producer
824     // and used in the consumer. The returned value of the fused op needs to be
825     // made the operand of the tiled/fused consumer operation. By construction
826     // the value returned by the producer is the value used by the consumer.
827     for (auto &dependence : fusableDependences.lookup(origOp.getOperation())) {
828       if (dependence.dependenceType !=
829           LinalgDependenceGraph::DependenceType::RAW)
830         continue;
831 
832       unsigned resultIndex =
833           dependence.getDependentOpViewResultNum().getValue();
834       LinalgOp consumer = origOpToFusedOp.lookup(dependence.getIndexingOp());
835       if (!consumer)
836         continue;
837 
838       Value replacementValue = fusedOp.getOperation()->getResult(resultIndex);
839       consumer.getOperation()->setOperand(
840           dependence.getIndexingOpViewOperandNum().getValue(),
841           replacementValue);
842     }
843 
844     // At this point, all Linalg uses of the tensors produced by `origOp` have
845     // been replaced. However, there may still be "output tensor"-like uses
846     // coming from WAW dependencies.
847     // All these uses are iter_args of the outermost loop (TODO: add a check).
848     // Such iter_args uses serve 2 purposes:
849     //  1. give a shape to the output
850     //  2. encode destructive updates that may be inplaceable by bufferization.
851     // To keep the second type of information while letting the unfused op die
852     // unused, we need to forward the producer output operand.
853     for (auto &operand :
854          cast<scf::ForOp>(tiledLinalgOp.loops.front()).getIterOpOperands())
855       if (auto opResult = operand.get().dyn_cast<OpResult>())
856         if (opResult.getOwner() == origOp)
857           operand.set(origOp.getOutputTensors()[opResult.getResultNumber()]);
858   }
859   return fusedOps;
860 }
861 
862 template <typename LoopType>
863 static Optional<TiledAndFusedLinalgOps>
864 tileAndFuseLinalgOpsImpl(OpBuilder &builder, ArrayRef<LinalgOp> ops,
865                          const LinalgDependenceGraph &dependenceGraph,
866                          const LinalgTilingOptions &tilingOptions) {
867   if (ops.size() < 2)
868     return llvm::None;
869   LinalgOp rootOp = ops.back();
870   if (!llvm::all_of(
871           ops,
872           [](LinalgOp linalgOp) { return linalgOp.hasBufferSemantics(); }) &&
873       !llvm::all_of(ops, [](LinalgOp linalgOp) {
874         return linalgOp.hasTensorSemantics();
875       })) {
876     rootOp.emitError(
877         "unable to fuse operations that have tensor semantics with operations "
878         "that have buffer semantics and viceversa.");
879     return llvm::None;
880   }
881   // TODO: Support interchange with tile + fuse. This might actually help do
882   // better fusion.
883   if (!tilingOptions.interchangeVector.empty()) {
884     rootOp.emitRemark("unable to handle tile and fuse with interchange");
885     return llvm::None;
886   }
887 
888   OpBuilder::InsertionGuard guard(builder);
889   builder.setInsertionPoint(rootOp);
890   ScopedContext scope(builder, rootOp.getLoc());
891 
892   // Find all the producers.
893   LLVM_DEBUG(llvm::dbgs() << "findAllFusableDependences\n");
894   FusableOpDependencesTy fusableDependences =
895       findAllFusableDependences(ops, dependenceGraph);
896   if (fusableDependences.empty()) {
897     LLVM_DEBUG(llvm::dbgs() << "no fusable dependencies found\n");
898     return llvm::None;
899   }
900 
901   TiledAndFusedLinalgOps ret;
902   // Find the loops that can be tiled and fused.
903   LLVM_DEBUG(llvm::dbgs() << "collectFusableLoops\n");
904   ret.fusedLoopDims = collectFusableLoops(ops, fusableDependences);
905 
906   // If there are no fusable dependences or there are no tile+fusable loops,
907   // just return.
908   if (ret.fusedLoopDims.empty()) {
909     LLVM_DEBUG(llvm::dbgs() << "no fusable loops found\n");
910     return llvm::None;
911   }
912 
913   // Tile the fused loops in the last operation in the list.
914   SmallVector<Value, 4> tileSizeVector =
915       tilingOptions.tileSizeComputationFunction(builder, rootOp);
916   Optional<TiledLinalgOp> tiledRootOp = tileRootOperation(
917       builder, rootOp, tileSizeVector, tilingOptions, ret.fusedLoopDims);
918   if (!tiledRootOp) {
919     rootOp.emitRemark("failed to tile the fused loops");
920     return llvm::None;
921   }
922   ret.op = tiledRootOp->op;
923   ret.fusedLoops.assign(tiledRootOp->loops.begin(), tiledRootOp->loops.end());
924 
925   // Fuse the other operations into the fused inter-tile loops produced above.
926   ret.fusedProducers =
927       fuseOperations(builder, rootOp, *tiledRootOp, ops.drop_back(),
928                      fusableDependences, ret.fusedLoopDims);
929 
930   return ret;
931 }
932 
933 Optional<TiledAndFusedLinalgOps>
934 mlir::linalg::tileAndFuseLinalgOps(OpBuilder &builder, ArrayRef<LinalgOp> ops,
935                                    const LinalgDependenceGraph &dependenceGraph,
936                                    const LinalgTilingOptions &tilingOptions) {
937   switch (tilingOptions.loopType) {
938   case LinalgTilingLoopType::Loops:
939     return tileAndFuseLinalgOpsImpl<scf::ForOp>(builder, ops, dependenceGraph,
940                                                 tilingOptions);
941   case LinalgTilingLoopType::ParallelLoops:
942     return tileAndFuseLinalgOpsImpl<scf::ParallelOp>(
943         builder, ops, dependenceGraph, tilingOptions);
944   default:;
945   }
946   return llvm::None;
947 }
948