MLIR  20.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 
14 #include "mlir/IR/AsmState.h"
15 #include "mlir/IR/BuiltinOps.h"
16 #include "mlir/IR/IRMapping.h"
17 #include "mlir/IR/Operation.h"
18 #include "mlir/IR/TypeUtilities.h"
19 #include "mlir/IR/Value.h"
22 #include "llvm/ADT/ScopeExit.h"
23 #include "llvm/Support/Debug.h"
24 
25 //===----------------------------------------------------------------------===//
26 // BufferizableOpInterface
27 //===----------------------------------------------------------------------===//
28 
29 namespace mlir {
30 namespace bufferization {
31 
32 #include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.cpp.inc"
33 
34 } // namespace bufferization
35 } // namespace mlir
36 
38 
39 #define DEBUG_TYPE "bufferizable-op-interface"
40 #define DBGS() (llvm::dbgs() << '[' << DEBUG_TYPE << "] ")
41 #define LDBG(X) LLVM_DEBUG(DBGS() << (X))
42 
43 using namespace mlir;
44 using namespace bufferization;
45 
46 static bool isRepetitiveRegion(Region *region,
48  Operation *op = region->getParentOp();
49  if (auto bufferizableOp = options.dynCastBufferizableOp(op))
50  if (bufferizableOp.isRepetitiveRegion(region->getRegionNumber()))
51  return true;
52  return false;
53 }
54 
57  if (!op->getBlock())
58  return nullptr;
59  if (auto iter = enclosingRepetitiveRegionCache.find_as(op);
60  iter != enclosingRepetitiveRegionCache.end())
61  return iter->second;
62  return enclosingRepetitiveRegionCache[op] =
63  getEnclosingRepetitiveRegion(op->getBlock(), options);
64 }
65 
67  Value value, const BufferizationOptions &options) {
68  if (auto iter = enclosingRepetitiveRegionCache.find_as(value);
69  iter != enclosingRepetitiveRegionCache.end())
70  return iter->second;
71 
72  Region *region = value.getParentRegion();
73  // Collect all visited regions since we only know the repetitive region we
74  // want to map it to later on
75  SmallVector<Region *> visitedRegions;
76  while (region) {
77  visitedRegions.push_back(region);
78  if (isRepetitiveRegion(region, options))
79  break;
80  region = region->getParentRegion();
81  }
82  enclosingRepetitiveRegionCache[value] = region;
83  for (Region *r : visitedRegions)
84  enclosingRepetitiveRegionCache[r] = region;
85  return region;
86 }
87 
89  Block *block, const BufferizationOptions &options) {
90  if (auto iter = enclosingRepetitiveRegionCache.find_as(block);
91  iter != enclosingRepetitiveRegionCache.end())
92  return iter->second;
93 
94  Region *region = block->getParent();
95  Operation *op = nullptr;
96  // Collect all visited regions since we only know the repetitive region we
97  // want to map it to later on
98  SmallVector<Region *> visitedRegions;
99  do {
100  op = region->getParentOp();
101  if (isRepetitiveRegion(region, options))
102  break;
103  } while ((region = op->getParentRegion()));
104 
105  enclosingRepetitiveRegionCache[block] = region;
106  for (Region *r : visitedRegions)
107  enclosingRepetitiveRegionCache[r] = region;
108  return region;
109 }
110 
111 void AnalysisState::resetCache() { enclosingRepetitiveRegionCache.clear(); }
112 
114  Region *region, const BufferizationOptions &options) {
115  assert(isRepetitiveRegion(region, options) && "expected repetitive region");
116  while ((region = region->getParentRegion())) {
117  if (isRepetitiveRegion(region, options))
118  break;
119  }
120  return region;
121 }
122 
124  const BufferizationOptions &options) {
125  while (region) {
126  auto bufferizableOp = options.dynCastBufferizableOp(region->getParentOp());
127  if (bufferizableOp &&
128  bufferizableOp.isParallelRegion(region->getRegionNumber())) {
129  assert(isRepetitiveRegion(region, options) &&
130  "expected that all parallel regions are also repetitive regions");
131  return region;
132  }
133  region = region->getParentRegion();
134  }
135  return nullptr;
136 }
137 
139  if (auto opResult = llvm::dyn_cast<OpResult>(value))
140  return opResult.getDefiningOp();
141  return llvm::cast<BlockArgument>(value).getOwner()->getParentOp();
142 }
143 
144 /// Create an AllocTensorOp for the given shaped value. If `copy` is set, the
145 /// shaped value is copied. Otherwise, a tensor with undefined contents is
146 /// allocated.
148  OpBuilder &b, Location loc, Value shapedValue,
149  const BufferizationOptions &options, bool copy) {
150  Value tensor;
151  if (llvm::isa<RankedTensorType>(shapedValue.getType())) {
152  tensor = shapedValue;
153  } else if (llvm::isa<MemRefType>(shapedValue.getType())) {
154  tensor = b.create<ToTensorOp>(loc, shapedValue);
155  } else if (llvm::isa<UnrankedTensorType>(shapedValue.getType()) ||
156  llvm::isa<UnrankedMemRefType>(shapedValue.getType())) {
157  return getOwnerOfValue(shapedValue)
158  ->emitError("copying of unranked tensors is not implemented");
159  } else {
160  llvm_unreachable("expected RankedTensorType or MemRefType");
161  }
162  RankedTensorType tensorType = llvm::cast<RankedTensorType>(tensor.getType());
163  SmallVector<Value> dynamicSizes;
164  if (!copy) {
165  // Compute the dynamic part of the shape.
166  // First try to query the shape via ReifyRankedShapedTypeOpInterface.
167  bool reifiedShapes = false;
168  if (llvm::isa<RankedTensorType>(shapedValue.getType()) &&
169  llvm::isa<OpResult>(shapedValue)) {
170  ReifiedRankedShapedTypeDims resultDims;
171  if (succeeded(
172  reifyResultShapes(b, shapedValue.getDefiningOp(), resultDims))) {
173  reifiedShapes = true;
174  auto &shape =
175  resultDims[llvm::cast<OpResult>(shapedValue).getResultNumber()];
176  for (const auto &dim : enumerate(tensorType.getShape()))
177  if (ShapedType::isDynamic(dim.value()))
178  dynamicSizes.push_back(shape[dim.index()].get<Value>());
179  }
180  }
181 
182  // If the shape could not be reified, create DimOps.
183  if (!reifiedShapes)
184  populateDynamicDimSizes(b, loc, tensor, dynamicSizes);
185  }
186 
187  // Create AllocTensorOp.
188  auto allocTensorOp = b.create<AllocTensorOp>(loc, tensorType, dynamicSizes,
189  copy ? tensor : Value());
190 
191  // Add 'memory_space' attribute. Not needed if 'copy' operand is specified.
192  if (copy)
193  return allocTensorOp.getResult();
194  FailureOr<BaseMemRefType> copyBufferType = getBufferType(tensor, options);
195  if (failed(copyBufferType))
196  return failure();
197  std::optional<Attribute> memorySpace = copyBufferType->getMemorySpace();
198  if (!memorySpace)
199  memorySpace = options.defaultMemorySpaceFn(tensorType);
200  if (memorySpace.has_value())
201  allocTensorOp.setMemorySpaceAttr(memorySpace.value());
202  return allocTensorOp.getResult();
203 }
204 
205 LogicalResult BufferizableOpInterface::resolveTensorOpOperandConflicts(
206  RewriterBase &rewriter, const AnalysisState &state) {
207  OpBuilder::InsertionGuard g(rewriter);
208  Operation *op = getOperation();
209  SmallVector<OpOperand *> outOfPlaceOpOperands;
210  DenseSet<OpOperand *> copiedOpOperands;
211  SmallVector<Value> outOfPlaceValues;
212  DenseSet<Value> copiedOpValues;
213 
214  // Find all out-of-place OpOperands.
215  for (OpOperand &opOperand : op->getOpOperands()) {
216  Type operandType = opOperand.get().getType();
217  if (!llvm::isa<TensorType>(operandType))
218  continue;
219  if (state.isInPlace(opOperand))
220  continue;
221  if (llvm::isa<UnrankedTensorType>(operandType))
222  return op->emitError("copying of unranked tensors is not implemented");
223 
224  AliasingValueList aliasingValues = state.getAliasingValues(opOperand);
225  if (aliasingValues.getNumAliases() == 1 &&
226  isa<OpResult>(aliasingValues.getAliases()[0].value) &&
227  !state.bufferizesToMemoryWrite(opOperand) &&
228  state.getAliasingOpOperands(aliasingValues.getAliases()[0].value)
229  .getNumAliases() == 1 &&
230  !isa<UnrankedTensorType>(
231  aliasingValues.getAliases()[0].value.getType())) {
232  // The op itself does not write but may create exactly one alias. Instead
233  // of copying the OpOperand, copy the OpResult. The OpResult can sometimes
234  // be smaller than the OpOperand (e.g., in the case of an extract_slice,
235  // where the result is usually a smaller part of the source). Do not apply
236  // this optimization if the OpResult is an unranked tensor (because those
237  // cannot be copied at the moment).
238  Value value = aliasingValues.getAliases()[0].value;
239  outOfPlaceValues.push_back(value);
240  if (!state.canOmitTensorCopy(opOperand))
241  copiedOpValues.insert(value);
242  } else {
243  // In all other cases, make a copy of the OpOperand.
244  outOfPlaceOpOperands.push_back(&opOperand);
245  if (!state.canOmitTensorCopy(opOperand))
246  copiedOpOperands.insert(&opOperand);
247  }
248  }
249 
250  // Insert copies of OpOperands.
251  rewriter.setInsertionPoint(op);
252  for (OpOperand *opOperand : outOfPlaceOpOperands) {
253  FailureOr<Value> copy = allocateTensorForShapedValue(
254  rewriter, op->getLoc(), opOperand->get(), state.getOptions(),
255  copiedOpOperands.contains(opOperand));
256  if (failed(copy))
257  return failure();
258  rewriter.modifyOpInPlace(op, [&]() { opOperand->set(*copy); });
259  }
260 
261  // Insert copies of Values.
262  rewriter.setInsertionPointAfter(op);
263  for (Value value : outOfPlaceValues) {
264  FailureOr<Value> copy = allocateTensorForShapedValue(
265  rewriter, op->getLoc(), value, state.getOptions(),
266  copiedOpValues.count(value));
267  if (failed(copy))
268  return failure();
269  SmallVector<OpOperand *> uses = llvm::to_vector(
270  llvm::map_range(value.getUses(), [](OpOperand &use) { return &use; }));
271  for (OpOperand *use : uses) {
272  // Do not update the alloc_tensor op that we just created.
273  if (use->getOwner() == copy->getDefiningOp())
274  continue;
275  // tensor.dim ops may have been created to be used as alloc_tensor op
276  // dynamic extents. Do not update these either.
277  if (isa<tensor::DimOp>(use->getOwner()))
278  continue;
279  rewriter.modifyOpInPlace(use->getOwner(), [&]() { use->set(*copy); });
280  }
281  }
282 
283  return success();
284 }
285 
286 //===----------------------------------------------------------------------===//
287 // OpFilter
288 //===----------------------------------------------------------------------===//
289 
291  // All other ops: Allow/disallow according to filter.
292  bool isAllowed = !hasAllowRule();
293  for (const Entry &entry : entries) {
294  bool filterResult = entry.fn(op);
295  switch (entry.type) {
296  case Entry::ALLOW:
297  isAllowed |= filterResult;
298  break;
299  case Entry::DENY:
300  if (filterResult)
301  // DENY filter matches. This op is no allowed. (Even if other ALLOW
302  // filters may match.)
303  return false;
304  };
305  }
306  return isAllowed;
307 }
308 
309 //===----------------------------------------------------------------------===//
310 // BufferizationOptions
311 //===----------------------------------------------------------------------===//
312 
313 namespace {
314 
315 /// Default function arg type converter: Use a fully dynamic layout map.
317 defaultFunctionArgTypeConverter(TensorType type, Attribute memorySpace,
318  FunctionOpInterface funcOp,
319  const BufferizationOptions &options) {
320  return getMemRefTypeWithFullyDynamicLayout(type, memorySpace);
321 }
322 /// Default unknown type converter: Use a fully dynamic layout map.
324 defaultUnknownTypeConverter(Value value, Attribute memorySpace,
325  const BufferizationOptions &options) {
327  llvm::cast<TensorType>(value.getType()), memorySpace);
328 }
329 
330 } // namespace
331 
332 // Default constructor for BufferizationOptions.
334  : functionArgTypeConverterFn(defaultFunctionArgTypeConverter),
335  unknownTypeConverterFn(defaultUnknownTypeConverter) {}
336 
338  // Special case: If function boundary bufferization is deactivated, do not
339  // allow ops that belong to the `func` dialect.
340  bool isFuncBoundaryOp = isa_and_nonnull<func::FuncDialect>(op->getDialect());
341  if (!bufferizeFunctionBoundaries && isFuncBoundaryOp)
342  return false;
343 
344  return opFilter.isOpAllowed(op);
345 }
346 
347 BufferizableOpInterface
349  if (!isOpAllowed(op))
350  return nullptr;
351  auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op);
352  if (!bufferizableOp)
353  return nullptr;
354  return bufferizableOp;
355 }
356 
357 BufferizableOpInterface
359  return dynCastBufferizableOp(getOwnerOfValue(value));
360 }
361 
363  LayoutMapOption layoutMapOption) {
364  functionArgTypeConverterFn = [=](TensorType tensorType, Attribute memorySpace,
365  FunctionOpInterface funcOp,
366  const BufferizationOptions &options) {
367  if (layoutMapOption == LayoutMapOption::IdentityLayoutMap)
369  memorySpace);
371  memorySpace);
372  };
374  layoutMapOption == LayoutMapOption::InferLayoutMap;
375 }
376 
377 //===----------------------------------------------------------------------===//
378 // Helper functions for BufferizableOpInterface
379 //===----------------------------------------------------------------------===//
380 
381 static void setInsertionPointAfter(OpBuilder &b, Value value) {
382  if (auto bbArg = llvm::dyn_cast<BlockArgument>(value)) {
383  b.setInsertionPointToStart(bbArg.getOwner());
384  } else {
386  }
387 }
388 
389 /// Determine which OpOperand* will alias with `value` if the op is bufferized
390 /// in place. Return all tensor OpOperand* if the op is not bufferizable.
392  if (Operation *op = getOwnerOfValue(value))
393  if (auto bufferizableOp = getOptions().dynCastBufferizableOp(op))
394  return bufferizableOp.getAliasingOpOperands(value, *this);
395 
396  // The op is not bufferizable.
398 }
399 
400 /// Determine which Values will alias with `opOperand` if the op is bufferized
401 /// in place. Return all tensor Values if the op is not bufferizable.
403  if (auto bufferizableOp =
404  getOptions().dynCastBufferizableOp(opOperand.getOwner()))
405  return bufferizableOp.getAliasingValues(opOperand, *this);
406 
407  // The op is not bufferizable.
408  return detail::unknownGetAliasingValues(opOperand);
409 }
410 
411 /// Return true if `opOperand` bufferizes to a memory read. Return `true` if the
412 /// op is not bufferizable.
414  if (auto bufferizableOp =
415  getOptions().dynCastBufferizableOp(opOperand.getOwner()))
416  return bufferizableOp.bufferizesToMemoryRead(opOperand, *this);
417 
418  // Unknown op that returns a tensor. The inplace analysis does not support it.
419  // Conservatively return true.
420  return true;
421 }
422 
423 /// Return true if `opOperand` bufferizes to a memory write. Return
424 /// `true` if the op is not bufferizable.
426  if (auto bufferizableOp =
427  getOptions().dynCastBufferizableOp(opOperand.getOwner()))
428  return bufferizableOp.bufferizesToMemoryWrite(opOperand, *this);
429 
430  // Unknown op that returns a tensor. The inplace analysis does not support it.
431  // Conservatively return true.
432  return true;
433 }
434 
435 /// Return true if `opOperand` does neither read nor write but bufferizes to an
436 /// alias. Return false if the op is not bufferizable.
438  if (auto bufferizableOp =
439  getOptions().dynCastBufferizableOp(opOperand.getOwner()))
440  return bufferizableOp.bufferizesToAliasOnly(opOperand, *this);
441 
442  // Unknown op that returns a tensor. The inplace analysis does not support it.
443  // Conservatively return false.
444  return false;
445 }
446 
448  auto opResult = llvm::dyn_cast<OpResult>(value);
449  if (!opResult)
450  return true;
451  auto bufferizableOp = getOptions().dynCastBufferizableOp(value);
452  if (!bufferizableOp)
453  return true;
454  return bufferizableOp.resultBufferizesToMemoryWrite(opResult, *this);
455 }
456 
457 /// Return true if the given value is read by an op that bufferizes to a memory
458 /// read. Also takes into account ops that create an alias but do not read by
459 /// themselves (e.g., ExtractSliceOp).
461  assert(llvm::isa<TensorType>(value.getType()) && "expected TensorType");
462  SmallVector<OpOperand *> workingSet;
463  DenseSet<OpOperand *> visited;
464  for (OpOperand &use : value.getUses())
465  workingSet.push_back(&use);
466 
467  while (!workingSet.empty()) {
468  OpOperand *uMaybeReading = workingSet.pop_back_val();
469  if (!visited.insert(uMaybeReading).second)
470  continue;
471 
472  // Skip over all ops that neither read nor write (but create an alias).
473  if (bufferizesToAliasOnly(*uMaybeReading))
474  for (AliasingValue alias : getAliasingValues(*uMaybeReading))
475  for (OpOperand &use : alias.value.getUses())
476  workingSet.push_back(&use);
477  if (bufferizesToMemoryRead(*uMaybeReading))
478  return true;
479  }
480 
481  return false;
482 }
483 
484 // Starting from `value`, follow the use-def chain in reverse, always selecting
485 // the aliasing OpOperands. Find and return Values for which `condition`
486 // evaluates to true. OpOperands of such matching Values are not traversed any
487 // further.
489  Value value, llvm::function_ref<bool(Value)> condition,
490  TraversalConfig config) const {
491  llvm::DenseSet<Value> visited;
492  llvm::SetVector<Value> result, workingSet;
493  workingSet.insert(value);
494 
495  while (!workingSet.empty()) {
496  Value value = workingSet.pop_back_val();
497 
498  if (!config.revisitAlreadyVisitedValues && visited.contains(value)) {
499  // Stop traversal if value was already visited.
500  if (config.alwaysIncludeLeaves)
501  result.insert(value);
502  continue;
503  }
504  visited.insert(value);
505 
506  if (condition(value)) {
507  result.insert(value);
508  continue;
509  }
510 
511  if (!config.followUnknownOps && !options.dynCastBufferizableOp(value)) {
512  // Stop iterating if `followUnknownOps` is unset and the op is either
513  // not bufferizable or excluded in the OpFilter.
514  if (config.alwaysIncludeLeaves)
515  result.insert(value);
516  continue;
517  }
518 
520  if (aliases.getNumAliases() == 0) {
521  // The traversal ends naturally if there are no more OpOperands that
522  // could be followed.
523  if (config.alwaysIncludeLeaves)
524  result.insert(value);
525  continue;
526  }
527 
528  for (AliasingOpOperand a : aliases) {
529  if (config.followEquivalentOnly &&
530  a.relation != BufferRelation::Equivalent) {
531  // Stop iterating if `followEquivalentOnly` is set but the alias is not
532  // equivalent.
533  if (config.alwaysIncludeLeaves)
534  result.insert(value);
535  continue;
536  }
537 
538  if (config.followInPlaceOnly && !isInPlace(*a.opOperand)) {
539  // Stop iterating if `followInPlaceOnly` is set but the alias is
540  // out-of-place.
541  if (config.alwaysIncludeLeaves)
542  result.insert(value);
543  continue;
544  }
545 
546  if (config.followSameTypeOrCastsOnly &&
547  a.opOperand->get().getType() != value.getType() &&
548  !value.getDefiningOp<CastOpInterface>()) {
549  // Stop iterating if `followSameTypeOrCastsOnly` is set but the alias is
550  // has a different type and the op is not a cast.
551  if (config.alwaysIncludeLeaves)
552  result.insert(value);
553  continue;
554  }
555 
556  workingSet.insert(a.opOperand->get());
557  }
558  }
559 
560  return result;
561 }
562 
563 // Find the values that define the contents of the given value.
565  TraversalConfig config;
566  config.alwaysIncludeLeaves = false;
568  value, [&](Value v) { return this->bufferizesToMemoryWrite(v); }, config);
569 }
570 
573 
575  : options(options), type(type) {
577  options.stateInitializers)
578  fn(*this);
579 }
580 
582  // Do not copy if the tensor has undefined contents.
583  if (hasUndefinedContents(&opOperand))
584  return true;
585 
586  // Do not copy if the buffer of the tensor is entirely overwritten (with
587  // values that do not depend on the old tensor).
588  if (bufferizesToMemoryWrite(opOperand) && !bufferizesToMemoryRead(opOperand))
589  return true;
590 
591  // Do not copy if the tensor is never read.
592  AliasingValueList aliases = getAliasingValues(opOperand);
593  if (!bufferizesToMemoryRead(opOperand) &&
594  llvm::none_of(aliases,
595  [&](AliasingValue a) { return isValueRead(a.value); }))
596  return true;
597 
598  // Default: Cannot omit the copy.
599  return false;
600 }
601 
602 bool AnalysisState::isInPlace(OpOperand &opOperand) const {
603  // ToMemrefOps are always in-place.
604  if (isa<ToMemrefOp>(opOperand.getOwner()))
605  return true;
606 
607  // In the absence of analysis information, OpOperands that bufferize to a
608  // memory write are out-of-place, i.e., an alloc and copy is inserted.
609  return !bufferizesToMemoryWrite(opOperand);
610 }
611 
613  // In the absence of analysis information, we do not know if the values are
614  // equivalent. The conservative answer is "false".
615  return false;
616 }
617 
619  // In the absence of analysis information, we do not know if the values may be
620  // aliasing. The conservative answer is "true".
621  return true;
622 }
623 
625  // In the absence of analysis information, the conservative answer is "false".
626  return false;
627 }
628 
629 // bufferization.to_memref is not allowed to change the rank.
630 static void ensureToMemrefOpIsValid(Value tensor, Type memrefType) {
631 #ifndef NDEBUG
632  auto rankedTensorType = llvm::dyn_cast<RankedTensorType>(tensor.getType());
633  assert((!rankedTensorType || llvm::cast<MemRefType>(memrefType).getRank() ==
634  rankedTensorType.getRank()) &&
635  "to_memref would be invalid: mismatching ranks");
636 #endif
637 }
638 
639 FailureOr<Value> bufferization::getBuffer(RewriterBase &rewriter, Value value,
640  const BufferizationOptions &options) {
641 #ifndef NDEBUG
642  auto tensorType = llvm::dyn_cast<TensorType>(value.getType());
643  assert(tensorType && "unexpected non-tensor type");
644 #endif // NDEBUG
645 
646  // Replace "%t = to_tensor %m" with %m.
647  if (auto toTensorOp = value.getDefiningOp<bufferization::ToTensorOp>())
648  return toTensorOp.getMemref();
649 
650  // Insert to_memref op.
651  OpBuilder::InsertionGuard g(rewriter);
652  setInsertionPointAfter(rewriter, value);
653  FailureOr<BaseMemRefType> memrefType = getBufferType(value, options);
654  if (failed(memrefType))
655  return failure();
656  ensureToMemrefOpIsValid(value, *memrefType);
657  return rewriter
658  .create<bufferization::ToMemrefOp>(value.getLoc(), *memrefType, value)
659  .getResult();
660 }
661 
662 /// Return the buffer type for a given Value (tensor) after bufferization.
663 FailureOr<BaseMemRefType>
665  SmallVector<Value> invocationStack;
666  return getBufferType(value, options, invocationStack);
667 }
668 
669 /// Return the buffer type for a given Value (tensor) after bufferization.
670 FailureOr<BaseMemRefType>
672  SmallVector<Value> &invocationStack) {
673  assert(llvm::isa<TensorType>(value.getType()) &&
674  "unexpected non-tensor type");
675  invocationStack.push_back(value);
676  auto popFromStack =
677  llvm::make_scope_exit([&]() { invocationStack.pop_back(); });
678 
679  // Try querying BufferizableOpInterface.
680  Operation *op = getOwnerOfValue(value);
681  auto bufferizableOp = options.dynCastBufferizableOp(op);
682  if (bufferizableOp)
683  return bufferizableOp.getBufferType(value, options, invocationStack);
684 
685  // Op is not bufferizable.
686  auto memSpace =
687  options.defaultMemorySpaceFn(cast<TensorType>(value.getType()));
688  if (!memSpace.has_value())
689  return op->emitError("could not infer memory space");
690 
691  return getMemRefType(value, options, /*layout=*/{}, *memSpace);
692 }
693 
695  if (auto bufferizableOp = dyn_cast<BufferizableOpInterface>(op))
696  return bufferizableOp.hasTensorSemantics();
698 }
699 
701  Operation *op,
702  ValueRange values) {
703  assert(values.size() == op->getNumResults() &&
704  "expected one value per OpResult");
705  OpBuilder::InsertionGuard g(rewriter);
706 
707  // Replace all OpResults with the given values.
708  SmallVector<Value> replacements;
709  for (OpResult opResult : op->getOpResults()) {
710  Value replacement = values[opResult.getResultNumber()];
711  if (llvm::isa<TensorType>(opResult.getType())) {
712  // The OpResult is a tensor. Such values are replaced with memrefs during
713  // bufferization.
714  assert((llvm::isa<MemRefType>(replacement.getType()) ||
715  llvm::isa<UnrankedMemRefType>(replacement.getType())) &&
716  "tensor op result should be replaced with a memref value");
717  // The existing uses of the OpResult still expect a tensor. Insert a
718  // ToTensorOp. Throughout bufferization, this ToTensorOp will gradually
719  // loose all of its users and eventually DCE away.
720  rewriter.setInsertionPointAfter(op);
721  replacement = rewriter.create<bufferization::ToTensorOp>(
722  replacement.getLoc(), replacement);
723  }
724  replacements.push_back(replacement);
725  }
726 
727  rewriter.replaceOp(op, replacements);
728 }
729 
730 //===----------------------------------------------------------------------===//
731 // Bufferization-specific scoped alloc insertion support.
732 //===----------------------------------------------------------------------===//
733 
734 /// Create a memref allocation with the given type and dynamic extents.
736  MemRefType type,
737  ValueRange dynShape) const {
738  if (allocationFn)
739  return (*allocationFn)(b, loc, type, dynShape, bufferAlignment);
740 
741  // Default bufferallocation via AllocOp.
742  if (bufferAlignment != 0)
743  return b
744  .create<memref::AllocOp>(loc, type, dynShape,
746  .getResult();
747  return b.create<memref::AllocOp>(loc, type, dynShape).getResult();
748 }
749 
750 /// Create a memory copy between two memref buffers.
752  Value from, Value to) const {
753  if (memCpyFn)
754  return (*memCpyFn)(b, loc, from, to);
755 
756  b.create<memref::CopyOp>(loc, from, to);
757  return success();
758 }
759 
760 //===----------------------------------------------------------------------===//
761 // Bufferization-specific IRMapping support with debugging.
762 //===----------------------------------------------------------------------===//
763 
766  MemRefLayoutAttrInterface layout,
767  Attribute memorySpace) {
768  auto tensorType = llvm::cast<TensorType>(value.getType());
769 
770  // Case 1: Unranked memref type.
771  if (auto unrankedTensorType =
772  llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
773  assert(!layout && "UnrankedTensorType cannot have a layout map");
774  return UnrankedMemRefType::get(unrankedTensorType.getElementType(),
775  memorySpace);
776  }
777 
778  // Case 2: Ranked memref type with specified layout.
779  auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
780  if (layout) {
781  return MemRefType::get(rankedTensorType.getShape(),
782  rankedTensorType.getElementType(), layout,
783  memorySpace);
784  }
785 
786  return options.unknownTypeConverterFn(value, memorySpace, options);
787 }
788 
791  Attribute memorySpace) {
792  // Case 1: Unranked memref type.
793  if (auto unrankedTensorType =
794  llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
795  return UnrankedMemRefType::get(unrankedTensorType.getElementType(),
796  memorySpace);
797  }
798 
799  // Case 2: Ranked memref type.
800  auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
801  int64_t dynamicOffset = ShapedType::kDynamic;
802  SmallVector<int64_t> dynamicStrides(rankedTensorType.getRank(),
803  ShapedType::kDynamic);
804  auto stridedLayout = StridedLayoutAttr::get(tensorType.getContext(),
805  dynamicOffset, dynamicStrides);
806  return MemRefType::get(rankedTensorType.getShape(),
807  rankedTensorType.getElementType(), stridedLayout,
808  memorySpace);
809 }
810 
811 /// Return a MemRef type with a static identity layout (i.e., no layout map). If
812 /// the given tensor type is unranked, return an unranked MemRef type.
815  Attribute memorySpace) {
816  // Case 1: Unranked memref type.
817  if (auto unrankedTensorType =
818  llvm::dyn_cast<UnrankedTensorType>(tensorType)) {
819  return UnrankedMemRefType::get(unrankedTensorType.getElementType(),
820  memorySpace);
821  }
822 
823  // Case 2: Ranked memref type.
824  auto rankedTensorType = llvm::cast<RankedTensorType>(tensorType);
825  MemRefLayoutAttrInterface layout = {};
826  return MemRefType::get(rankedTensorType.getShape(),
827  rankedTensorType.getElementType(), layout,
828  memorySpace);
829 }
830 
831 //===----------------------------------------------------------------------===//
832 // Default implementations of interface methods
833 //===----------------------------------------------------------------------===//
834 
836  OpResult opResult, const AnalysisState &state) {
837  auto bufferizableOp = cast<BufferizableOpInterface>(opResult.getDefiningOp());
838  AliasingOpOperandList opOperands =
839  bufferizableOp.getAliasingOpOperands(opResult, state);
840 
841  // Case 1: OpResults that have no aliasing OpOperand usually bufferize to
842  // memory writes.
843  if (opOperands.getAliases().empty())
844  return true;
845 
846  // Case 2: If an aliasing OpOperand bufferizes to a memory write, the OpResult
847  // may bufferize to a memory write.
848  if (llvm::any_of(opOperands, [&](AliasingOpOperand alias) {
849  return state.bufferizesToMemoryWrite(*alias.opOperand);
850  }))
851  return true;
852 
853  // Case 3: Check if a nested aliasing OpOperand value bufferizes to a memory
854  // write. (Or: The reverse SSA use-def chain ends inside the reigon.) In that
855  // case, the OpResult bufferizes to a memory write. E.g.:
856  //
857  // %0 = "some_writing_op" : tensor<?xf32>
858  // %r = scf.if ... -> tensor<?xf32> {
859  // scf.yield %0 : tensor<?xf32>
860  // } else {
861  // %1 = "another_writing_op"(%0) : tensor<?xf32>
862  // scf.yield %1 : tensor<?xf32>
863  // }
864  // "some_reading_op"(%r)
865  //
866  // %r bufferizes to a memory write because an aliasing OpOperand value (%1)
867  // bufferizes to a memory write and the defining op is inside the scf.if.
868  //
869  // Note: This treatment of surrouding ops is useful for ops that have a
870  // region but no OpOperand such as scf.if or scf.execute_region. It simplifies
871  // the analysis considerably.
872  //
873  // "another_writing_op" in the above example should be able to bufferize
874  // inplace in the absence of another read of %0. However, if the scf.if op
875  // would not be considered a "write", the analysis would detect the
876  // following conflict:
877  //
878  // * read = some_reading_op
879  // * lastWrite = %0 (Note: The last write of %r would be a set: {%0, %1}.)
880  // * conflictingWrite = %1
881  //
882  auto isMemoryWriteInsideOp = [&](Value v) {
883  Operation *op = getOwnerOfValue(v);
884  if (!opResult.getDefiningOp()->isAncestor(op))
885  return false;
886  return state.bufferizesToMemoryWrite(v);
887  };
888  TraversalConfig config;
889  config.alwaysIncludeLeaves = false;
890  for (AliasingOpOperand alias : opOperands) {
891  if (!state
892  .findValueInReverseUseDefChain(alias.opOperand->get(),
893  isMemoryWriteInsideOp, config)
894  .empty())
895  return true;
896  }
897  return false;
898 }
899 
900 // Compute the AliasingOpOperandList for a given Value based on
901 // getAliasingValues.
903  Value value, const AnalysisState &state) {
904  Operation *op = getOwnerOfValue(value);
906  for (OpOperand &opOperand : op->getOpOperands()) {
907  if (!llvm::isa<TensorType>(opOperand.get().getType()))
908  continue;
909  AliasingValueList aliasingValues = state.getAliasingValues(opOperand);
910  for (const auto &it : aliasingValues)
911  if (it.value == value)
912  result.emplace_back(&opOperand, it.relation, it.isDefinite);
913  }
914  return AliasingOpOperandList(std::move(result));
915 }
916 
918  Value value, const BufferizationOptions &options,
919  SmallVector<Value> &invocationStack) {
920  assert(llvm::isa<TensorType>(value.getType()) && "expected tensor type");
921 
922  // No further analysis is possible for a block argument.
923  if (llvm::isa<BlockArgument>(value))
924  return bufferization::getMemRefType(value, options);
925 
926  // Value is an OpResult.
927  Operation *op = getOwnerOfValue(value);
928  auto opResult = llvm::cast<OpResult>(value);
929  AnalysisState state(options);
930  AliasingOpOperandList aliases = state.getAliasingOpOperands(opResult);
931  if (aliases.getNumAliases() > 0 &&
932  aliases.getAliases()[0].relation == BufferRelation::Equivalent) {
933  // If the OpResult has an equivalent OpOperand, both OpResult and
934  // OpOperand bufferize to the exact same buffer type.
935  Value equivalentOperand = aliases.getAliases().front().opOperand->get();
936  return getBufferType(equivalentOperand, options, invocationStack);
937  }
938 
939  // If we do not know the memory space and there is no default memory space,
940  // report a failure.
941  auto memSpace =
942  options.defaultMemorySpaceFn(cast<TensorType>(value.getType()));
943  if (!memSpace.has_value())
944  return op->emitError("could not infer memory space");
945 
946  return getMemRefType(value, options, /*layout=*/{}, *memSpace);
947 }
948 
950  BufferizableOpInterface bufferizableOp, unsigned index) {
951  assert(index < bufferizableOp->getNumRegions() && "invalid region index");
952  auto regionInterface =
953  dyn_cast<RegionBranchOpInterface>(bufferizableOp.getOperation());
954  if (!regionInterface)
955  return false;
956  return regionInterface.isRepetitiveRegion(index);
957 }
958 
961  // TODO: Take into account successor blocks.
962  // No aliasing in case of non-entry blocks.
963  if (auto bbArg = dyn_cast<BlockArgument>(value))
964  if (bbArg.getOwner() != &bbArg.getOwner()->getParent()->getBlocks().front())
965  return {};
966 
967  // Unknown op: Conservatively assume that each OpResult may alias with every
968  // OpOperand. In addition, each block argument of an entry block may alias
969  // with every OpOperand.
971  for (OpOperand &operand : value.getDefiningOp()->getOpOperands())
972  if (isa<TensorType>(operand.get().getType()))
973  r.addAlias({&operand, BufferRelation::Unknown, /*isDefinite=*/false});
974  return r;
975 }
976 
979  // TODO: Take into account successor blocks.
980  // Unknown op: Conservatively assume that each OpResult may alias with every
981  // OpOperand. In addition, each block argument of an entry block may alias
982  // with every OpOperand.
984  for (OpResult result : opOperand.getOwner()->getOpResults())
985  if (llvm::isa<TensorType>(result.getType()))
986  r.addAlias({result, BufferRelation::Unknown, /*isDefinite=*/false});
987  for (Region &region : opOperand.getOwner()->getRegions())
988  if (!region.getBlocks().empty())
989  for (BlockArgument bbArg : region.getBlocks().front().getArguments())
990  if (isa<TensorType>(bbArg.getType()))
991  r.addAlias({bbArg, BufferRelation::Unknown, /*isDefinite=*/false});
992  return r;
993 }
994 
996  auto isaTensor = [](Type t) { return isa<TensorType>(t); };
997  bool hasTensorBlockArgument = any_of(op->getRegions(), [&](Region &r) {
998  return any_of(r.getBlocks(), [&](Block &b) {
999  return any_of(b.getArguments(), [&](BlockArgument bbArg) {
1000  return isaTensor(bbArg.getType());
1001  });
1002  });
1003  });
1004  if (hasTensorBlockArgument)
1005  return true;
1006 
1007  if (any_of(op->getResultTypes(), isaTensor))
1008  return true;
1009  return any_of(op->getOperandTypes(), isaTensor);
1010 }
static void ensureToMemrefOpIsValid(Value tensor, Type memrefType)
static void setInsertionPointAfter(OpBuilder &b, Value value)
static bool isRepetitiveRegion(Region *region, const BufferizationOptions &options)
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:263
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:149
This class represents an argument of a Block.
Definition: Value.h:319
Block represents an ordered list of Operations.
Definition: Block.h:31
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:26
IntegerAttr getI64IntegerAttr(int64_t value)
Definition: Builders.cpp:152
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:66
RAII guard to reset the insertion point of the builder when destroyed.
Definition: Builders.h:356
This class helps build Operations.
Definition: Builders.h:215
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition: Builders.h:439
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:406
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:497
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition: Builders.h:420
This class represents an operand of an operation.
Definition: Value.h:267
This is a value defined by a result of an operation.
Definition: Value.h:457
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
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
Definition: Operation.h:402
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:672
operand_type_range getOperandTypes()
Definition: Operation.h:392
MutableArrayRef< OpOperand > getOpOperands()
Definition: Operation.h:378
result_type_range getResultTypes()
Definition: Operation.h:423
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:415
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:399
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:400
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:630
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
Definition: BuiltinTypes.h:102
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:104
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:381
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:129
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition: Value.h:212
Location getLoc() const
Return the location of this value.
Definition: Value.cpp:26
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:20
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition: Value.cpp:41
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.
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 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.
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.
SetVector< Value > findValueInReverseUseDefChain(Value value, llvm::function_ref< bool(Value)> condition, TraversalConfig config=TraversalConfig()) const
Starting from value, follow the use-def chain in reverse, always selecting the aliasing OpOperands.
SetVector< Value > findDefinitions(Value value) const
Find the values that may define the contents of the given value at runtime.
virtual bool areEquivalentBufferizedValues(Value v1, Value v2) const
Return true if v1 and v2 bufferize to equivalent buffers.
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.
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.
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.
FailureOr< BaseMemRefType > defaultGetBufferType(Value value, const BufferizationOptions &options, SmallVector< Value > &invocationStack)
This is the default implementation of BufferizableOpInterface::getBufferType.
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.
BaseMemRefType getMemRefType(Value value, const BufferizationOptions &options, MemRefLayoutAttrInterface layout={}, Attribute memorySpace=nullptr)
Return a MemRefType to which the type of the given value can be bufferized.
Region * getParallelRegion(Region *region, const BufferizationOptions &options)
If region is a parallel region, return region.
Region * getNextEnclosingRepetitiveRegion(Region *region, const BufferizationOptions &options)
Assuming that the given region is repetitive, find the next enclosing repetitive region.
AliasList< AliasingOpOperand > AliasingOpOperandList
A list of possible aliasing OpOperands.
FailureOr< Value > allocateTensorForShapedValue(OpBuilder &b, Location loc, Value shapedValue, const BufferizationOptions &options, bool copy=true)
Create an AllocTensorOp for the given shaped value (memref or tensor).
FailureOr< BaseMemRefType > getBufferType(Value value, const BufferizationOptions &options)
Return the buffer type for a given Value (tensor) after bufferization without bufferizing any IR.
FailureOr< Value > getBuffer(RewriterBase &rewriter, Value value, const BufferizationOptions &options)
Lookup the buffer for the given value.
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
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).
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 converter from tensors to memrefs.
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.
bool followUnknownOps
Specifies whether unknown/non-bufferizable/ops not included in the OpFilter of BufferizationOptions s...
bool alwaysIncludeLeaves
Specifies if leaves (that do not have further OpOperands to follow) should be returned even if they d...
bool followSameTypeOrCastsOnly
Specifies whether OpOperands with a different type that are not the result of a CastOpInterface op sh...
bool followInPlaceOnly
Specifies whether out-of-place/undecided OpOperands should be followed.
bool followEquivalentOnly
Specifies whether non-equivalent OpOperands should be followed.
bool revisitAlreadyVisitedValues
Specifies whether already visited values should be visited again.