MLIR  22.0.0git
BufferizableOpInterface.cpp
Go to the documentation of this file.
1 //===- BufferizableOpInterface.cpp - Bufferizable Ops ---=----------------===//
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 
15 #include "mlir/IR/AsmState.h"
16 #include "mlir/IR/Operation.h"
17 #include "mlir/IR/TypeUtilities.h"
18 #include "mlir/IR/Value.h"
20 #include "llvm/ADT/ScopeExit.h"
21 
22 //===----------------------------------------------------------------------===//
23 // BufferizableOpInterface
24 //===----------------------------------------------------------------------===//
25 
26 namespace mlir {
27 namespace bufferization {
28 
29 #include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.cpp.inc"
30 
31 } // namespace bufferization
32 } // namespace mlir
33 
35 
36 #define DEBUG_TYPE "bufferizable-op-interface"
37 
38 using namespace mlir;
39 using namespace bufferization;
40 
41 static bool isRepetitiveRegion(Region *region,
43  Operation *op = region->getParentOp();
44  if (auto bufferizableOp = options.dynCastBufferizableOp(op))
45  if (bufferizableOp.isRepetitiveRegion(region->getRegionNumber()))
46  return true;
47  return false;
48 }
49 
52  if (!op->getBlock())
53  return nullptr;
54  if (auto iter = enclosingRepetitiveRegionCache.find_as(op);
55  iter != enclosingRepetitiveRegionCache.end())
56  return iter->second;
57  return enclosingRepetitiveRegionCache[op] =
58  getEnclosingRepetitiveRegion(op->getBlock(), options);
59 }
60 
62  Value value, const BufferizationOptions &options) {
63  if (auto iter = enclosingRepetitiveRegionCache.find_as(value);
64  iter != enclosingRepetitiveRegionCache.end())
65  return iter->second;
66 
67  Region *region = value.getParentRegion();
68  // Collect all visited regions since we only know the repetitive region we
69  // want to map it to later on
70  SmallVector<Region *> visitedRegions;
71  while (region) {
72  visitedRegions.push_back(region);
73  if (isRepetitiveRegion(region, options))
74  break;
75  region = region->getParentRegion();
76  }
77  enclosingRepetitiveRegionCache[value] = region;
78  for (Region *r : visitedRegions)
79  enclosingRepetitiveRegionCache[r] = region;
80  return region;
81 }
82 
84  Block *block, const BufferizationOptions &options) {
85  if (auto iter = enclosingRepetitiveRegionCache.find_as(block);
86  iter != enclosingRepetitiveRegionCache.end())
87  return iter->second;
88 
89  Region *region = block->getParent();
90  Operation *op = nullptr;
91  // Collect all visited regions since we only know the repetitive region we
92  // want to map it to later on
93  SmallVector<Region *> visitedRegions;
94  do {
95  op = region->getParentOp();
96  if (isRepetitiveRegion(region, options))
97  break;
98  } while ((region = op->getParentRegion()));
99 
100  enclosingRepetitiveRegionCache[block] = region;
101  for (Region *r : visitedRegions)
102  enclosingRepetitiveRegionCache[r] = region;
103  return region;
104 }
105 
107  Operation *op1) {
108  auto key = std::make_pair(op0, op1);
109  if (auto iter = insideMutuallyExclusiveRegionsCache.find(key);
110  iter != insideMutuallyExclusiveRegionsCache.end())
111  return iter->second;
112  bool result = ::mlir::insideMutuallyExclusiveRegions(op0, op1);
113  // Populate results for both orderings of the ops.
114  insideMutuallyExclusiveRegionsCache[key] = result;
115  insideMutuallyExclusiveRegionsCache[std::make_pair(op1, op0)] = result;
116  return result;
117 }
118 
120  enclosingRepetitiveRegionCache.clear();
121  insideMutuallyExclusiveRegionsCache.clear();
122 }
123 
125  return symbolTables;
126 }
127 
129  Region *region, const BufferizationOptions &options) {
130  assert(isRepetitiveRegion(region, options) && "expected repetitive region");
131  while ((region = region->getParentRegion())) {
132  if (isRepetitiveRegion(region, options))
133  break;
134  }
135  return region;
136 }
137 
139  const BufferizationOptions &options) {
140  while (region) {
141  auto bufferizableOp = options.dynCastBufferizableOp(region->getParentOp());
142  if (bufferizableOp &&
143  bufferizableOp.isParallelRegion(region->getRegionNumber())) {
144  assert(isRepetitiveRegion(region, options) &&
145  "expected that all parallel regions are also repetitive regions");
146  return region;
147  }
148  region = region->getParentRegion();
149  }
150  return nullptr;
151 }
152 
154  if (auto opResult = llvm::dyn_cast<OpResult>(value))
155  return opResult.getDefiningOp();
156  return llvm::cast<BlockArgument>(value).getOwner()->getParentOp();
157 }
158 
159 /// Create an AllocTensorOp for the given shaped value. If `copy` is set, the
160 /// shaped value is copied. Otherwise, a tensor with undefined contents is
161 /// allocated.
163  OpBuilder &b, Location loc, Value shapedValue,
164  const BufferizationOptions &options, const BufferizationState &state,
165  bool copy) {
166  Value tensor;
167  if (llvm::isa<RankedTensorType>(shapedValue.getType())) {
168  tensor = shapedValue;
169  } else if (llvm::isa<MemRefType>(shapedValue.getType())) {
170  tensor = ToTensorOp::create(
171  b, loc, memref::getTensorTypeFromMemRefType(shapedValue.getType()),
172  shapedValue);
173  } else if (llvm::isa<UnrankedTensorType>(shapedValue.getType()) ||
174  llvm::isa<UnrankedMemRefType>(shapedValue.getType())) {
175  return getOwnerOfValue(shapedValue)
176  ->emitError("copying of unranked tensors is not implemented");
177  } else {
178  llvm_unreachable("expected RankedTensorType or MemRefType");
179  }
180  RankedTensorType tensorType = llvm::cast<RankedTensorType>(tensor.getType());
181  SmallVector<Value> dynamicSizes;
182  if (!copy) {
183  // Compute the dynamic part of the shape.
184  // First try to query the shape via ReifyRankedShapedTypeOpInterface.
185  bool reifiedShapes = false;
186  if (llvm::isa<RankedTensorType>(shapedValue.getType()) &&
187  llvm::isa<OpResult>(shapedValue)) {
188  ReifiedRankedShapedTypeDims resultDims;
189  if (succeeded(
190  reifyResultShapes(b, shapedValue.getDefiningOp(), resultDims))) {
191  reifiedShapes = true;
192  auto &shape =
193  resultDims[llvm::cast<OpResult>(shapedValue).getResultNumber()];
194  for (const auto &dim : enumerate(tensorType.getShape())) {
195  if (ShapedType::isDynamic(dim.value())) {
196  dynamicSizes.push_back(
197  getValueOrCreateConstantIndexOp(b, loc, shape[dim.index()]));
198  }
199  }
200  }
201  }
202 
203  // If the shape could not be reified, create DimOps.
204  if (!reifiedShapes)
205  populateDynamicDimSizes(b, loc, tensor, dynamicSizes);
206  }
207 
208  // Create AllocTensorOp.
209  auto allocTensorOp = AllocTensorOp::create(b, loc, tensorType, dynamicSizes,
210  copy ? tensor : Value());
211 
212  // Add 'memory_space' attribute. Not needed if 'copy' operand is specified.
213  if (copy)
214  return allocTensorOp.getResult();
215  auto copyBufferType =
216  detail::asMemRefType(getBufferType(tensor, options, state));
217  if (failed(copyBufferType))
218  return failure();
219  std::optional<Attribute> memorySpace = copyBufferType->getMemorySpace();
220  if (!memorySpace)
221  memorySpace = options.defaultMemorySpaceFn(tensorType);
222  if (memorySpace.has_value())
223  allocTensorOp.setMemorySpaceAttr(memorySpace.value());
224  return allocTensorOp.getResult();
225 }
226 
227 LogicalResult BufferizableOpInterface::resolveTensorOpOperandConflicts(
228  RewriterBase &rewriter, const AnalysisState &analysisState,
229  const BufferizationState &bufferizationState) {
230  OpBuilder::InsertionGuard g(rewriter);
231  Operation *op = getOperation();
232  SmallVector<OpOperand *> outOfPlaceOpOperands;
233  DenseSet<OpOperand *> copiedOpOperands;
234  SmallVector<Value> outOfPlaceValues;
235  DenseSet<Value> copiedOpValues;
236 
237  // Find all out-of-place OpOperands.
238  for (OpOperand &opOperand : op->getOpOperands()) {
239  Type operandType = opOperand.get().getType();
240  if (!llvm::isa<TensorType>(operandType))
241  continue;
242  if (analysisState.isInPlace(opOperand))
243  continue;
244  if (llvm::isa<UnrankedTensorType>(operandType))
245  return op->emitError("copying of unranked tensors is not implemented");
246 
247  AliasingValueList aliasingValues =
248  analysisState.getAliasingValues(opOperand);
249  if (aliasingValues.getNumAliases() == 1 &&
250  isa<OpResult>(aliasingValues.getAliases()[0].value) &&
251  !analysisState.bufferizesToMemoryWrite(opOperand) &&
252  analysisState
253  .getAliasingOpOperands(aliasingValues.getAliases()[0].value)
254  .getNumAliases() == 1 &&
255  !isa<UnrankedTensorType>(
256  aliasingValues.getAliases()[0].value.getType())) {
257  // The op itself does not write but may create exactly one alias. Instead
258  // of copying the OpOperand, copy the OpResult. The OpResult can sometimes
259  // be smaller than the OpOperand (e.g., in the case of an extract_slice,
260  // where the result is usually a smaller part of the source). Do not apply
261  // this optimization if the OpResult is an unranked tensor (because those
262  // cannot be copied at the moment).
263  Value value = aliasingValues.getAliases()[0].value;
264  outOfPlaceValues.push_back(value);
265  if (!analysisState.canOmitTensorCopy(opOperand))
266  copiedOpValues.insert(value);
267  } else {
268  // In all other cases, make a copy of the OpOperand.
269  outOfPlaceOpOperands.push_back(&opOperand);
270  if (!analysisState.canOmitTensorCopy(opOperand))
271  copiedOpOperands.insert(&opOperand);
272  }
273  }
274 
275  // Insert copies of OpOperands.
276  rewriter.setInsertionPoint(op);
277  for (OpOperand *opOperand : outOfPlaceOpOperands) {
278  FailureOr<Value> copy = allocateTensorForShapedValue(
279  rewriter, op->getLoc(), opOperand->get(), analysisState.getOptions(),
280  bufferizationState, copiedOpOperands.contains(opOperand));
281  if (failed(copy))
282  return failure();
283  rewriter.modifyOpInPlace(op, [&]() { opOperand->set(*copy); });
284  }
285 
286  // Insert copies of Values.
287  rewriter.setInsertionPointAfter(op);
288  for (Value value : outOfPlaceValues) {
289  FailureOr<Value> copy = allocateTensorForShapedValue(
290  rewriter, op->getLoc(), value, analysisState.getOptions(),
291  bufferizationState, copiedOpValues.count(value));
292  if (failed(copy))
293  return failure();
294  SmallVector<OpOperand *> uses = llvm::to_vector(
295  llvm::map_range(value.getUses(), [](OpOperand &use) { return &use; }));
296  for (OpOperand *use : uses) {
297  // Do not update the alloc_tensor op that we just created.
298  if (use->getOwner() == copy->getDefiningOp())
299  continue;
300  // tensor.dim ops may have been created to be used as alloc_tensor op
301  // dynamic extents. Do not update these either.
302  if (isa<tensor::DimOp>(use->getOwner()))
303  continue;
304  rewriter.modifyOpInPlace(use->getOwner(), [&]() { use->set(*copy); });
305  }
306  }
307 
308  return success();
309 }
310 
311 //===----------------------------------------------------------------------===//
312 // OpFilter
313 //===----------------------------------------------------------------------===//
314 
316  // All other ops: Allow/disallow according to filter.
317  bool isAllowed = !hasAllowRule();
318  for (const Entry &entry : entries) {
319  bool filterResult = entry.fn(op);
320  switch (entry.type) {
321  case Entry::ALLOW:
322  isAllowed |= filterResult;
323  break;
324  case Entry::DENY:
325  if (filterResult)
326  // DENY filter matches. This op is no allowed. (Even if other ALLOW
327  // filters may match.)
328  return false;
329  };
330  }
331  return isAllowed;
332 }
333 
334 //===----------------------------------------------------------------------===//
335 // BufferizationOptions
336 //===----------------------------------------------------------------------===//
337 
338 namespace {
339 
340 /// Default function arg type converter: Use a fully dynamic layout map.
341 BufferLikeType
342 defaultFunctionArgTypeConverter(TensorLikeType type, Attribute memorySpace,
343  func::FuncOp funcOp,
344  const BufferizationOptions &options) {
345  if (auto tensorType = mlir::dyn_cast<TensorType>(type)) {
346  return cast<BufferLikeType>(
347  getMemRefTypeWithFullyDynamicLayout(tensorType, memorySpace));
348  }
349 
350  // If not builtin, fallback to TensorLikeType::getBufferType()
351  auto bufferType =
352  type.getBufferType(options, [&]() { return funcOp->emitError(); });
353  assert(succeeded(bufferType) &&
354  "a valid buffer is always expected at function boundary");
355  return *bufferType;
356 }
357 /// Default unknown type converter: Use a fully dynamic layout map.
359 defaultUnknownTypeConverter(TensorType tensorType, Attribute memorySpace,
360  const BufferizationOptions &options) {
361  return getMemRefTypeWithFullyDynamicLayout(tensorType, memorySpace);
362 }
363 
364 } // namespace
365 
366 // Default constructor for BufferizationOptions.
368  : functionArgTypeConverterFn(defaultFunctionArgTypeConverter),
369  unknownTypeConverterFn(defaultUnknownTypeConverter) {}
370 
372  // Special case: If function boundary bufferization is deactivated, do not
373  // allow ops that belong to the `func` dialect.
374  bool isFuncBoundaryOp = isa_and_nonnull<func::FuncDialect>(op->getDialect());
375  if (!bufferizeFunctionBoundaries && isFuncBoundaryOp)
376  return false;
377 
378  return opFilter.isOpAllowed(op);
379 }
380 
381 BufferizableOpInterface
383  if (!isOpAllowed(op))
384  return nullptr;
385  auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op);
386  if (!bufferizableOp)
387  return nullptr;
388  return bufferizableOp;
389 }
390 
391 BufferizableOpInterface
393  return dynCastBufferizableOp(getOwnerOfValue(value));
394 }
395 
397  LayoutMapOption layoutMapOption) {
398  functionArgTypeConverterFn = [=](TensorLikeType type, Attribute memorySpace,
399  func::FuncOp funcOp,
400  const BufferizationOptions &options) {
401  if (auto tensorType = mlir::dyn_cast<TensorType>(type)) {
402  if (layoutMapOption == LayoutMapOption::IdentityLayoutMap)
403  return cast<BufferLikeType>(
405  memorySpace));
406  return cast<BufferLikeType>(
408  memorySpace));
409  }
410 
411  // If not builtin, fallback to TensorLikeType::getBufferType()
412  auto bufferType =
413  type.getBufferType(options, [&]() { return funcOp->emitError(); });
414  assert(succeeded(bufferType) &&
415  "a valid buffer is always expected at function boundary");
416  return *bufferType;
417  };
419  layoutMapOption == LayoutMapOption::InferLayoutMap;
420 }
421 
422 //===----------------------------------------------------------------------===//
423 // Helper functions for BufferizableOpInterface
424 //===----------------------------------------------------------------------===//
425 
426 static void setInsertionPointAfter(OpBuilder &b, Value value) {
427  if (auto bbArg = llvm::dyn_cast<BlockArgument>(value)) {
428  b.setInsertionPointToStart(bbArg.getOwner());
429  } else {
431  }
432 }
433 
434 /// Determine which OpOperand* will alias with `value` if the op is bufferized
435 /// in place. Return all tensor OpOperand* if the op is not bufferizable.
437  if (Operation *op = getOwnerOfValue(value))
438  if (auto bufferizableOp = getOptions().dynCastBufferizableOp(op))
439  return bufferizableOp.getAliasingOpOperands(value, *this);
440 
441  // The op is not bufferizable.
443 }
444 
445 /// Determine which Values will alias with `opOperand` if the op is bufferized
446 /// in place. Return all tensor Values if the op is not bufferizable.
448  if (auto bufferizableOp =
449  getOptions().dynCastBufferizableOp(opOperand.getOwner()))
450  return bufferizableOp.getAliasingValues(opOperand, *this);
451 
452  // The op is not bufferizable.
453  return detail::unknownGetAliasingValues(opOperand);
454 }
455 
456 /// Return true if `opOperand` bufferizes to a memory read. Return `true` if the
457 /// op is not bufferizable.
459  if (auto bufferizableOp =
460  getOptions().dynCastBufferizableOp(opOperand.getOwner()))
461  return bufferizableOp.bufferizesToMemoryRead(opOperand, *this);
462 
463  // Unknown op that returns a tensor. The inplace analysis does not support it.
464  // Conservatively return true.
465  return true;
466 }
467 
468 /// Return true if `opOperand` bufferizes to a memory write. Return
469 /// `true` if the op is not bufferizable.
471  if (auto bufferizableOp =
472  getOptions().dynCastBufferizableOp(opOperand.getOwner()))
473  return bufferizableOp.bufferizesToMemoryWrite(opOperand, *this);
474 
475  // Unknown op that returns a tensor. The inplace analysis does not support it.
476  // Conservatively return true.
477  return true;
478 }
479 
480 /// Return true if `opOperand` does neither read nor write but bufferizes to an
481 /// alias. Return false if the op is not bufferizable.
483  if (auto bufferizableOp =
484  getOptions().dynCastBufferizableOp(opOperand.getOwner()))
485  return bufferizableOp.bufferizesToAliasOnly(opOperand, *this);
486 
487  // Unknown op that returns a tensor. The inplace analysis does not support it.
488  // Conservatively return false.
489  return false;
490 }
491 
493  auto opResult = llvm::dyn_cast<OpResult>(value);
494  if (!opResult)
495  return true;
496  auto bufferizableOp = getOptions().dynCastBufferizableOp(value);
497  if (!bufferizableOp)
498  return true;
499  return bufferizableOp.resultBufferizesToMemoryWrite(opResult, *this);
500 }
501 
502 /// Return true if the given value is read by an op that bufferizes to a memory
503 /// read. Also takes into account ops that create an alias but do not read by
504 /// themselves (e.g., ExtractSliceOp).
506  assert(llvm::isa<TensorType>(value.getType()) && "expected TensorType");
507  SmallVector<OpOperand *> workingSet;
508  DenseSet<OpOperand *> visited;
509  for (OpOperand &use : value.getUses())
510  workingSet.push_back(&use);
511 
512  while (!workingSet.empty()) {
513  OpOperand *uMaybeReading = workingSet.pop_back_val();
514  if (!visited.insert(uMaybeReading).second)
515  continue;
516 
517  // Skip over all ops that neither read nor write (but create an alias).
518  if (bufferizesToAliasOnly(*uMaybeReading))
519  for (AliasingValue alias : getAliasingValues(*uMaybeReading))
520  for (OpOperand &use : alias.value.getUses())
521  workingSet.push_back(&use);
522  if (bufferizesToMemoryRead(*uMaybeReading))
523  return true;
524  }
525 
526  return false;
527 }
528 
529 // Starting from `opOperand`, follow the use-def chain in reverse, always
530 // selecting the aliasing OpOperands. Find and return Values for which
531 // `condition` evaluates to true. Uses of such matching Values are not
532 // traversed any further, the visited aliasing opOperands will be preserved
533 // through `visitedOpOperands`.
535  OpOperand *opOperand, llvm::function_ref<bool(Value)> condition,
537  llvm::DenseSet<OpOperand *> *visitedOpOperands) const {
538  llvm::DenseSet<Value> visited;
539  llvm::SetVector<Value> result, workingSet;
540  workingSet.insert(opOperand->get());
541 
542  if (visitedOpOperands)
543  visitedOpOperands->insert(opOperand);
544 
545  while (!workingSet.empty()) {
546  Value value = workingSet.pop_back_val();
547 
548  if (!config.revisitAlreadyVisitedValues && visited.contains(value)) {
549  // Stop traversal if value was already visited.
550  if (config.alwaysIncludeLeaves)
551  result.insert(value);
552  continue;
553  }
554  visited.insert(value);
555 
556  if (condition(value)) {
557  result.insert(value);
558  continue;
559  }
560 
561  if (!config.followUnknownOps && !options.dynCastBufferizableOp(value)) {
562  // Stop iterating if `followUnknownOps` is unset and the op is either
563  // not bufferizable or excluded in the OpFilter.
564  if (config.alwaysIncludeLeaves)
565  result.insert(value);
566  continue;
567  }
568 
570  if (aliases.getNumAliases() == 0) {
571  // The traversal ends naturally if there are no more OpOperands that
572  // could be followed.
573  if (config.alwaysIncludeLeaves)
574  result.insert(value);
575  continue;
576  }
577 
578  for (AliasingOpOperand a : aliases) {
579  if (config.followEquivalentOnly &&
580  a.relation != BufferRelation::Equivalent) {
581  // Stop iterating if `followEquivalentOnly` is set but the alias is not
582  // equivalent.
583  if (config.alwaysIncludeLeaves)
584  result.insert(value);
585  continue;
586  }
587 
588  if (config.followInPlaceOnly && !isInPlace(*a.opOperand)) {
589  // Stop iterating if `followInPlaceOnly` is set but the alias is
590  // out-of-place.
591  if (config.alwaysIncludeLeaves)
592  result.insert(value);
593  continue;
594  }
595 
596  if (config.followSameTypeOrCastsOnly &&
597  a.opOperand->get().getType() != value.getType() &&
598  !value.getDefiningOp<CastOpInterface>()) {
599  // Stop iterating if `followSameTypeOrCastsOnly` is set but the alias is
600  // has a different type and the op is not a cast.
601  if (config.alwaysIncludeLeaves)
602  result.insert(value);
603  continue;
604  }
605 
606  workingSet.insert(a.opOperand->get());
607  if (visitedOpOperands)
608  visitedOpOperands->insert(a.opOperand);
609  }
610  }
611 
612  return result;
613 }
614 
615 // Find the values that define the contents of the given operand's value.
619  config.alwaysIncludeLeaves = false;
621  opOperand, [&](Value v) { return this->bufferizesToMemoryWrite(v); },
622  config);
623 }
624 
627 
629  : options(options), type(type) {
631  options.stateInitializers)
632  fn(*this);
633 }
634 
636  // Do not copy if the tensor has undefined contents.
637  if (hasUndefinedContents(&opOperand))
638  return true;
639 
640  // Do not copy if the buffer of the tensor is entirely overwritten (with
641  // values that do not depend on the old tensor).
642  if (bufferizesToMemoryWrite(opOperand) && !bufferizesToMemoryRead(opOperand))
643  return true;
644 
645  // Do not copy if the tensor is never read.
646  AliasingValueList aliases = getAliasingValues(opOperand);
647  if (!bufferizesToMemoryRead(opOperand) &&
648  llvm::none_of(aliases,
649  [&](AliasingValue a) { return isValueRead(a.value); }))
650  return true;
651 
652  // Default: Cannot omit the copy.
653  return false;
654 }
655 
656 bool AnalysisState::isInPlace(OpOperand &opOperand) const {
657  // ToBufferOps are always in-place.
658  if (isa<ToBufferOp>(opOperand.getOwner()))
659  return true;
660 
661  // In the absence of analysis information, OpOperands that bufferize to a
662  // memory write are out-of-place, i.e., an alloc and copy is inserted.
663  return !bufferizesToMemoryWrite(opOperand);
664 }
665 
667  // In the absence of analysis information, we do not know if the values are
668  // equivalent. The conservative answer is "false".
669  return false;
670 }
671 
673  // In the absence of analysis information, we do not know if the values may be
674  // aliasing. The conservative answer is "true".
675  return true;
676 }
677 
679  // In the absence of analysis information, the conservative answer is "false".
680  return false;
681 }
682 
683 // bufferization.to_buffer is not allowed to change the rank.
684 static void ensureToBufferOpIsValid(Value tensor, Type memrefType) {
685 #ifndef NDEBUG
686  auto rankedTensorType = llvm::dyn_cast<RankedTensorType>(tensor.getType());
687  assert((!rankedTensorType || llvm::cast<MemRefType>(memrefType).getRank() ==
688  rankedTensorType.getRank()) &&
689  "to_buffer would be invalid: mismatching ranks");
690 #endif
691 }
692 
693 FailureOr<Value> bufferization::getBuffer(RewriterBase &rewriter, Value value,
695  const BufferizationState &state) {
696 #ifndef NDEBUG
697  auto tensorType = llvm::dyn_cast<TensorLikeType>(value.getType());
698  assert(tensorType && "unexpected non-tensor type");
699 #endif // NDEBUG
700 
701  // Replace "%t = to_tensor %m" with %m.
702  if (auto toTensorOp = value.getDefiningOp<bufferization::ToTensorOp>())
703  return toTensorOp.getBuffer();
704 
705  // Insert to_buffer op.
706  OpBuilder::InsertionGuard g(rewriter);
707  setInsertionPointAfter(rewriter, value);
708  FailureOr<BufferLikeType> bufferType = getBufferType(value, options, state);
709  if (failed(bufferType))
710  return failure();
711  ensureToBufferOpIsValid(value, *bufferType);
712  return bufferization::ToBufferOp::create(rewriter, value.getLoc(),
713  *bufferType, value)
714  .getResult();
715 }
716 
717 /// Return the buffer type for a given Value (tensor) after bufferization.
718 FailureOr<BufferLikeType>
720  const BufferizationState &state) {
721  SmallVector<Value> invocationStack;
722  return getBufferType(value, options, state, invocationStack);
723 }
724 
725 /// Return the buffer type for a given Value (tensor) after bufferization.
726 FailureOr<BufferLikeType>
728  const BufferizationState &state,
729  SmallVector<Value> &invocationStack) {
730  assert(llvm::isa<TensorLikeType>(value.getType()) &&
731  "unexpected non-tensor type");
732  invocationStack.push_back(value);
733  auto popFromStack =
734  llvm::make_scope_exit([&]() { invocationStack.pop_back(); });
735 
736  // Try querying BufferizableOpInterface.
737  Operation *op = getOwnerOfValue(value);
738  auto bufferizableOp = options.dynCastBufferizableOp(op);
739  if (bufferizableOp)
740  return bufferizableOp.getBufferType(value, options, state, invocationStack);
741 
742  // Op is not bufferizable.
743  return cast<TensorLikeType>(value.getType()).getBufferType(options, [&]() {
744  return op->emitError();
745  });
746 }
747 
749  if (auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op))
750  return bufferizableOp.hasTensorSemantics();
752 }
753 
755  Operation *op,
756  ValueRange values) {
757  assert(values.size() == op->getNumResults() &&
758  "expected one value per OpResult");
759  OpBuilder::InsertionGuard g(rewriter);
760 
761  // Replace all OpResults with the given values.
762  SmallVector<Value> replacements;
763  for (OpResult opResult : op->getOpResults()) {
764  Value replacement = values[opResult.getResultNumber()];
765  if (llvm::isa<TensorLikeType>(opResult.getType())) {
766  // The OpResult is a tensor. Such values are replaced with memrefs during
767  // bufferization.
768  assert(llvm::isa<BufferLikeType>(replacement.getType()) &&
769  "tensor op result should be replaced with a buffer value");
770  // The existing uses of the OpResult still expect a tensor. Insert a
771  // ToTensorOp. Throughout bufferization, this ToTensorOp will gradually
772  // loose all of its users and eventually DCE away.
773  rewriter.setInsertionPointAfter(op);
774  replacement = bufferization::ToTensorOp::create(
775  rewriter, replacement.getLoc(), opResult.getType(), replacement);
776  }
777  replacements.push_back(replacement);
778  }
779 
780  rewriter.replaceOp(op, replacements);
781 }
782 
783 //===----------------------------------------------------------------------===//
784 // Bufferization-specific scoped alloc insertion support.
785 //===----------------------------------------------------------------------===//
786 
787 /// Create a memref allocation with the given type and dynamic extents.
789  MemRefType type,
790  ValueRange dynShape) const {
791  if (allocationFn)
792  return (*allocationFn)(b, loc, type, dynShape, bufferAlignment);
793 
794  // Default bufferallocation via AllocOp.
795  if (bufferAlignment != 0)
796  return memref::AllocOp::create(b, loc, type, dynShape,
798  .getResult();
799  return memref::AllocOp::create(b, loc, type, dynShape).getResult();
800 }
801 
802 /// Create a memory copy between two memref buffers.
804  Value from, Value to) const {
805  if (memCpyFn)
806  return (*memCpyFn)(b, loc, from, to);
807 
808  memref::CopyOp::create(b, loc, from, to);
809  return success();
810 }
811 
812 //===----------------------------------------------------------------------===//
813 // Bufferization-specific IRMapping support with debugging.
814 //===----------------------------------------------------------------------===//
815 
818  MemRefLayoutAttrInterface layout,
819  Attribute memorySpace) {
820  // Case 1: Unranked memref type.
821  if (auto unrankedTensorType =
822  llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
823  assert(!layout && "UnrankedTensorType cannot have a layout map");
824  return UnrankedMemRefType::get(unrankedTensorType.getElementType(),
825  memorySpace);
826  }
827 
828  // Case 2: Ranked memref type with specified layout.
829  auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
830  if (layout) {
831  return MemRefType::get(rankedTensorType.getShape(),
832  rankedTensorType.getElementType(), layout,
833  memorySpace);
834  }
835 
836  return options.unknownTypeConverterFn(tensorType, memorySpace, options);
837 }
838 
841  Attribute memorySpace) {
842  // Case 1: Unranked memref type.
843  if (auto unrankedTensorType =
844  llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
845  return UnrankedMemRefType::get(unrankedTensorType.getElementType(),
846  memorySpace);
847  }
848 
849  // Case 2: Ranked memref type.
850  auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
851  int64_t dynamicOffset = ShapedType::kDynamic;
852  SmallVector<int64_t> dynamicStrides(rankedTensorType.getRank(),
853  ShapedType::kDynamic);
854  auto stridedLayout = StridedLayoutAttr::get(tensorType.getContext(),
855  dynamicOffset, dynamicStrides);
856  return MemRefType::get(rankedTensorType.getShape(),
857  rankedTensorType.getElementType(), stridedLayout,
858  memorySpace);
859 }
860 
861 /// Return a MemRef type with a static identity layout (i.e., no layout map). If
862 /// the given tensor type is unranked, return an unranked MemRef type.
865  Attribute memorySpace) {
866  // Case 1: Unranked memref type.
867  if (auto unrankedTensorType =
868  llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
869  return UnrankedMemRefType::get(unrankedTensorType.getElementType(),
870  memorySpace);
871  }
872 
873  // Case 2: Ranked memref type.
874  auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
875  MemRefLayoutAttrInterface layout = {};
876  return MemRefType::get(rankedTensorType.getShape(),
877  rankedTensorType.getElementType(), layout,
878  memorySpace);
879 }
880 
881 //===----------------------------------------------------------------------===//
882 // Default implementations of interface methods
883 //===----------------------------------------------------------------------===//
884 
886  OpResult opResult, const AnalysisState &state) {
887  auto bufferizableOp = cast<BufferizableOpInterface>(opResult.getDefiningOp());
888  AliasingOpOperandList opOperands =
889  bufferizableOp.getAliasingOpOperands(opResult, state);
890 
891  // Case 1: OpResults that have no aliasing OpOperand usually bufferize to
892  // memory writes.
893  if (opOperands.getAliases().empty())
894  return true;
895 
896  // Case 2: If an aliasing OpOperand bufferizes to a memory write, the OpResult
897  // may bufferize to a memory write.
898  if (llvm::any_of(opOperands, [&](AliasingOpOperand alias) {
899  return state.bufferizesToMemoryWrite(*alias.opOperand);
900  }))
901  return true;
902 
903  // Case 3: Check if a nested aliasing OpOperand value bufferizes to a memory
904  // write. (Or: The reverse SSA use-def chain ends inside the reigon.) In that
905  // case, the OpResult bufferizes to a memory write. E.g.:
906  //
907  // %0 = "some_writing_op" : tensor<?xf32>
908  // %r = scf.if ... -> tensor<?xf32> {
909  // scf.yield %0 : tensor<?xf32>
910  // } else {
911  // %1 = "another_writing_op"(%0) : tensor<?xf32>
912  // scf.yield %1 : tensor<?xf32>
913  // }
914  // "some_reading_op"(%r)
915  //
916  // %r bufferizes to a memory write because an aliasing OpOperand value (%1)
917  // bufferizes to a memory write and the defining op is inside the scf.if.
918  //
919  // Note: This treatment of surrouding ops is useful for ops that have a
920  // region but no OpOperand such as scf.if or scf.execute_region. It simplifies
921  // the analysis considerably.
922  //
923  // "another_writing_op" in the above example should be able to bufferize
924  // inplace in the absence of another read of %0. However, if the scf.if op
925  // would not be considered a "write", the analysis would detect the
926  // following conflict:
927  //
928  // * read = some_reading_op
929  // * lastWrite = %0 (Note: The last write of %r would be a set: {%0, %1}.)
930  // * conflictingWrite = %1
931  //
932  auto isMemoryWriteInsideOp = [&](Value v) {
933  Operation *op = getOwnerOfValue(v);
934  if (!opResult.getDefiningOp()->isAncestor(op))
935  return false;
936  return state.bufferizesToMemoryWrite(v);
937  };
939  config.alwaysIncludeLeaves = false;
940  for (AliasingOpOperand alias : opOperands) {
941  if (!state
942  .findValueInReverseUseDefChain(alias.opOperand,
943  isMemoryWriteInsideOp, config)
944  .empty())
945  return true;
946  }
947  return false;
948 }
949 
950 // Compute the AliasingOpOperandList for a given Value based on
951 // getAliasingValues.
953  Value value, const AnalysisState &state) {
954  Operation *op = getOwnerOfValue(value);
956  for (OpOperand &opOperand : op->getOpOperands()) {
957  if (!llvm::isa<TensorType>(opOperand.get().getType()))
958  continue;
959  AliasingValueList aliasingValues = state.getAliasingValues(opOperand);
960  for (const auto &it : aliasingValues)
961  if (it.value == value)
962  result.emplace_back(&opOperand, it.relation, it.isDefinite);
963  }
964  return AliasingOpOperandList(std::move(result));
965 }
966 
968  Value value, const BufferizationOptions &options,
969  const BufferizationState &bufferizationState,
970  SmallVector<Value> &invocationStack) {
971  assert(llvm::isa<TensorType>(value.getType()) && "expected tensor type");
972  auto tensorType = cast<TensorType>(value.getType());
973 
974  // No further analysis is possible for a block argument.
975  if (llvm::isa<BlockArgument>(value)) {
976  return cast<BufferLikeType>(
978  }
979 
980  // Value is an OpResult.
981  Operation *op = getOwnerOfValue(value);
982  auto opResult = llvm::cast<OpResult>(value);
983  AnalysisState analysisState(options);
984  AliasingOpOperandList aliases = analysisState.getAliasingOpOperands(opResult);
985  if (aliases.getNumAliases() > 0 &&
986  aliases.getAliases()[0].relation == BufferRelation::Equivalent) {
987  // If the OpResult has an equivalent OpOperand, both OpResult and
988  // OpOperand bufferize to the exact same buffer type.
989  Value equivalentOperand = aliases.getAliases().front().opOperand->get();
990  return getBufferType(equivalentOperand, options, bufferizationState,
991  invocationStack);
992  }
993 
994  // If we do not know the memory space and there is no default memory space,
995  // report a failure.
996  auto memSpace =
997  options.defaultMemorySpaceFn(cast<TensorType>(value.getType()));
998  if (!memSpace.has_value())
999  return op->emitError("could not infer memory space");
1000 
1001  return cast<BufferLikeType>(
1002  getMemRefType(tensorType, options, /*layout=*/{}, *memSpace));
1003 }
1004 
1006  BufferizableOpInterface bufferizableOp, unsigned index) {
1007  assert(index < bufferizableOp->getNumRegions() && "invalid region index");
1008  auto regionInterface =
1009  dyn_cast<RegionBranchOpInterface>(bufferizableOp.getOperation());
1010  if (!regionInterface)
1011  return false;
1012  return regionInterface.isRepetitiveRegion(index);
1013 }
1014 
1017  // TODO: Take into account successor blocks.
1018  // No aliasing in case of non-entry blocks.
1019  if (auto bbArg = dyn_cast<BlockArgument>(value))
1020  if (bbArg.getOwner() != &bbArg.getOwner()->getParent()->getBlocks().front())
1021  return {};
1022 
1023  // Unknown op: Conservatively assume that each OpResult may alias with every
1024  // OpOperand. In addition, each block argument of an entry block may alias
1025  // with every OpOperand.
1027  for (OpOperand &operand : value.getDefiningOp()->getOpOperands())
1028  if (isa<TensorType>(operand.get().getType()))
1029  r.addAlias({&operand, BufferRelation::Unknown, /*isDefinite=*/false});
1030  return r;
1031 }
1032 
1035  // TODO: Take into account successor blocks.
1036  // Unknown op: Conservatively assume that each OpResult may alias with every
1037  // OpOperand. In addition, each block argument of an entry block may alias
1038  // with every OpOperand.
1040  for (OpResult result : opOperand.getOwner()->getOpResults())
1041  if (llvm::isa<TensorType>(result.getType()))
1042  r.addAlias({result, BufferRelation::Unknown, /*isDefinite=*/false});
1043  for (Region &region : opOperand.getOwner()->getRegions())
1044  if (!region.getBlocks().empty())
1045  for (BlockArgument bbArg : region.getBlocks().front().getArguments())
1046  if (isa<TensorType>(bbArg.getType()))
1047  r.addAlias({bbArg, BufferRelation::Unknown, /*isDefinite=*/false});
1048  return r;
1049 }
1050 
1052  auto isaTensor = [](Type t) { return isa<TensorLikeType>(t); };
1053  bool hasTensorBlockArgument = any_of(op->getRegions(), [&](Region &r) {
1054  return any_of(r.getBlocks(), [&](Block &b) {
1055  return any_of(b.getArguments(), [&](BlockArgument bbArg) {
1056  return isaTensor(bbArg.getType());
1057  });
1058  });
1059  });
1060  if (hasTensorBlockArgument)
1061  return true;
1062 
1063  if (any_of(op->getResultTypes(), isaTensor))
1064  return true;
1065  return any_of(op->getOperandTypes(), isaTensor);
1066 }
1067 
1068 FailureOr<BaseMemRefType>
1069 bufferization::detail::asMemRefType(FailureOr<BufferLikeType> bufferType) {
1070  if (failed(bufferType))
1071  return failure();
1072  return cast<BaseMemRefType>(*bufferType);
1073 }
1074 
1076  Value tensor,
1077  Value buffer) {
1078  return mlir::succeeded(
1079  cast<TensorLikeType>(tensor.getType())
1080  .verifyCompatibleBufferType(cast<BufferLikeType>(buffer.getType()),
1081  [&]() { return op.emitError(); }));
1082 }
static void setInsertionPointAfter(OpBuilder &b, Value value)
static bool isRepetitiveRegion(Region *region, const BufferizationOptions &options)
static void ensureToBufferOpIsValid(Value tensor, Type memrefType)
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static bool isaTensor(Type t)
static llvm::ManagedStatic< PassManagerOptions > options
#define MLIR_DEFINE_EXPLICIT_TYPE_ID(CLASS_NAME)
Definition: TypeID.h:323
Base class for generic analysis states.
Attributes are known-constant values of operations.
Definition: Attributes.h:25
This class provides a shared interface for ranked and unranked memref types.
Definition: BuiltinTypes.h:104
This class represents an argument of a Block.
Definition: Value.h:309
Block represents an ordered list of Operations.
Definition: Block.h:33
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:27
IntegerAttr getI64IntegerAttr(int64_t value)
Definition: Builders.cpp:112
IRValueT get() const
Return the current value being used by this operand.
Definition: UseDefLists.h:160
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:76
RAII guard to reset the insertion point of the builder when destroyed.
Definition: Builders.h:348
This class helps build Operations.
Definition: Builders.h:207
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:431
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:398
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition: Builders.h:412
This class represents an operand of an operation.
Definition: Value.h:257
This is a value defined by a result of an operation.
Definition: Value.h:447
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
Dialect * getDialect()
Return the dialect this operation is associated with, or nullptr if the associated dialect is not loa...
Definition: Operation.h:220
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition: Operation.h:234
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:268
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:213
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:677
operand_type_range getOperandTypes()
Definition: Operation.h:397
MutableArrayRef< OpOperand > getOpOperands()
Definition: Operation.h:383
result_type_range getResultTypes()
Definition: Operation.h:428
bool isAncestor(Operation *other)
Return true if this operation is an ancestor of the other operation.
Definition: Operation.h:263
result_range getOpResults()
Definition: Operation.h:420
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition: Operation.h:230
unsigned getNumResults()
Return the number of results held by this operation.
Definition: Operation.h:404
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Definition: Region.cpp:45
unsigned getRegionNumber()
Return the number of this region in the parent operation.
Definition: Region.cpp:62
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition: Region.h:200
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
Definition: PatternMatch.h:368
virtual void replaceOp(Operation *op, ValueRange newValues)
Replace the results of the given (original) operation with the specified list of values (replacements...
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
Definition: PatternMatch.h:638
This class represents a collection of SymbolTables.
Definition: SymbolTable.h:283
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
Definition: BuiltinTypes.h:55
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:107
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition: Types.cpp:35
This class provides an abstraction over the different types of ranges over Values.
Definition: ValueRange.h:387
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
Type getType() const
Return the type of this value.
Definition: Value.h:105
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition: Value.h:188
Location getLoc() const
Return the location of this value.
Definition: Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:18
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition: Value.cpp:39
AnalysisState provides a variety of helper functions for dealing with tensor values.
bool isValueRead(Value value) const
Return true if the given value is read by an op that bufferizes to a memory read.
AliasingValueList getAliasingValues(OpOperand &opOperand) const
Determine which Value will alias with opOperand if the op is bufferized in place.
SetVector< Value > findValueInReverseUseDefChain(OpOperand *opOperand, llvm::function_ref< bool(Value)> condition, TraversalConfig config=TraversalConfig(), llvm::DenseSet< OpOperand * > *visitedOpOperands=nullptr) const
Starting from opOperand, follow the use-def chain in reverse, always selecting the aliasing OpOperand...
virtual bool areAliasingBufferizedValues(Value v1, Value v2) const
Return true if v1 and v2 may bufferize to aliasing buffers.
virtual bool hasUndefinedContents(OpOperand *opOperand) const
Return true if the given tensor has undefined contents.
bool insideMutuallyExclusiveRegions(Operation *op0, Operation *op1)
Checks whether op0 and op1 are inside mutually exclusive regions.
bool canOmitTensorCopy(OpOperand &opOperand) const
Return true if a copy can always be avoided when allocating a new tensor for the given OpOperand.
bool bufferizesToMemoryWrite(OpOperand &opOperand) const
Return true if opOperand bufferizes to a memory write.
virtual bool isInPlace(OpOperand &opOperand) const
Return true if the given OpResult has been decided to bufferize inplace.
SetVector< Value > findDefinitions(OpOperand *opOperand) const
Find the values that may define the contents of the given value at runtime.
bool bufferizesToAliasOnly(OpOperand &opOperand) const
Return true if opOperand does neither read nor write but bufferizes to an alias.
AliasingOpOperandList getAliasingOpOperands(Value value) const
Determine which OpOperand* will alias with value if the op is bufferized in place.
AnalysisState(const BufferizationOptions &options)
Region * getEnclosingRepetitiveRegion(Operation *op, const BufferizationOptions &options)
Return the closest enclosing repetitive region around the given op.
const BufferizationOptions & getOptions() const
Return a reference to the BufferizationOptions.
bool bufferizesToMemoryRead(OpOperand &opOperand) const
Return true if opOperand bufferizes to a memory read.
virtual bool areEquivalentBufferizedValues(Value v1, Value v2) const
Return true if v1 and v2 bufferize to equivalent buffers.
BufferizationState provides information about the state of the IR during the bufferization process.
SymbolTableCollection & getSymbolTables()
Get a reference to the collection of cached symbol tables.
bool isOpAllowed(Operation *op) const
Return whether the op is allowed or not.
Operation * getOwner() const
Return the owner of this operand.
Definition: UseDefLists.h:38
AliasingOpOperandList defaultGetAliasingOpOperands(Value value, const AnalysisState &state)
This is the default implementation of BufferizableOpInterface::getAliasingOpOperands.
bool defaultResultBufferizesToMemoryWrite(OpResult opResult, const AnalysisState &state)
This is the default implementation of BufferizableOpInterface::resultBufferizesToMemoryWrite.
FailureOr< BufferLikeType > defaultGetBufferType(Value value, const BufferizationOptions &options, const BufferizationState &state, SmallVector< Value > &invocationStack)
This is the default implementation of BufferizableOpInterface::getBufferType.
AliasingValueList unknownGetAliasingValues(OpOperand &opOperand)
This is the default implementation of getAliasingValues in case the owner op does not implement the B...
bool defaultIsRepetitiveRegion(BufferizableOpInterface bufferizableOp, unsigned index)
This is the default implementation of BufferizableOpInterface::isRepetitiveRegion.
FailureOr< BaseMemRefType > asMemRefType(FailureOr< BufferLikeType > bufferType)
This is a helper function used when buffer type is guaranteed to be memref.
AliasingOpOperandList unknownGetAliasingOpOperands(Value value)
This is the default implementation of getAliasingOpOperands in case the defining op does not implemen...
bool defaultHasTensorSemantics(Operation *op)
This is the default implementation of BufferizableOpInterface::hasTensorSemantics.
bool typesMatchAfterBufferization(Operation &op, Value tensor, Value buffer)
This function is a free-standing helper that relies on bufferization::TensorLikeTypeInterface to veri...
BaseMemRefType getMemRefType(TensorType tensorType, const BufferizationOptions &options, MemRefLayoutAttrInterface layout={}, Attribute memorySpace=nullptr)
Return a MemRefType to which the TensorType can be bufferized.
void replaceOpWithBufferizedValues(RewriterBase &rewriter, Operation *op, ValueRange values)
Replace an op with replacement values.
BaseMemRefType getMemRefTypeWithStaticIdentityLayout(TensorType tensorType, Attribute memorySpace=nullptr)
Return a MemRef type with a static identity layout (i.e., no layout map).
Operation * getOwnerOfValue(Value value)
Return the owner of the given value.
Region * getParallelRegion(Region *region, const BufferizationOptions &options)
If region is a parallel region, return region.
FailureOr< Value > getBuffer(RewriterBase &rewriter, Value value, const BufferizationOptions &options, const BufferizationState &state)
Lookup the buffer for the given value.
Region * getNextEnclosingRepetitiveRegion(Region *region, const BufferizationOptions &options)
Assuming that the given region is repetitive, find the next enclosing repetitive region.
FailureOr< BufferLikeType > getBufferType(Value value, const BufferizationOptions &options, const BufferizationState &state)
Return the buffer type for a given Value (tensor) after bufferization without bufferizing any IR.
AliasList< AliasingOpOperand > AliasingOpOperandList
A list of possible aliasing OpOperands.
FailureOr< Value > allocateTensorForShapedValue(OpBuilder &b, Location loc, Value shapedValue, const BufferizationOptions &options, const BufferizationState &state, bool copy=true)
Create an AllocTensorOp for the given shaped value (memref or tensor).
BaseMemRefType getMemRefTypeWithFullyDynamicLayout(TensorType tensorType, Attribute memorySpace=nullptr)
Return a MemRef type with fully dynamic layout.
void populateDynamicDimSizes(OpBuilder &b, Location loc, Value shapedValue, SmallVector< Value > &dynamicDims)
Populate dynamicDims with tensor::DimOp / memref::DimOp results for all dynamic dimensions of the giv...
bool hasTensorSemantics(Operation *op)
Return "true" if the given op has tensor semantics and should be bufferized.
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Definition: Matchers.h:344
Type getTensorTypeFromMemRefType(Type type)
Return an unranked/ranked tensor type for the given unranked/ranked memref type.
Definition: MemRefOps.cpp:60
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
Include the generated interface declarations.
LogicalResult reifyResultShapes(OpBuilder &b, Operation *op, ReifiedRankedShapedTypeDims &reifiedReturnShapes)
Reify the shape of the result of an operation (typically in terms of the shape of its operands).
const FrozenRewritePatternSet GreedyRewriteConfig config
bool insideMutuallyExclusiveRegions(Operation *a, Operation *b)
Return true if a and b are in mutually exclusive regions as per RegionBranchOpInterface.
Value getValueOrCreateConstantIndexOp(OpBuilder &b, Location loc, OpFoldResult ofr)
Converts an OpFoldResult to a Value.
Definition: Utils.cpp:111
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
Options for BufferizableOpInterface-based bufferization.
std::function< void(AnalysisState &)> AnalysisStateInitFn
Initializer function for analysis state.
void setFunctionBoundaryTypeConversion(LayoutMapOption layoutMapOption)
This function controls buffer types on function signatures.
BufferizableOpInterface dynCastBufferizableOp(Operation *op) const
Try to cast the given op to BufferizableOpInterface if the op is allow listed.
bool inferFunctionResultLayout
If true, function result types are inferred from the body of the function.
unsigned int bufferAlignment
Buffer alignment for new memory allocations.
FunctionArgTypeConverterFn functionArgTypeConverterFn
Type conversion from tensors to buffers.
std::optional< AllocationFn > allocationFn
Helper functions for allocation and memory copying.
OpFilter opFilter
A filter that specifies which ops should be bufferized and which ops should be ignored.
bool isOpAllowed(Operation *op) const
Return true if the given op should be bufferized.
bool bufferizeFunctionBoundaries
Specifies whether function boundaries (ops in the func dialect) should be bufferized or not.
FailureOr< Value > createAlloc(OpBuilder &b, Location loc, MemRefType type, ValueRange dynShape) const
Create a memref allocation with the given type and dynamic extents.
LogicalResult createMemCpy(OpBuilder &b, Location loc, Value from, Value to) const
Creates a memcpy between two given buffers.
SmallVector< AnalysisStateInitFn > stateInitializers
Initializer functions for analysis state.
Traversal parameters for findValueInReverseUseDefChain.