MLIR  19.0.0git
BufferizableOpInterface.h
Go to the documentation of this file.
1 //===- BufferizableOpInterface.h - Bufferizable Ops -------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef MLIR_DIALECT_BUFFERIZATION_IR_BUFFERIZABLEOPINTERFACE_H_
10 #define MLIR_DIALECT_BUFFERIZATION_IR_BUFFERIZABLEOPINTERFACE_H_
11 
12 #include "mlir/IR/Operation.h"
13 #include "mlir/IR/PatternMatch.h"
14 #include "mlir/Support/LLVM.h"
15 #include "llvm/ADT/DenseMapInfoVariant.h"
16 #include "llvm/ADT/SetVector.h"
17 #include <optional>
18 
19 #include "mlir/Dialect/Bufferization/IR/BufferizationEnums.h.inc"
20 
21 namespace mlir {
22 class OpBuilder;
23 namespace func {
24 class FuncOp;
25 }
26 
27 namespace bufferization {
28 
29 class AnalysisState;
30 class BufferizableOpInterface;
31 
32 /// Specifies a fine-grain relationship between buffers to enable more analysis.
33 enum class BufferRelation {
34  Unknown,
35  // TODO: ResultContainsOperand,
36  // TODO: OperandContainsResult,
38 };
39 
40 /// A maybe aliasing OpOperand. If `isDefinite` is `true`, the OpOperand is
41 /// guaranteed to alias at runtime.
44  bool isDefinite = true)
46 
49  bool isDefinite;
50 };
51 
52 /// A maybe aliasing Value. If `isDefinite` is `true`, the Value is guaranteed
53 /// to alias at runtime.
54 struct AliasingValue {
57 
60  bool isDefinite;
61 };
62 
63 template <typename T> class AliasList {
64 public:
65  /// Create an empty list of aliases.
66  AliasList() = default;
67 
68  /// Create a list of aliases.
69  AliasList(std::initializer_list<T> elems) {
70  for (T alias : elems)
71  addAlias(alias);
72  }
73 
74  /// Create a list of aliases.
75  AliasList(SmallVector<T> &&aliases) : aliases(std::move(aliases)) {}
76 
77  ArrayRef<T> getAliases() const { return aliases; }
78 
79  size_t getNumAliases() const { return aliases.size(); }
80 
81  void addAlias(T alias) { aliases.push_back(alias); }
82 
83  auto begin() const { return aliases.begin(); }
84  auto end() const { return aliases.end(); }
85 
86 private:
87  /// The list of aliases.
88  SmallVector<T> aliases;
89 };
90 
91 /// A list of possible aliasing OpOperands. This list models the runtime
92 /// aliasing relationship for a Value.
94 
95 /// A list of possible aliasing Values. This list models the runtime aliasing
96 /// relationship for an OpOperand.
98 
99 class OpFilter {
100 public:
101  /// An op filter entry. Filters can be used to specify which ops should be
102  /// processed by the bufferization.
103  struct Entry {
104  /// If the filter function evaluates to `true`, the filter matches.
105  using FilterFn = std::function<bool(Operation *)>;
106 
107  /// Filter type: A filter can either be a DENY filter or an ALLOW filter.
108  enum FilterType : int8_t { DENY = 0, ALLOW = 1 };
109 
112  };
113 
114  /// Return whether the op is allowed or not.
115  ///
116  /// If the filter does not have an ALLOW rule, ops are allowed by default,
117  /// unless they are explicitly marked as DENY. If the filter has at least one
118  /// ALLOW rule, ops are denied by default and only allowed if they match
119  /// an ALLOW rule and no DENY rule.
120  bool isOpAllowed(Operation *op) const;
121 
122  /// Allow the given dialects.
123  ///
124  /// This function adds one or multiple ALLOW entries.
125  template <typename... DialectTs>
126  void allowDialect() {
127  // The following expands a call to allowDialectImpl for each dialect
128  // in 'DialectTs'.
129  (allowDialectImpl<DialectTs>(), ...);
130  }
131 
132  /// Deny the given dialects.
133  ///
134  /// This function adds one or multiple DENY entries.
135  template <typename... DialectTs>
136  void denyDialect() {
137  (denyDialectImpl<DialectTs>(), ...);
138  }
139 
140  /// Allow the given dialect.
141  ///
142  /// This function adds an ALLOW entry.
143  void allowDialect(StringRef dialectNamespace) {
144  Entry::FilterFn filterFn = [=](Operation *op) {
145  return op->getName().getDialectNamespace() == dialectNamespace;
146  };
147  entries.push_back(Entry{filterFn, Entry::FilterType::ALLOW});
148  }
149 
150  /// Deny the given dialect.
151  ///
152  /// This function adds a DENY entry.
153  void denyDialect(StringRef dialectNamespace) {
154  Entry::FilterFn filterFn = [=](Operation *op) {
155  return op->getDialect()->getNamespace() == dialectNamespace;
156  };
157  entries.push_back(Entry{filterFn, Entry::FilterType::DENY});
158  }
159 
160  /// Allow the given ops.
161  ///
162  /// This function adds one or multiple ALLOW entries.
163  template <typename... OpTys>
164  void allowOperation() {
165  (allowOperationImpl<OpTys>(), ...);
166  }
167 
168  /// Deny the given ops.
169  ///
170  /// This function adds one or multiple DENY entries.
171  template <typename... OpTys>
172  void denyOperation() {
173  (denyOperationImpl<OpTys>(), ...);
174  }
175 
176  /// Allow the given op.
177  ///
178  /// This function adds an ALLOW entry.
179  void allowOperation(StringRef opName) {
180  Entry::FilterFn filterFn = [=](Operation *op) {
181  return op->getName().getStringRef() == opName;
182  };
183  allowOperation(filterFn);
184  }
185 
186  /// Deny the given op.
187  ///
188  /// This function adds a DENY entry.
189  void denyOperation(StringRef opName) {
190  Entry::FilterFn filterFn = [=](Operation *op) {
191  return op->getName().getStringRef() == opName;
192  };
193  denyOperation(filterFn);
194  }
195 
196  /// Allow ops that are matched by `fn`.
197  ///
198  /// This function adds an ALLOW entry.
200  entries.push_back(Entry{fn, Entry::FilterType::ALLOW});
201  }
202 
203  /// Deny ops that are matched by `fn`.
204  ///
205  /// This function adds a DENY entry.
207  entries.push_back(Entry{fn, Entry::FilterType::DENY});
208  }
209 
210 private:
211  /// Return `true` if the filter has at least one ALLOW rule.
212  bool hasAllowRule() const {
213  for (const Entry &e : entries)
214  if (e.type == Entry::FilterType::ALLOW)
215  return true;
216  return false;
217  }
218 
219  /// Allow a dialect.
220  template <typename DialectT>
221  void allowDialectImpl() {
222  allowDialect(DialectT::getDialectNamespace());
223  }
224 
225  /// Deny a dialect.
226  template <typename DialectT>
227  void denyDialectImpl() {
228  denyDialect(DialectT::getDialectNamespace());
229  }
230 
231  /// Allow an op.
232  template <typename OpTy>
233  void allowOperationImpl() {
234  allowOperation(OpTy::getOperationName());
235  }
236 
237  /// Deny an op.
238  template <typename OpTy>
239  void denyOperationImpl() {
240  denyOperation(OpTy::getOperationName());
241  }
242 
243  /// A list of filter entries that determine whether an op should be allowed or
244  /// denied. If the filter has an ALLOW rule, only ops that are allowed and not
245  /// denied are allowed. If the filter does not have an ALLOW rule, only ops
246  /// that are not denied are allowed.
247  SmallVector<Entry> entries;
248 };
249 
250 /// Options for BufferizableOpInterface-based bufferization.
252  /// Allocator function: Generate a memref allocation with the given type,
253  /// dynamic extents and alignment.
254  using AllocationFn = std::function<FailureOr<Value>(
255  OpBuilder &, Location, MemRefType, ValueRange, unsigned int)>;
256  /// Memcpy function: Generate a memcpy between two buffers.
257  using MemCpyFn =
258  std::function<LogicalResult(OpBuilder &, Location, Value, Value)>;
259  /// Initializer function for analysis state.
260  using AnalysisStateInitFn = std::function<void(AnalysisState &)>;
261  /// Tensor -> MemRef type converter.
262  /// Parameters: Value, memory space, func op, bufferization options
264  std::function<BaseMemRefType(TensorType, Attribute memorySpace,
265  func::FuncOp, const BufferizationOptions &)>;
266  /// Tensor -> MemRef type converter.
267  /// Parameters: Value, memory space, bufferization options
268  using UnknownTypeConverterFn = std::function<BaseMemRefType(
269  Value, Attribute memorySpace, const BufferizationOptions &)>;
270  // Produce a MemorySpace attribute from a tensor type
272  std::function<std::optional<Attribute>(TensorType t)>;
273 
275 
276  /// Try to cast the given op to BufferizableOpInterface if the op is allow
277  /// listed.
278  BufferizableOpInterface dynCastBufferizableOp(Operation *op) const;
279 
280  /// Try to cast the given value to BufferizableOpInterface if the op is allow
281  /// listed.
282  BufferizableOpInterface dynCastBufferizableOp(Value value) const;
283 
284  /// A filter that specifies which ops should be bufferized and which ops
285  /// should be ignored.
287 
288  /// Return `true` if the given op should be bufferized.
289  bool isOpAllowed(Operation *op) const;
290 
291  /// Helper functions for allocation and memory copying.
292  std::optional<AllocationFn> allocationFn;
293  std::optional<MemCpyFn> memCpyFn;
294 
295  /// Create a memref allocation with the given type and dynamic extents.
296  FailureOr<Value> createAlloc(OpBuilder &b, Location loc, MemRefType type,
297  ValueRange dynShape) const;
298 
299  /// Creates a memcpy between two given buffers.
301  Value to) const;
302 
303  /// Specifies whether not bufferizable ops are allowed in the input. If so,
304  /// bufferization.to_memref and bufferization.to_tensor ops are inserted at
305  /// the boundaries.
306  bool allowUnknownOps = false;
307 
308  /// Specifies whether function boundaries (ops in the func dialect) should be
309  /// bufferized or not.
311 
312  // Specifies whether to account for parallel regions in RaW analysis. If true,
313  // then writes inside of parallel regions that write to buffers defined
314  // outside of the parallel region will be given a new buffer.
315  bool checkParallelRegions = true;
316 
317  /// Certain ops have aliasing OpOperand/OpResult invariants (e.g., scf.for).
318  /// If this flag is set to `false`, those invariants are no longer enforced
319  /// with buffer copies.
320  ///
321  /// Note: Deactivating this flag can lead to incorrect bufferization results
322  /// when used incorrectly. This flag is useful with
323  /// `AlwaysCopyAnalysisState` which bufferizes all writing tensor
324  /// OpOperands out-of-place.
326 
327  /// This function controls buffer types on function signatures. Sets
328  /// `functionArgTypeConverterFn` and `inferFunctionResultLayout` accordingly.
329  ///
330  /// * InferLayoutMap: All function parameter types have a fully dynamic layout
331  /// map, but function result types are inferred from the body of the
332  /// function.
333  /// * FullyDynamicLayoutMap: All function parameter types and result types
334  /// have a fully dynamic layout map. This option is most efficient because
335  /// any layout map can be casted to a fully dynamic one.
336  /// * IdentityLayoutMap: All function parameter types and result types have a
337  /// static identity layout (i.e., no layout map). This option may introduce
338  /// additional buffer allocs and copies because layout maps cannot be casted
339  /// away.
340  ///
341  /// Note: Inferred layout maps may not be desireable when interacting with
342  /// external functions, because the generated function signatures will be less
343  /// predictable.
344  void setFunctionBoundaryTypeConversion(LayoutMapOption layoutMapOption);
345 
346  /// Type converter from tensors to memrefs. This type converter is used to
347  /// determine bufferized function argument types. By default, a type
348  /// converter that returns a memref type with a fully dynamic layout map is
349  /// used.
350  ///
351  /// If `bufferizeFunctionBoundaries` is not set, this function isn't used.
353 
354  /// If true, function result types are inferred from the body of the function.
355  /// Otherwise, function result type is determined by
356  /// `functionArgTypeConverterFn`.
357  ///
358  /// If `bufferizeFunctionBoundaries` is not set, this flag has no effect.
360 
361  /// Type converter from tensors to memrefs. This type converter is used if no
362  /// memref type could be inferred during bufferization. By default, a type
363  /// converter that returns a memref type with a fully dynamic layout map is
364  /// used.
366 
367  // Use during type conversion to determine the memory space for memref based
368  // on the original tensor type if the memory space cannot be inferred.
369  // Returning std::nullopt will cause bufferization to fail (useful to indicate
370  // failure to determine memory space for a tensor type).
372  [](TensorType t) -> std::optional<Attribute> { return Attribute(); };
373 
374  /// If set to `true`, the analysis is skipped. A buffer is copied before every
375  /// write. This flag cannot be used together with `testAnalysisOnly = true`.
376  bool copyBeforeWrite = false;
377 
378  /// If set to `true`, does not modify the IR apart from adding attributes (for
379  /// checking the results of the analysis) and post analysis steps.
380  bool testAnalysisOnly = false;
381 
382  /// If set to `true`, the IR is annotated with details about RaW conflicts.
383  /// For debugging only. Should be used together with `testAnalysisOnly`.
384  bool printConflicts = false;
385 
386  /// Buffer alignment for new memory allocations.
387  unsigned int bufferAlignment = 64;
388 
389  /// Initializer functions for analysis state. These can be used to
390  /// initialize dialect-specific analysis state.
392 };
393 
394 /// Traversal parameters for `findValueInReverseUseDefChain`.
396  /// Specifies if leaves (that do not have further OpOperands to follow)
397  /// should be returned even if they do not match the specified filter.
398  bool alwaysIncludeLeaves = true;
399 
400  /// Specifies whether out-of-place/undecided OpOperands should be followed.
401  bool followInPlaceOnly = false;
402 
403  /// Specifies whether non-equivalent OpOperands should be followed.
404  bool followEquivalentOnly = false;
405 
406  /// Specifies whether unknown/non-bufferizable/ops not included in the
407  /// OpFilter of BufferizationOptions should be followed.
408  bool followUnknownOps = false;
409 
410  /// Specifies whether OpOperands with a different type that are not the result
411  /// of a CastOpInterface op should be followed.
413 
414  /// Specifies whether already visited values should be visited again.
415  /// (Note: This can result in infinite looping.)
417 };
418 
419 /// AnalysisState provides a variety of helper functions for dealing with
420 /// tensor values.
422 public:
423  /// Determine which OpOperand* will alias with `value` if the op is
424  /// bufferized in place. Return all tensor OpOperand* if the op is not
425  /// bufferizable.
427 
428  /// Determine which Value will alias with `opOperand` if the op is bufferized
429  /// in place. Return all tensor Values if the op is not bufferizable.
431 
432  /// Return true if `opOperand` bufferizes to a memory read. Return `true` if
433  /// the op is not bufferizable.
434  bool bufferizesToMemoryRead(OpOperand &opOperand) const;
435 
436  /// Return true if `opOperand` bufferizes to a memory write. Return true` if
437  /// the op is not bufferizable.
438  bool bufferizesToMemoryWrite(OpOperand &opOperand) const;
439 
440  /// Return true if the given `value` bufferizes to a memory write. Return
441  /// true if the value is a block argument. Return `true` if the defining op is
442  /// not bufferizable. Otherwise, consult the BufferizableOpInterface.
443  bool bufferizesToMemoryWrite(Value value) const;
444 
445  /// Return true if `opOperand` does neither read nor write but bufferizes to
446  /// an alias. Return false if the op is not bufferizable.
447  bool bufferizesToAliasOnly(OpOperand &opOperand) const;
448 
449  /// Return true if a copy can always be avoided when allocating a new tensor
450  /// for the given OpOperand.
451  bool canOmitTensorCopy(OpOperand &opOperand) const;
452 
453  /// Return true if the given value is read by an op that bufferizes to a
454  /// memory read. Also takes into account ops that create an alias but do not
455  /// read by themselves (e.g., ExtractSliceOp).
456  bool isValueRead(Value value) const;
457 
458  /// Starting from `value`, follow the use-def chain in reverse, always
459  /// selecting the aliasing OpOperands. Find and return Values for which
460  /// `condition` evaluates to true. OpOperands of such matching Values are not
461  /// traversed any further.
462  ///
463  /// When reaching the end of a chain, also return the last Value of that
464  /// chain if `config.alwaysIncludeLeaves` is set.
465  ///
466  /// Example:
467  ///
468  /// 8
469  /// |
470  /// 6* 7* +-----+----+
471  /// | | | |
472  /// 2* 3 4* 5
473  /// | | | |
474  /// +----------+----------+----------+
475  /// |
476  /// 1
477  ///
478  /// In the above example, Values with a star satisfy the condition. When
479  /// starting the traversal from Value 1, the resulting SetVector is:
480  /// { 2, 7, 8, 5 }
481  ///
482  /// Additional stopping conditions for the traversal can be specified in
483  /// `config`.
485  Value value, llvm::function_ref<bool(Value)> condition,
486  TraversalConfig config = TraversalConfig()) const;
487 
488  /// Find the values that may define the contents of the given value at
489  /// runtime. A block argument is always a definition. An OpResult is a
490  /// definition if it bufferizes to memory write. If it does not bufferize to
491  /// a memory write but has aliasing operands, we continue the lookup on these
492  /// values.
493  ///
494  /// Example: %r = tensor.insert %f into %t[%c0] : tensor<?xf32>
495  /// findDefinitions(%r) = {%r} because %r bufferizes to memory write.
496  ///
497  /// Example: %r = tensor.empty() : tensor<10xf32>
498  /// findDefinitions(%r) = {} because tensor.empty does not the define the
499  /// contents of its result (i.e., it does not bufferize to a memory write)
500  /// and it has no aliasing OpOperands.
501  ///
502  /// Example:
503  /// %a = arith.constant ... : tensor<10xf32>
504  /// %b1 = tensor.insert %f into %t : tensor<50xf32>
505  /// %b2 = tensor.extract_slice %b1[0][10][1] : tensor<50xf32> tensor<10xf32>
506  /// %r = arith.select %cond, %a, %b : tensor<10xf32>
507  /// findDefinitions(%r) = {%a, %b1}. %r and %b2 are skipped (lookup continues
508  /// in the operands) because their defining ops do not define the contents of
509  /// the tensor.
510  ///
511  /// Example:
512  /// %a = tensor.empty() : tensor<10xf32>
513  /// %b = arith.constant ... : tensor<10xf32>
514  /// %r = arith.select %cond, %a, %b : tensor<10xf32>
515  /// findDefinitions(%r) = {%b}. %a is excluded because it does not define the
516  /// contents of the tensor.
517  ///
518  /// Note: OpResults of unknown ops are handled conservatively and assumed to
519  /// be definitions.
521 
522  /// Return `true` if the given OpResult has been decided to bufferize inplace.
523  virtual bool isInPlace(OpOperand &opOperand) const;
524 
525  /// Return true if `v1` and `v2` bufferize to equivalent buffers.
526  virtual bool areEquivalentBufferizedValues(Value v1, Value v2) const;
527 
528  /// Return true if `v1` and `v2` may bufferize to aliasing buffers.
529  virtual bool areAliasingBufferizedValues(Value v1, Value v2) const;
530 
531  /// Return `true` if the given tensor has undefined contents.
532  virtual bool hasUndefinedContents(OpOperand *opOperand) const;
533 
534  /// Return a reference to the BufferizationOptions.
535  const BufferizationOptions &getOptions() const { return options; }
536 
537  AnalysisState(const BufferizationOptions &options);
538 
539  // AnalysisState should be passed as a reference.
540  AnalysisState(const AnalysisState &) = delete;
541 
542  virtual ~AnalysisState() = default;
543 
544  static bool classof(const AnalysisState *base) { return true; }
545 
546  TypeID getType() const { return type; }
547 
548  /// Return the closest enclosing repetitive region around the given op.
550  const BufferizationOptions &options);
551 
552  /// Return the closest enclosing repetitive region around the place where the
553  /// given value is defined.
555  const BufferizationOptions &options);
556 
557  /// Return the closest enclosing repetitive region around the given block.
559  const BufferizationOptions &options);
560 
561  virtual void resetCache();
562 
563 protected:
564  AnalysisState(const BufferizationOptions &options, TypeID type);
565 
566 private:
567  /// A reference to current bufferization options.
568  const BufferizationOptions &options;
569 
570  /// The type of analysis.
571  TypeID type;
572 
573  /// Cache containing closest ancestor repetitive Region.
575  enclosingRepetitiveRegionCache;
576 };
577 
578 /// Create an AllocTensorOp for the given shaped value (memref or tensor).
579 /// If `copy` is set, the shaped value is copied. Otherwise, a tensor with
580 /// undefined contents is allocated.
583  const BufferizationOptions &options,
584  bool copy = true);
585 
586 /// Lookup the buffer for the given value. If the value was not bufferized
587 /// yet, wrap it in a ToMemrefOp. Otherwise, it is the result of a ToTensorOp,
588 /// from which the memref operand is returned.
590  const BufferizationOptions &options);
591 
592 /// Return the buffer type for a given Value (tensor) after bufferization
593 /// without bufferizing any IR.
594 ///
595 /// Note: It should be sufficient to call `getBuffer()->getType()` in most
596 /// cases. However, when a buffer type should be predicted without modifying any
597 /// IR, this function can be used.
598 ///
599 /// This function is a wrapper around BufferizableOpInterface::getBufferType.
601  const BufferizationOptions &options);
602 
603 /// Return the buffer type for a given Value (tensor) after bufferization
604 /// without bufferizing any IR. This function (and not the other overload
605 /// without `invocationStack`) can be used from `getBufferType` implementations
606 /// of the `BufferizableOpInterface`.
607 ///
608 /// Note: It should be sufficient to call `getBuffer()->getType()` in most
609 /// cases. However, when a buffer type should be predicted without modifying any
610 /// IR, this function can be used.
611 ///
612 /// This function is a wrapper around `BufferizableOpInterface::getBufferType`.
614  const BufferizationOptions &options,
615  SmallVector<Value> &invocationStack);
616 
617 /// Return "true" if the given op has tensor semantics and should be bufferized.
618 /// If the op is bufferizable, the BufferizableOpInterface is queried.
619 /// Otherwise, an op has tensor semantics if it has tensor operands, tensor
620 /// op results and/or tensor block arguments.
621 bool hasTensorSemantics(Operation *op);
622 
623 /// Replace an op with replacement values. The op is deleted. Tensor OpResults
624 /// must be replaced with memref values.
626  ValueRange values);
627 
628 /// Replace an op with a new op. The new op must have the same number of
629 /// results as the replaced op. The new op may not return any tensor values.
630 template <typename OpTy, typename... Args>
632  Args &&...args) {
633  auto newOp = rewriter.create<OpTy>(op->getLoc(), std::forward<Args>(args)...);
634  replaceOpWithBufferizedValues(rewriter, op, newOp->getResults());
635  return newOp;
636 }
637 
638 /// Return a MemRefType to which the type of the given value can be bufferized.
639 ///
640 /// If possible, op bufferization implementations should not use this function
641 /// and instead infer precise memref types for tensor results by themselves.
642 ///
643 /// Unless a layout map was specified, `options.unknownTypeConverterFn`
644 /// determines what kind of layout map will be used. For best composability
645 /// (without copies), the fully dynamic layout map is used by default.
646 ///
647 /// Note: Canonicalization patterns could clean up layout maps and infer more
648 /// precise layout maps after bufferization. However, many possible
649 /// canonicalizations are currently not implemented.
650 BaseMemRefType getMemRefType(Value value, const BufferizationOptions &options,
651  MemRefLayoutAttrInterface layout = {},
652  Attribute memorySpace = nullptr);
653 
654 /// Return a MemRef type with fully dynamic layout. If the given tensor type
655 /// is unranked, return an unranked MemRef type.
656 BaseMemRefType
657 getMemRefTypeWithFullyDynamicLayout(TensorType tensorType,
658  Attribute memorySpace = nullptr);
659 
660 /// Return a MemRef type with a static identity layout (i.e., no layout map). If
661 /// the given tensor type is unranked, return an unranked MemRef type.
662 BaseMemRefType
663 getMemRefTypeWithStaticIdentityLayout(TensorType tensorType,
664  Attribute memorySpace = nullptr);
665 
666 /// Return the owner of the given value. In case of a BlockArgument that is the
667 /// owner of the block. In case of an OpResult that is the defining op.
668 Operation *getOwnerOfValue(Value value);
669 
670 /// Assuming that the given region is repetitive, find the next enclosing
671 /// repetitive region.
672 Region *getNextEnclosingRepetitiveRegion(Region *region,
673  const BufferizationOptions &options);
674 
675 /// If `region` is a parallel region, return `region`. Otherwise, find the first
676 /// enclosing parallel region of `region`. If there is no such region, return
677 /// "nullptr".
678 ///
679 /// Note: Whether a region is parallel or sequential is queried from the
680 /// `BufferizableOpInterface`.
681 Region *getParallelRegion(Region *region, const BufferizationOptions &options);
682 
683 namespace detail {
684 /// This is the default implementation of
685 /// BufferizableOpInterface::getAliasingOpOperands. Should not be called from
686 /// other places.
688  const AnalysisState &state);
689 
690 /// This is the default implementation of
691 /// BufferizableOpInterface::getBufferType. Should not be called from other
692 /// places.
695  SmallVector<Value> &invocationStack);
696 
697 /// This is the default implementation of
698 /// BufferizableOpInterface::resultBufferizesToMemoryWrite. Should not be called
699 /// from other places.
701  const AnalysisState &state);
702 
703 /// This is the default implementation of
704 /// BufferizableOpInterface::isRepetitiveRegion. Should not be called from other
705 /// places.
706 bool defaultIsRepetitiveRegion(BufferizableOpInterface bufferizableOp,
707  unsigned index);
708 
709 /// This is the default implementation of getAliasingOpOperands in case the
710 /// defining op does not implement the BufferizableOpInterface.
712 
713 /// This is the default implementation of getAliasingValues in case the owner
714 /// op does not implement the BufferizableOpInterface.
716 
717 /// This is the default implementation of
718 /// BufferizableOpInterface::hasTensorSemantics
720 } // namespace detail
721 
722 } // namespace bufferization
723 } // namespace mlir
724 
726 
727 //===----------------------------------------------------------------------===//
728 // Bufferization Interfaces
729 //===----------------------------------------------------------------------===//
730 
731 #include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h.inc"
732 
733 #endif // MLIR_DIALECT_BUFFERIZATION_IR_BUFFERIZABLEOPINTERFACE_H_
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 llvm::ManagedStatic< PassManagerOptions > options
#define MLIR_DECLARE_EXPLICIT_TYPE_ID(CLASS_NAME)
Definition: TypeID.h:249
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:138
Block represents an ordered list of Operations.
Definition: Block.h:31
This class provides support for representing a failure result, or a valid value of type T.
Definition: LogicalResult.h:78
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
This class helps build Operations.
Definition: Builders.h:209
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:464
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
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition: Region.h:26
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
Definition: PatternMatch.h:400
Tensor types represent multi-dimensional arrays, and have two variants: RankedTensorType and Unranked...
Definition: BuiltinTypes.h:91
This class provides an efficient unique identifier for a specific C++ type.
Definition: TypeID.h:104
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
AliasList(std::initializer_list< T > elems)
Create a list of aliases.
AliasList()=default
Create an empty list of aliases.
AliasList(SmallVector< T > &&aliases)
Create a list of aliases.
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.
static bool classof(const AnalysisState *base)
virtual bool areEquivalentBufferizedValues(Value v1, Value v2) const
Return true if v1 and v2 bufferize to equivalent buffers.
AnalysisState(const AnalysisState &)=delete
void denyOperation()
Deny the given ops.
void allowOperation(StringRef opName)
Allow the given op.
void denyOperation(StringRef opName)
Deny the given op.
void denyDialect()
Deny the given dialects.
void allowOperation(Entry::FilterFn fn)
Allow ops that are matched by fn.
void allowDialect()
Allow the given dialects.
void allowDialect(StringRef dialectNamespace)
Allow the given dialect.
void denyOperation(Entry::FilterFn fn)
Deny ops that are matched by fn.
bool isOpAllowed(Operation *op) const
Return whether the op is allowed or not.
void allowOperation()
Allow the given ops.
void denyDialect(StringRef dialectNamespace)
Deny the given dialect.
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.
OpTy replaceOpWithNewBufferizedOp(RewriterBase &rewriter, Operation *op, Args &&...args)
Replace an op with a new op.
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.
bool hasTensorSemantics(Operation *op)
Return "true" if the given op has tensor semantics and should be bufferized.
BufferRelation
Specifies a fine-grain relationship between buffers to enable more analysis.
Include the generated interface declarations.
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26
AliasingOpOperand(OpOperand *opOperand, BufferRelation relation, bool isDefinite=true)
AliasingValue(Value value, BufferRelation relation, bool isDefinite=true)
Options for BufferizableOpInterface-based bufferization.
bool copyBeforeWrite
If set to true, the analysis is skipped.
std::function< void(AnalysisState &)> AnalysisStateInitFn
Initializer function for analysis state.
void setFunctionBoundaryTypeConversion(LayoutMapOption layoutMapOption)
This function controls buffer types on function signatures.
bool allowUnknownOps
Specifies whether not bufferizable ops are allowed in the input.
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.
std::function< BaseMemRefType(Value, Attribute memorySpace, const BufferizationOptions &)> UnknownTypeConverterFn
Tensor -> MemRef type converter.
unsigned int bufferAlignment
Buffer alignment for new memory allocations.
FunctionArgTypeConverterFn functionArgTypeConverterFn
Type converter from tensors to memrefs.
bool printConflicts
If set to true, the IR is annotated with details about RaW conflicts.
std::function< std::optional< Attribute >(TensorType t)> DefaultMemorySpaceFn
std::function< BaseMemRefType(TensorType, Attribute memorySpace, func::FuncOp, const BufferizationOptions &)> FunctionArgTypeConverterFn
Tensor -> MemRef type converter.
std::optional< AllocationFn > allocationFn
Helper functions for allocation and memory copying.
bool testAnalysisOnly
If set to true, does not modify the IR apart from adding attributes (for checking the results of the ...
bool enforceAliasingInvariants
Certain ops have aliasing OpOperand/OpResult invariants (e.g., scf.for).
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.
UnknownTypeConverterFn unknownTypeConverterFn
Type converter from tensors to memrefs.
bool bufferizeFunctionBoundaries
Specifies whether function boundaries (ops in the func dialect) should be bufferized or not.
std::function< LogicalResult(OpBuilder &, Location, Value, Value)> MemCpyFn
Memcpy function: Generate a memcpy between two buffers.
FailureOr< Value > createAlloc(OpBuilder &b, Location loc, MemRefType type, ValueRange dynShape) const
Create a memref allocation with the given type and dynamic extents.
std::function< FailureOr< Value >(OpBuilder &, Location, MemRefType, ValueRange, unsigned int)> AllocationFn
Allocator function: Generate a memref allocation with the given type, dynamic extents and alignment.
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.
FilterType
Filter type: A filter can either be a DENY filter or an ALLOW filter.
std::function< bool(Operation *)> FilterFn
If the filter function evaluates to true, the filter matches.
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.