MLIR  16.0.0git
Fusion.cpp
Go to the documentation of this file.
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"
23 #include "mlir/IR/AffineExpr.h"
24 #include "mlir/IR/AffineMap.h"
25 #include "mlir/IR/Dominance.h"
26 #include "mlir/Support/LLVM.h"
29 #include "llvm/ADT/MapVector.h"
30 #include "llvm/ADT/ScopeExit.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/Debug.h"
33 
34 #include <set>
35 
36 #define DEBUG_TYPE "linalg-fusion"
37 
38 using namespace mlir;
39 using namespace mlir::linalg;
40 
41 /// Implements a simple high-level fusion pass on linalg structured operations.
42 ///
43 /// In each block, linalg ops are processed in reverse textual order.
44 /// Given a linalg op `O`, fusion occurs by:
45 /// 1. inspecting the linalg ops that write into the views read by `O`. There
46 /// are 2 cases:
47 /// a) buffer case: use the SSA value of the views and a simple alias
48 /// analysis on subview ops to determine producer-consumer dependences;
49 /// b) tensor case: use SSA use-def chains on extract_slice ops;
50 /// 2. greedily fuse the linalg ops that produce the subview/extract_slice.
51 /// 3. inspect the fused ops and determine whether they have other remaining
52 /// LinalgOp uses. If not, then erase the original producing linalg op.
53 ///
54 /// More advanced use cases, analyses as well as profitability heuristics are
55 /// left for future work.
56 
59  unsigned dimension;
60 };
61 
62 // Given an `op`, returns the first (`shape`, `dimension`) pair that identifies
63 // the loop range at `loopDepth`. The semantics of the loopToOperandRangesMaps
64 // guarantees at least one such dimension is found. If multiple candidates exist
65 // they must agree by construction (i.e. have the same size) and we just return
66 // the first one.
67 static ShapeDimension
68 getShapeDefiningLoopRange(LinalgOp op, unsigned loopDepth,
69  bool fromSubViewOpOnly = false) {
70  // Iterate over the inputs and outputs in order.
71  // Extract the subranges from the linearized ranges.
72  for (OpOperand *opOperand : op.getInputAndOutputOperands()) {
73  // The method `getRangeFromOperandShape` requires using SubViewOp or
74  // ExtractSliceOps. If the value isn't defined from there continue.
75  // todo: The method should be adapted to get the values from
76  // `ViewInterface`. The interface needs a `getOrCreateRanges` method which
77  // currently returns a `linalg.range`. The fix here is to move this op to
78  // `std` dialect and add the method to `ViewInterface`.
79  if (fromSubViewOpOnly &&
80  !isa_and_nonnull<memref::SubViewOp, tensor::ExtractSliceOp>(
81  opOperand->get().getDefiningOp()))
82  continue;
83 
84  AffineMap map = op.getTiedIndexingMap(opOperand);
85  LLVM_DEBUG(llvm::dbgs() << "getShapeDefiningLoopRange I/O idx: "
86  << opOperand->getOperandNumber() << "\n");
87  LLVM_DEBUG(llvm::dbgs()
88  << "getShapeDefiningLoopRange map: " << map << "\n");
89  SmallVector<Value, 8> shapeRanges(map.getNumResults(), nullptr);
90  for (const auto &en : llvm::enumerate(map.getResults())) {
91  auto dimExpr = en.value().dyn_cast<AffineDimExpr>();
92  if (!dimExpr)
93  continue;
94  if (loopDepth == en.value().cast<AffineDimExpr>().getPosition()) {
95  LLVM_DEBUG(llvm::dbgs() << "getShapeDefiningLoopRange loopDepth: "
96  << loopDepth << "\n");
97  LLVM_DEBUG(llvm::dbgs() << "getShapeDefiningLoopRange shape: "
98  << opOperand->get() << "\n");
99  return ShapeDimension{opOperand->get(),
100  static_cast<unsigned>(en.index())};
101  }
102  }
103  }
104  llvm_unreachable("Expect to be able to extract a shape defining loop range");
105 }
106 
107 static SmallVector<Value> getTiledOperands(LinalgOp producer) {
108  return producer.getInputAndOutputOperands();
109 }
110 
111 /// Fuses the producer by cloning the `producer`. The `fusedLoopsAndRanges`
112 /// provides the loop range information for the fused loops. The rest are
113 /// obtained from the producer itself, since they are not tiled + fused.
114 static LinalgOp fuse(OpBuilder &b, LinalgOp producer,
115  const DenseMap<unsigned, Range> &fusedLoopsAndRanges) {
116  SmallVector<OpFoldResult> ivs, tileSizes, sizeBounds;
117  SmallVector<Range> loopRanges;
118  Location loc = producer.getLoc();
119 
120  for (unsigned i = 0, e = producer.getNumLoops(); i < e; ++i) {
121  auto shapeDim = getShapeDefiningLoopRange(producer, i);
122  OpFoldResult dim =
123  createFoldedDimOp(b, loc, shapeDim.shape, shapeDim.dimension);
124  sizeBounds.push_back(dim);
125  auto it = fusedLoopsAndRanges.find(i);
126  if (it != fusedLoopsAndRanges.end()) {
127  ivs.push_back(it->second.offset);
128  tileSizes.push_back(it->second.size);
129  loopRanges.push_back(it->second);
130  LLVM_DEBUG(llvm::dbgs() << "tiled loop#" << i << " with LoopRange "
131  << loopRanges.back() << "\n");
132  } else {
133  tileSizes.push_back(b.getIndexAttr(0));
134  loopRanges.push_back(Range{b.getIndexAttr(0), dim, b.getIndexAttr(1)});
135  LLVM_DEBUG(llvm::dbgs() << "full loop#" << i << " with LoopRange "
136  << loopRanges.back() << "\n");
137  }
138  }
139 
140  SmallVector<Value, 8> clonedShapes;
141  clonedShapes.reserve(producer.getNumInputsAndOutputs());
142 
143  // Compute subranges for all tensor input/output operands.
144  clonedShapes.append(makeTiledShapes(
145  b, loc, producer, getTiledOperands(producer), ivs, tileSizes, sizeBounds,
146  /**omitPartialTileCheck=*/false));
147 
148  // Iterate over the results in order.
149  // Extract the subtensor type from the linearized range.
150  // Since we do not enforce any canonicalizations on the fly, this is always
151  // fully dynamic at construction time.
152  SmallVector<Type, 4> resultTypes;
153  resultTypes.reserve(producer->getNumResults());
154  for (RankedTensorType t : producer.getOutputTensorTypes()) {
155  unsigned rank = t.getRank();
156  SmallVector<int64_t, 4> staticOffsetsVector(
157  rank, ShapedType::kDynamicStrideOrOffset);
158  SmallVector<int64_t, 4> staticSizesVector(rank, ShapedType::kDynamicSize);
159  SmallVector<int64_t, 4> staticStridesVector(
160  rank, ShapedType::kDynamicStrideOrOffset);
161  resultTypes.push_back(tensor::ExtractSliceOp::inferResultType(
162  t.cast<RankedTensorType>(), staticOffsetsVector, staticSizesVector,
163  staticStridesVector));
164  }
165 
166  Operation *clonedOp = producer.clone(b, loc, resultTypes, clonedShapes);
167 
168  // Shift all IndexOp results by the tile offset.
169  SmallVector<OpFoldResult> allIvs = llvm::to_vector(
170  llvm::map_range(loopRanges, [&](Range range) { return range.offset; }));
171  offsetIndices(b, clonedOp, allIvs);
172 
173  return clonedOp;
174 }
175 
176 /// Get the loop range for a dimension `dim` based on the `shapedOperand`. It is
177 /// expected to be defined by a subview op or an extract_slice op.
179  Value shapedOperand, unsigned dim) {
180  Operation *shapeProducingOp = shapedOperand.getDefiningOp();
181  if (auto subViewOp = dyn_cast<memref::SubViewOp>(shapeProducingOp))
182  return subViewOp.getOrCreateRanges(b, loc)[dim];
183  if (auto sliceOp = dyn_cast<tensor::ExtractSliceOp>(shapeProducingOp))
184  return sliceOp.getOrCreateRanges(b, loc)[dim];
185  llvm_unreachable("SubviewOp or ExtractSliceOp expected");
186 }
187 
188 /// Fuses the producer into the loop immediately enclosing the consumer.
189 /// This is achieved by "recomputing" the producer at the time it
190 /// is needed just before the consumer.
191 static LinalgOp fuse(OpBuilder &b, LinalgOp producerOp, AffineMap producerMap,
192  OpOperand &consumerOpOperand) {
193  LLVM_DEBUG(llvm::dbgs() << "Producer map: " << producerMap << "\n");
194  DenseMap<unsigned, Range> fusedLoopsAndRanges;
195  Value shapedOperand = consumerOpOperand.get();
196  for (const auto &en : llvm::enumerate(producerMap.getResults())) {
197  unsigned posInProducerLoop = en.value().cast<AffineDimExpr>().getPosition();
198  fusedLoopsAndRanges[posInProducerLoop] = getRangeFromOperandShape(
199  b, consumerOpOperand.getOwner()->getLoc(), shapedOperand, en.index());
200  }
201  return fuse(b, producerOp, fusedLoopsAndRanges);
202 }
203 
204 // Encode structural fusion safety preconditions.
205 // Some of these will be lifted in the future with better analysis.
206 static bool isStructurallyFusableProducer(LinalgOp producer, Value consumedView,
207  LinalgOp consumer) {
208  assert(producer.hasBufferSemantics() &&
209  "expected linalg op with buffer semantics");
210  assert(consumer.hasBufferSemantics() &&
211  "expected linalg op with buffer semantics");
212  if (producer.getNumOutputs() != 1) {
213  LLVM_DEBUG(llvm::dbgs() << "\nNot structurally fusable (multi-output)");
214  return false;
215  }
216  // Only fuse when the producer block dominates.
217  DominanceInfo dom(producer.getOperation());
218  if (!dom.dominates(producer->getBlock(), consumer->getBlock())) {
219  LLVM_DEBUG(
220  llvm::dbgs()
221  << "\nNot structurally fusable (producer block does not dominate)");
222  return false;
223  }
224  return true;
225 }
226 
228  LinalgOp consumer,
229  Value consumedView,
230  LinalgOp producer) {
231  assert(producer.hasBufferSemantics() &&
232  "expected linalg op with buffer semantics");
233  assert(consumer.hasBufferSemantics() &&
234  "expected linalg op with buffer semantics");
235  // Make some simple structural checks that alleviate the need for more
236  // complex analyses.
237  if (!isStructurallyFusableProducer(producer, consumedView, consumer)) {
238  LLVM_DEBUG(llvm::dbgs() << "\n***Not static last write due to structure:\t"
239  << *producer.getOperation());
240  return false;
241  }
242  // Check for any interleaved write to consumedView.
243  if (!graph.findCoveringWrites(producer, consumer, consumedView).empty()) {
244  LLVM_DEBUG(llvm::dbgs() << "\n***Not fusable due to interleaved write:\t"
245  << *producer.getOperation());
246  return false;
247  }
248  return true;
249 }
250 
252  LinalgOp consumer, Value consumedView,
253  LinalgOp producer) {
254  assert(producer.hasBufferSemantics() &&
255  "expected linalg op with buffer semantics");
256  assert(consumer.hasBufferSemantics() &&
257  "expected linalg op with buffer semantics");
258  if (!isProducerLastWriteOfView(graph, consumer, consumedView, producer))
259  return false;
260  // Check for any fusion-preventing dependence to any shape read/written that
261  // would violate dependences.
262  if (!graph.findCoveringDependences(producer, consumer).empty()) {
263  LLVM_DEBUG(llvm::dbgs()
264  << "\n***Not fusable due to an interleaved dependence:\t"
265  << *producer.getOperation());
266  return false;
267  }
268  return true;
269 }
270 
271 /// For `consumer` with buffer semantics, find the Linalg operation on buffers
272 /// that is the last writer of `consumerOpOperand`. For now the fusable
273 /// dependence is returned as an instance of the `dependenceGraph`.
275 findFusableProducer(OpOperand &consumerOpOperand,
276  const LinalgDependenceGraph &dependenceGraph) {
277  LLVM_DEBUG(llvm::dbgs() << "findFusableProducer for: "
278  << consumerOpOperand.get() << " @"
279  << consumerOpOperand.getOperandNumber() << " in "
280  << *consumerOpOperand.getOwner() << "\n");
281  LinalgOp consumerOp = dyn_cast<LinalgOp>(consumerOpOperand.getOwner());
282  if (!consumerOp)
283  return failure();
284 
285  // Only consider RAW and WAW atm.
286  for (auto depType : {
287  LinalgDependenceGraph::DependenceType::RAW,
288  LinalgDependenceGraph::DependenceType::WAW,
289  }) {
290  LLVM_DEBUG(llvm::dbgs()
291  << "Dependencies into: " << *consumerOp.getOperation() << "\n");
292  for (auto dependence : llvm::make_filter_range(
293  dependenceGraph.getDependencesInto(consumerOp, depType),
295  LLVM_DEBUG(llvm::dbgs() << "Inspect dependence btw: "
296  << elem.getIndexingValue() << " and "
297  << elem.getDependentValue() << "\n");
298  Value v = elem.getIndexingValue();
299  Optional<unsigned> operandNum =
300  elem.getIndexingOpViewOperandNum();
301  return isa<LinalgOp>(elem.getDependentOp()) &&
302  v == consumerOpOperand.get() && operandNum &&
303  *operandNum == consumerOpOperand.getOperandNumber();
304  })) {
305  // Consumer consumes this view, `isStructurallyFusableProducer` also
306  // checks whether it is a strict subview of the producer view.
307  auto producer = cast<LinalgOp>(dependence.getDependentOp());
308  LLVM_DEBUG(llvm::dbgs()
309  << "\n"
311  << "producer: " << *dependence.getDependentOp()
312  << " view: " << dependence.getDependentValue() << "\n");
313 
314  // If the producer and consumer have tensor semantics, the only dependence
315  // between them is through a RAW dependence and they are fusable by
316  // construction. For buffer semantics need additional checks.
317  if (producer.hasBufferSemantics() && consumerOp.hasBufferSemantics() &&
318  isFusableInto(dependenceGraph, consumerOp, consumerOpOperand.get(),
319  producer))
320  return dependence;
321  if (producer.hasTensorSemantics() && consumerOp.hasTensorSemantics()) {
322  assert(dependence.dependenceType ==
323  LinalgDependenceGraph::DependenceType::RAW);
324  return dependence;
325  }
326  }
327  }
328  return failure();
329 }
330 
333  const LinalgDependenceGraph &graph) {
335  findFusableProducer(consumerOpOperand, graph);
336  if (!fusableDependence)
337  return failure();
338 
339  LinalgOp producerOp = dyn_cast<LinalgOp>(fusableDependence->getDependentOp());
340  if (!producerOp)
341  return failure();
342 
343  // If producer is already in the same block as consumer, we are done.
344  if (consumerOpOperand.get().getParentBlock() ==
345  fusableDependence->getDependentValue().getParentBlock())
346  return failure();
347 
348  Optional<AffineMap> producerMap =
349  fusableDependence->getDependentOpViewIndexingMap();
350  if (!producerMap)
351  return failure();
352 
353  // Must be a subview or an extract_slice to guarantee there are loops we can
354  // fuse into.
355  auto subView = consumerOpOperand.get().getDefiningOp<memref::SubViewOp>();
356  if (!subView) {
357  LLVM_DEBUG(llvm::dbgs() << "\nNot fusable (not a subview)");
358  return failure();
359  }
360 
361  // Fuse `producer` just before `consumer`.
363  b.setInsertionPoint(consumerOpOperand.getOwner());
364  LLVM_DEBUG(llvm::dbgs() << "Fuse into consumer: "
365  << *consumerOpOperand.getOwner() << "\n");
366 
367  auto fusedProducer = fuse(b, producerOp, *producerMap, consumerOpOperand);
368  return FusionInfo{producerOp, fusedProducer};
369 }
370 
371 /// Walk back use-def chain through scf::For yields.
372 /// Sets `producer` and `outputIndex` if it finds a producer LinalgOp
373 
374 // TODO(ravishankarm, ntv): This can be moved into the dependence graphs
375 // dependence tracking since the dependence tracking is similar to what is done
376 // w.r.t to buffers.
377 static void getProducerOfTensor(Value tensor, OpResult &opResult) {
378  if (!tensor.getType().isa<RankedTensorType>())
379  return;
380 
381  while (true) {
382  LLVM_DEBUG(llvm::dbgs() << "\ngetProducerOfTensor: " << tensor);
383  if (auto linalgOp = tensor.getDefiningOp<LinalgOp>()) {
384  opResult = tensor.cast<OpResult>();
385  return;
386  }
387  if (auto sliceOp = tensor.getDefiningOp<tensor::ExtractSliceOp>()) {
388  tensor = sliceOp.getSource();
389  continue;
390  }
391  if (auto blockArg = tensor.dyn_cast<BlockArgument>()) {
392  if (auto forOp = blockArg.getDefiningOp<scf::ForOp>()) {
393  tensor = *(forOp.getIterOperands().begin() + blockArg.getArgNumber());
394  continue;
395  }
396  }
397  return;
398  }
399 }
400 
403  Value inputTensor = consumerOpOperand.get();
404  OpResult producerOpResult;
405  getProducerOfTensor(inputTensor, producerOpResult);
406  if (!producerOpResult) {
407  LLVM_DEBUG(llvm::dbgs() << "\nUnable to find producer");
408  return failure();
409  }
410  return fuseProducerOfTensor(b, producerOpResult, consumerOpOperand);
411 }
412 
415  OpOperand &consumerOpOperand) {
416  auto producerOp = dyn_cast<LinalgOp>(producerOpResult.getOwner());
417  if (!producerOp)
418  return failure();
419 
420  LinalgOp consumerOp = dyn_cast<LinalgOp>(consumerOpOperand.getOwner());
421  if (!consumerOp)
422  return failure();
423 
424  Value inputTensor = consumerOpOperand.get();
425 
426  // Must be an extract_slice op to guarantee there are loops we can fuse into.
427  auto sliceOp = inputTensor.getDefiningOp<tensor::ExtractSliceOp>();
428  if (!sliceOp) {
429  LLVM_DEBUG(llvm::dbgs()
430  << "\nNot fusable, not an extract_slice op: " << inputTensor);
431  return failure();
432  }
433 
434  // If producer is already in the same block as consumer, we are done.
435  if (consumerOpOperand.get().getParentBlock() ==
436  producerOpResult.getParentBlock())
437  return failure();
438 
439  // Insert fused `producer` just before `consumer`.
441  b.setInsertionPoint(consumerOp);
442  LLVM_DEBUG(llvm::dbgs() << "Fuse into consumer: " << *consumerOp << "\n");
443  OpOperand *opOperand =
444  producerOp.getOutputOperand(producerOpResult.getResultNumber());
445  LinalgOp fusedProducer =
446  fuse(b, producerOp, producerOp.getTiedIndexingMap(opOperand),
447  consumerOpOperand);
448 
449  // Replace use.
450  // Canonicalizations are not guaranteed to have happened before constructing
451  // `fusedProducer`. In the tensor case this can result in temporary type
452  // mismatches. Insert a `tensor.cast` op to propagate the transformation
453  // invariant that types are compatible.
454  Value def = fusedProducer->getResult(producerOpResult.getResultNumber());
455  Type consumerType = consumerOpOperand.get().getType();
456  if (consumerType != def.getType())
457  def = b.create<tensor::CastOp>(fusedProducer.getLoc(), consumerType, def);
458  consumerOpOperand.set(def);
459  return FusionInfo{cast<LinalgOp>(producerOpResult.getOwner()), fusedProducer};
460 }
Include the generated interface declarations.
dependence_range getDependencesInto(Operation *dst, DependenceType dt) const
Returns the X such that X -> op is a dependence of type dt.
Operation is a basic unit of execution within MLIR.
Definition: Operation.h:28
bool isProducerLastWriteOfView(const LinalgDependenceGraph &graph, LinalgOp consumer, Value consumedView, LinalgOp producer)
Checks whether the specific producer is the last write to exactly the whole consumedView.
Definition: Fusion.cpp:227
This is a value defined by a result of an operation.
Definition: Value.h:425
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:344
static Range getRangeFromOperandShape(OpBuilder &b, Location loc, Value shapedOperand, unsigned dim)
Get the loop range for a dimension dim based on the shapedOperand.
Definition: Fusion.cpp:178
This class represents a single result from folding an operation.
Definition: OpDefinition.h:239
unsigned dimension
Definition: Fusion.cpp:59
A class for computing basic dominance information.
Definition: Dominance.h:117
SmallVector< Operation *, 8 > findCoveringDependences(LinalgOp srcLinalgOp, LinalgOp dstLinalgOp) const
Returns the operations that are interleaved between srcLinalgOp and dstLinalgOp and that are involved...
FailureOr< FusionInfo > fuseProducerOfBuffer(OpBuilder &b, OpOperand &consumerOpOperand, const LinalgDependenceGraph &graph)
Fuses producer into consumer if the producer is structurally feasible and the fusion would not violat...
Definition: Fusion.cpp:332
Implements a simple high-level fusion pass on linalg structured operations.
Definition: Fusion.cpp:57
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:48
Auxiliary range data structure to unpack the offset, size and stride operands into a list of triples...
Operation * getOwner() const
Returns the operation that owns this result.
Definition: Value.h:434
static void getProducerOfTensor(Value tensor, OpResult &opResult)
Walk back use-def chain through scf::For yields.
Definition: Fusion.cpp:377
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:404
unsigned getOperandNumber()
Return which operand this is in the OpOperand list of the Operation.
Definition: Value.cpp:212
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
bool isFusableInto(const LinalgDependenceGraph &graph, LinalgOp consumer, Value consumedView, LinalgOp producer)
Checks whether fusing the specific producer of the consumedView is feasible.
Definition: Fusion.cpp:251
This class provides support for representing a failure result, or a valid value of type T...
Definition: LogicalResult.h:78
static FailureOr< LinalgDependenceGraph::LinalgDependenceGraphElem > findFusableProducer(OpOperand &consumerOpOperand, const LinalgDependenceGraph &dependenceGraph)
For consumer with buffer semantics, find the Linalg operation on buffers that is the last writer of c...
Definition: Fusion.cpp:275
void set(IRValueT newValue)
Set the current value being used by this operand.
Definition: UseDefLists.h:140
Operation * clone(BlockAndValueMapping &mapper, CloneOptions options=CloneOptions::all())
Create a deep copy of this operation, remapping any operands that use values outside of the operation...
Definition: Operation.cpp:554
U dyn_cast() const
Definition: Value.h:100
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:233
unsigned getResultNumber() const
Returns the number of this result.
Definition: Value.h:437
Block * getParentBlock()
Return the Block in which this Value is defined.
Definition: Value.cpp:48
Data structure for holding a dependence graph that operates on LinalgOp and views as SSA values...
FailureOr< FusionInfo > fuseProducerOfTensor(OpBuilder &b, OpOperand &consumerOpOperand)
Tensor counterpart of fuseProducerOfBuffer.
Definition: Fusion.cpp:402
unsigned getNumResults() const
Definition: AffineMap.cpp:302
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:154
IRValueT get() const
Return the current value being used by this operand.
Definition: UseDefLists.h:137
SmallVector< Value > makeTiledShapes(OpBuilder &builder, Location loc, LinalgOp linalgOp, ValueRange valuesToTile, ArrayRef< OpFoldResult > ivs, ArrayRef< OpFoldResult > tileSizes, ArrayRef< OpFoldResult > sizeBounds, bool omitPartialTileCheck)
Creates extract_slice/subview ops for all valuesToTile of the given linalgOp with builder...
Definition: Utils.cpp:1075
A multi-dimensional affine map Affine map&#39;s are immutable like Type&#39;s, and they are uniqued...
Definition: AffineMap.h:42
This class represents an argument of a Block.
Definition: Value.h:300
ArrayRef< AffineExpr > getResults() const
Definition: AffineMap.cpp:307
OpFoldResult offset
SmallVector< Operation *, 8 > findCoveringWrites(LinalgOp srcLinalgOp, LinalgOp dstLinalgOp, Value view) const
Returns the operations that are interleaved between srcLinalgOp and dstLinalgOp and that are involved...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:72
static LinalgOp fuse(OpBuilder &b, LinalgOp producer, const DenseMap< unsigned, Range > &fusedLoopsAndRanges)
Fuses the producer by cloning the producer.
Definition: Fusion.cpp:114
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:85
OpFoldResult createFoldedDimOp(OpBuilder &b, Location loc, Value source, int64_t dim)
Definition: Utils.cpp:210
static bool isStructurallyFusableProducer(LinalgOp producer, Value consumedView, LinalgOp consumer)
Definition: Fusion.cpp:206
RAII guard to reset the insertion point of the builder when destroyed.
Definition: Builders.h:294
Type getType() const
Return the type of this value.
Definition: Value.h:118
A dimensional identifier appearing in an affine expression.
Definition: AffineExpr.h:216
static ShapeDimension getShapeDefiningLoopRange(LinalgOp op, unsigned loopDepth, bool fromSubViewOpOnly=false)
Definition: Fusion.cpp:68
Operation * getOwner() const
Return the owner of this operand.
Definition: UseDefLists.h:40
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:20
This class represents an operand of an operation.
Definition: Value.h:251
A struct containing the Linalg producer before and after fusion.
Definition: Utils.h:300
static SmallVector< Value > getTiledOperands(LinalgOp producer)
Definition: Fusion.cpp:107
U cast() const
Definition: Value.h:108
static StringRef getDependenceTypeStr(DependenceType depType)
bool isa() const
Definition: Types.h:254
void offsetIndices(OpBuilder &b, LinalgOp linalgOp, ArrayRef< OpFoldResult > offests)
Add the specified offsets to any linalg.index ops contained in the given linalgOp.
Definition: Utils.cpp:1096
Value shape
Definition: Fusion.cpp:58
This class helps build Operations.
Definition: Builders.h:192
IntegerAttr getIndexAttr(int64_t value)
Definition: Builders.cpp:95