MLIR 23.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"
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"
21
22namespace mlir {
23class OpBuilder;
24namespace func {
25class FuncOp;
26}
27
28namespace bufferization {
29
30class AnalysisState;
31class BufferizableOpInterface;
32
33/// Specifies a fine-grain relationship between buffers to enable more analysis.
34enum class BufferRelation {
35 Unknown,
36 // TODO: ResultContainsOperand,
37 // TODO: OperandContainsResult,
38 Equivalent
39};
40
41/// A maybe aliasing OpOperand. If `isDefinite` is `true`, the OpOperand is
42/// guaranteed to alias at runtime.
43struct AliasingOpOperand {
44 AliasingOpOperand(OpOperand *opOperand, BufferRelation relation,
45 bool isDefinite = true)
46 : opOperand(opOperand), relation(relation), isDefinite(isDefinite) {}
47
48 OpOperand *opOperand;
49 BufferRelation relation;
50 bool isDefinite;
51};
52
53/// A maybe aliasing Value. If `isDefinite` is `true`, the Value is guaranteed
54/// to alias at runtime.
55struct AliasingValue {
56 AliasingValue(Value value, BufferRelation relation, bool isDefinite = true)
57 : value(value), relation(relation), isDefinite(isDefinite) {}
58
59 Value value;
60 BufferRelation relation;
61 bool isDefinite;
62};
63
64template <typename T>
65class AliasList {
66public:
67 /// Create an empty list of aliases.
68 AliasList() = default;
69
70 /// Create a list of aliases.
71 AliasList(std::initializer_list<T> elems) {
72 for (T alias : elems)
73 addAlias(alias);
74 }
75
76 /// Create a list of aliases.
77 AliasList(SmallVector<T> &&aliases) : aliases(std::move(aliases)) {}
78
79 ArrayRef<T> getAliases() const { return aliases; }
80
81 size_t getNumAliases() const { return aliases.size(); }
82
83 void addAlias(T alias) { aliases.push_back(alias); }
84
85 auto begin() const { return aliases.begin(); }
86 auto end() const { return aliases.end(); }
87
88private:
89 /// The list of aliases.
90 SmallVector<T> aliases;
91};
92
93/// A list of possible aliasing OpOperands. This list models the runtime
94/// aliasing relationship for a Value.
95using AliasingOpOperandList = AliasList<AliasingOpOperand>;
96
97/// A list of possible aliasing Values. This list models the runtime aliasing
98/// relationship for an OpOperand.
99using AliasingValueList = AliasList<AliasingValue>;
100
101class OpFilter {
102public:
103 /// An op filter entry. Filters can be used to specify which ops should be
104 /// processed by the bufferization.
105 struct Entry {
106 /// If the filter function evaluates to `true`, the filter matches.
107 using FilterFn = std::function<bool(Operation *)>;
108
109 /// Filter type: A filter can either be a DENY filter or an ALLOW filter.
110 enum FilterType : int8_t { DENY = 0, ALLOW = 1 };
111
112 FilterFn fn;
113 FilterType type;
114 };
115
116 /// Return whether the op is allowed or not.
117 ///
118 /// If the filter does not have an ALLOW rule, ops are allowed by default,
119 /// unless they are explicitly marked as DENY. If the filter has at least one
120 /// ALLOW rule, ops are denied by default and only allowed if they match
121 /// an ALLOW rule and no DENY rule.
122 bool isOpAllowed(Operation *op) const;
123
124 /// Allow the given dialects.
125 ///
126 /// This function adds one or multiple ALLOW entries.
127 template <typename... DialectTs>
128 void allowDialect() {
129 // The following expands a call to allowDialectImpl for each dialect
130 // in 'DialectTs'.
131 (allowDialectImpl<DialectTs>(), ...);
132 }
133
134 /// Deny the given dialects.
135 ///
136 /// This function adds one or multiple DENY entries.
137 template <typename... DialectTs>
138 void denyDialect() {
139 (denyDialectImpl<DialectTs>(), ...);
140 }
141
142 /// Allow the given dialect.
143 ///
144 /// This function adds an ALLOW entry.
145 void allowDialect(StringRef dialectNamespace) {
146 Entry::FilterFn filterFn = [=](Operation *op) {
147 return op->getName().getDialectNamespace() == dialectNamespace;
148 };
149 entries.push_back(Entry{filterFn, Entry::FilterType::ALLOW});
150 }
151
152 /// Deny the given dialect.
153 ///
154 /// This function adds a DENY entry.
155 void denyDialect(StringRef dialectNamespace) {
156 Entry::FilterFn filterFn = [=](Operation *op) {
157 return op->getName().getDialectNamespace() == dialectNamespace;
158 };
159 entries.push_back(Entry{filterFn, Entry::FilterType::DENY});
160 }
161
162 /// Allow the given ops.
163 ///
164 /// This function adds one or multiple ALLOW entries.
165 template <typename... OpTys>
166 void allowOperation() {
167 (allowOperationImpl<OpTys>(), ...);
168 }
169
170 /// Deny the given ops.
171 ///
172 /// This function adds one or multiple DENY entries.
173 template <typename... OpTys>
174 void denyOperation() {
175 (denyOperationImpl<OpTys>(), ...);
176 }
177
178 /// Allow the given op.
179 ///
180 /// This function adds an ALLOW entry.
181 void allowOperation(StringRef opName) {
182 Entry::FilterFn filterFn = [=](Operation *op) {
183 return op->getName().getStringRef() == opName;
184 };
185 allowOperation(filterFn);
186 }
187
188 /// Deny the given op.
189 ///
190 /// This function adds a DENY entry.
191 void denyOperation(StringRef opName) {
192 Entry::FilterFn filterFn = [=](Operation *op) {
193 return op->getName().getStringRef() == opName;
194 };
195 denyOperation(filterFn);
196 }
197
198 /// Allow ops that are matched by `fn`.
199 ///
200 /// This function adds an ALLOW entry.
201 void allowOperation(Entry::FilterFn fn) {
202 entries.push_back(Entry{fn, Entry::FilterType::ALLOW});
203 }
204
205 /// Deny ops that are matched by `fn`.
206 ///
207 /// This function adds a DENY entry.
208 void denyOperation(Entry::FilterFn fn) {
209 entries.push_back(Entry{fn, Entry::FilterType::DENY});
210 }
211
212private:
213 /// Return `true` if the filter has at least one ALLOW rule.
214 bool hasAllowRule() const {
215 for (const Entry &e : entries)
216 if (e.type == Entry::FilterType::ALLOW)
217 return true;
218 return false;
219 }
220
221 /// Allow a dialect.
222 template <typename DialectT>
223 void allowDialectImpl() {
224 allowDialect(DialectT::getDialectNamespace());
225 }
226
227 /// Deny a dialect.
228 template <typename DialectT>
229 void denyDialectImpl() {
230 denyDialect(DialectT::getDialectNamespace());
231 }
232
233 /// Allow an op.
234 template <typename OpTy>
235 void allowOperationImpl() {
236 allowOperation(OpTy::getOperationName());
237 }
238
239 /// Deny an op.
240 template <typename OpTy>
241 void denyOperationImpl() {
242 denyOperation(OpTy::getOperationName());
243 }
244
245 /// A list of filter entries that determine whether an op should be allowed or
246 /// denied. If the filter has an ALLOW rule, only ops that are allowed and not
247 /// denied are allowed. If the filter does not have an ALLOW rule, only ops
248 /// that are not denied are allowed.
249 SmallVector<Entry> entries;
250};
251
252/// Options for BufferizableOpInterface-based bufferization.
253struct BufferizationOptions {
254 /// Allocator function: Generate a memref allocation with the given type,
255 /// dynamic extents and alignment.
256 using AllocationFn = std::function<FailureOr<Value>(
257 OpBuilder &, Location, MemRefType, ValueRange, unsigned int)>;
258 /// Memcpy function: Generate a memcpy between two buffers.
259 using MemCpyFn =
260 std::function<LogicalResult(OpBuilder &, Location, Value, Value)>;
261 /// Cast function: Convert a buffer value to a new value with the specified
262 /// type. This method is typically used when a simple cast-like operation is
263 /// sufficient to convert the buffer value, for example, when layout maps
264 /// between buffer value and resulting type do not match.
265 using CastFn =
266 std::function<FailureOr<Value>(OpBuilder &, Location, Type, Value)>;
267 /// Initializer function for analysis state.
268 using AnalysisStateInitFn = std::function<void(AnalysisState &)>;
269 /// Tensor-like -> Buffer-like type conversion.
270 /// Parameters: tensor-like type, memory space, func op, bufferization options
271 using FunctionArgTypeConverterFn =
272 std::function<BufferLikeType(TensorLikeType, Attribute memorySpace,
273 func::FuncOp, const BufferizationOptions &)>;
274 /// Tensor -> MemRef type conversion.
275 /// Parameters: tensor type, memory space, bufferization options
276 using UnknownTypeConverterFn = std::function<BufferLikeType(
277 TensorLikeType, Attribute memorySpace, const BufferizationOptions &)>;
278 // Produce a MemorySpace attribute from a tensor type
279 using DefaultMemorySpaceFn =
280 std::function<std::optional<Attribute>(TensorLikeType t)>;
281
282 /// Resolve a mismatch between buffer types that were independently inferred,
283 /// which results in a conflict at the "merge" point. Returns `failure()` to
284 /// signal bufferization failure; returns a buffer-like type when
285 /// reconciliation suceeded.
286 using ReconcileBufferTypeMismatchFn = std::function<FailureOr<BufferLikeType>(
287 BufferLikeType, BufferLikeType, const BufferizationOptions &)>;
288
289 BufferizationOptions();
290
291 /// Try to cast the given op to BufferizableOpInterface if the op is allow
292 /// listed.
293 BufferizableOpInterface dynCastBufferizableOp(Operation *op) const;
294
295 /// Try to cast the given value to BufferizableOpInterface if the op is allow
296 /// listed.
297 BufferizableOpInterface dynCastBufferizableOp(Value value) const;
298
299 /// A filter that specifies which ops should be bufferized and which ops
300 /// should be ignored.
301 OpFilter opFilter;
302
303 /// Return `true` if the given op should be bufferized.
304 bool isOpAllowed(Operation *op) const;
305
306 /// Specifies whether not bufferizable ops are allowed in the input. If so,
307 /// bufferization.to_buffer and bufferization.to_tensor ops are inserted at
308 /// the boundaries.
309 bool allowUnknownOps = false;
310
311 /// Specifies whether function boundaries (ops in the func dialect) should be
312 /// bufferized or not.
313 bool bufferizeFunctionBoundaries = false;
314
315 // Specifies whether to account for parallel regions in RaW analysis. If true,
316 // then writes inside of parallel regions that write to buffers defined
317 // outside of the parallel region will be given a new buffer.
318 bool checkParallelRegions = true;
319
320 /// This function controls buffer types on function signatures. Sets
321 /// `functionArgTypeConverterFn` and `inferFunctionResultLayout` accordingly.
322 ///
323 /// * InferLayoutMap: All function parameter types have a fully dynamic layout
324 /// map, but function result types are inferred from the body of the
325 /// function.
326 /// * FullyDynamicLayoutMap: All function parameter types and result types
327 /// have a fully dynamic layout map. This option is most efficient because
328 /// any layout map can be casted to a fully dynamic one.
329 /// * IdentityLayoutMap: All function parameter types and result types have a
330 /// static identity layout (i.e., no layout map). This option may introduce
331 /// additional buffer allocs and copies because layout maps cannot be casted
332 /// away.
333 ///
334 /// Note: Inferred layout maps may not be desireable when interacting with
335 /// external functions, because the generated function signatures will be less
336 /// predictable.
337 void setFunctionBoundaryTypeConversion(LayoutMapOption layoutMapOption);
338
339 /// Create a memref allocation with the given type and dynamic extents.
340 AllocationFn allocationFn = nullptr;
341
342 /// Creates a memcpy between two given buffers.
343 MemCpyFn memCpyFn = nullptr;
344
345 /// Creates a cast function from a buffer value to a new type.
346 CastFn castFn = nullptr;
347
348 /// Type conversion from tensors to buffers. This type conversion is used to
349 /// determine bufferized function argument and result types.
350 ///
351 /// By default, if tensor is a (builtin) tensor type, it is converted to a
352 /// memref type with a fully dynamic layout map; if tensor is a (generic)
353 /// tensor-like type, it is converted using unknownTypeConverterFn.
354 ///
355 /// If `bufferizeFunctionBoundaries` is not set, this function isn't used.
356 FunctionArgTypeConverterFn functionArgTypeConverterFn = nullptr;
357
358 /// If true, function result types are inferred from the body of the function.
359 /// Otherwise, function result type is determined by
360 /// `functionArgTypeConverterFn`.
361 ///
362 /// If `bufferizeFunctionBoundaries` is not set, this flag has no effect.
363 bool inferFunctionResultLayout = true;
364
365 /// Type conversion from tensors to memrefs. This type conversion is used if
366 /// no memref type could be inferred during bufferization. By default, returns
367 /// a memref type with a fully dynamic layout map.
368 UnknownTypeConverterFn unknownTypeConverterFn = nullptr;
369
370 // Use during type conversion to determine the memory space for memref based
371 // on the original tensor type if the memory space cannot be inferred.
372 // Returning std::nullopt will cause bufferization to fail (useful to indicate
373 // failure to determine memory space for a tensor type).
374 DefaultMemorySpaceFn defaultMemorySpaceFn =
375 [](TensorLikeType t) -> std::optional<Attribute> { return Attribute(); };
376
377 /// Hook to resolve a mismatch between conflicting buffer types that were
378 /// independently inferred and have to now "converge" to a common buffer type
379 /// (e.g. due to differences in iterations of a loop or branches of
380 /// if-statements). Depending on the situation and the types involved, this
381 /// may produce a "joined" type (e.g. a type combining properties of both), or
382 /// either one of the two types, etc. The default keeps the framework
383 /// behavior: promote to fully-dynamic layout on layout mismatch, fail on
384 /// memory-space mismatch.
385 ReconcileBufferTypeMismatchFn reconcileBufferTypeMismatchFn = nullptr;
386
387 /// If set to `true`, the analysis is skipped. A buffer is copied before every
388 /// write. This flag cannot be used together with `testAnalysisOnly = true`.
389 bool copyBeforeWrite = false;
390
391 /// If set to `true`, does not modify the IR apart from adding attributes (for
392 /// checking the results of the analysis) and post analysis steps.
393 bool testAnalysisOnly = false;
394
395 /// If set to `true`, the IR is annotated with details about RaW conflicts.
396 /// For debugging only. Should be used together with `testAnalysisOnly`.
397 bool printConflicts = false;
398
399 /// Buffer alignment for new memory allocations.
400 unsigned int bufferAlignment = 64;
401
402 /// Initializer functions for analysis state. These can be used to
403 /// initialize dialect-specific analysis state.
404 SmallVector<AnalysisStateInitFn> stateInitializers;
405};
406
407/// Traversal parameters for `findValueInReverseUseDefChain`.
408struct TraversalConfig {
409 /// Specifies if leaves (that do not have further OpOperands to follow)
410 /// should be returned even if they do not match the specified filter.
411 bool alwaysIncludeLeaves = true;
412
413 /// Specifies whether out-of-place/undecided OpOperands should be followed.
414 bool followInPlaceOnly = false;
415
416 /// Specifies whether non-equivalent OpOperands should be followed.
417 bool followEquivalentOnly = false;
418
419 /// Specifies whether unknown/non-bufferizable/ops not included in the
420 /// OpFilter of BufferizationOptions should be followed.
421 bool followUnknownOps = false;
422
423 /// Specifies whether OpOperands with a different type that are not the result
424 /// of a CastOpInterface op should be followed.
425 bool followSameTypeOrCastsOnly = false;
426
427 /// Specifies whether already visited values should be visited again.
428 /// (Note: This can result in infinite looping.)
429 bool revisitAlreadyVisitedValues = false;
430};
431
432/// AnalysisState provides a variety of helper functions for dealing with
433/// tensor values.
434class AnalysisState {
435public:
436 /// Determine which OpOperand* will alias with `value` if the op is
437 /// bufferized in place. Return all tensor OpOperand* if the op is not
438 /// bufferizable.
439 AliasingOpOperandList getAliasingOpOperands(Value value) const;
440
441 /// Determine which Value will alias with `opOperand` if the op is bufferized
442 /// in place. Return all tensor Values if the op is not bufferizable.
443 AliasingValueList getAliasingValues(OpOperand &opOperand) const;
444
445 /// Return true if `opOperand` bufferizes to a memory read. Return `true` if
446 /// the op is not bufferizable.
447 bool bufferizesToMemoryRead(OpOperand &opOperand) const;
448
449 /// Return true if `opOperand` bufferizes to a memory write. Return true` if
450 /// the op is not bufferizable.
451 bool bufferizesToMemoryWrite(OpOperand &opOperand) const;
452
453 /// Return true if the given `value` bufferizes to a memory write. Return
454 /// true if the value is a block argument. Return `true` if the defining op is
455 /// not bufferizable. Otherwise, consult the BufferizableOpInterface.
456 bool bufferizesToMemoryWrite(Value value) const;
457
458 /// Return true if `opOperand` does neither read nor write but bufferizes to
459 /// an alias. Return false if the op is not bufferizable.
460 bool bufferizesToAliasOnly(OpOperand &opOperand) const;
461
462 /// Return true if a copy can always be avoided when allocating a new tensor
463 /// for the given OpOperand.
464 bool canOmitTensorCopy(OpOperand &opOperand) const;
465
466 /// Return true if the given value is read by an op that bufferizes to a
467 /// memory read. Also takes into account ops that create an alias but do not
468 /// read by themselves (e.g., ExtractSliceOp).
469 bool isValueRead(Value value) const;
470
471 /// Starting from `opOperand`, follow the use-def chain in reverse, always
472 /// selecting the aliasing OpOperands. Find and return Values for which
473 /// `condition` evaluates to true. OpOperands of such matching Values are not
474 /// traversed any further, the visited aliasing opOperands will be preserved
475 /// through `visitedOpOperands`.
476 ///
477 /// When reaching the end of a chain, also return the last Value of that
478 /// chain if `config.alwaysIncludeLeaves` is set.
479 ///
480 /// Example:
481 ///
482 /// 8
483 /// |
484 /// 6* 7* +-----+----+
485 /// | | | |
486 /// 2* 3 4* 5
487 /// | | | |
488 /// +----------+----------+----------+
489 /// |
490 /// 1
491 ///
492 /// In the above example, Values with a star satisfy the condition. When
493 /// starting the traversal from Value 1, the resulting SetVector is:
494 /// { 2, 7, 8, 5 }
495 ///
496 /// Additional stopping conditions for the traversal can be specified in
497 /// `config`.
498 SetVector<Value> findValueInReverseUseDefChain(
499 OpOperand *opOperand, llvm::function_ref<bool(Value)> condition,
500 TraversalConfig config = TraversalConfig(),
501 llvm::DenseSet<OpOperand *> *visitedOpOperands = nullptr) const;
502
503 /// Find the values that may define the contents of the given value at
504 /// runtime. A block argument is always a definition. An OpResult is a
505 /// definition if it bufferizes to memory write. If it does not bufferize to
506 /// a memory write but has aliasing operands, we continue the lookup on these
507 /// values.
508 ///
509 /// Example: %r = tensor.insert %f into %t[%c0] : tensor<?xf32>
510 /// findDefinitions(%r) = {%r} because %r bufferizes to memory write.
511 ///
512 /// Example: %r = tensor.empty() : tensor<10xf32>
513 /// findDefinitions(%r) = {} because tensor.empty does not the define the
514 /// contents of its result (i.e., it does not bufferize to a memory write)
515 /// and it has no aliasing OpOperands.
516 ///
517 /// Example:
518 /// %a = arith.constant ... : tensor<10xf32>
519 /// %b1 = tensor.insert %f into %t : tensor<50xf32>
520 /// %b2 = tensor.extract_slice %b1[0][10][1] : tensor<50xf32> tensor<10xf32>
521 /// %r = arith.select %cond, %a, %b : tensor<10xf32>
522 /// findDefinitions(%r) = {%a, %b1}. %r and %b2 are skipped (lookup continues
523 /// in the operands) because their defining ops do not define the contents of
524 /// the tensor.
525 ///
526 /// Example:
527 /// %a = tensor.empty() : tensor<10xf32>
528 /// %b = arith.constant ... : tensor<10xf32>
529 /// %r = arith.select %cond, %a, %b : tensor<10xf32>
530 /// findDefinitions(%r) = {%b}. %a is excluded because it does not define the
531 /// contents of the tensor.
532 ///
533 /// Note: OpResults of unknown ops are handled conservatively and assumed to
534 /// be definitions.
535 SetVector<Value> findDefinitions(OpOperand *opOperand) const;
536
537 /// Return `true` if the given OpResult has been decided to bufferize inplace.
538 virtual bool isInPlace(OpOperand &opOperand) const;
539
540 /// Return true if `v1` and `v2` bufferize to equivalent buffers.
541 virtual bool areEquivalentBufferizedValues(Value v1, Value v2) const;
542
543 /// Return true if `v1` and `v2` may bufferize to aliasing buffers.
544 virtual bool areAliasingBufferizedValues(Value v1, Value v2) const;
545
546 /// Return `true` if the given tensor has undefined contents.
547 virtual bool hasUndefinedContents(OpOperand *opOperand) const;
548
549 /// Return a reference to the BufferizationOptions.
550 const BufferizationOptions &getOptions() const { return options; }
551
552 AnalysisState(const BufferizationOptions &options);
553
554 // AnalysisState should be passed as a reference.
555 AnalysisState(const AnalysisState &) = delete;
556
557 virtual ~AnalysisState() = default;
558
559 static bool classof(const AnalysisState *base) { return true; }
560
561 TypeID getType() const { return type; }
562
563 /// Return the closest enclosing repetitive region around the given op.
564 Region *getEnclosingRepetitiveRegion(Operation *op,
565 const BufferizationOptions &options);
566
567 /// Return the closest enclosing repetitive region around the place where the
568 /// given value is defined.
569 Region *getEnclosingRepetitiveRegion(Value value,
570 const BufferizationOptions &options);
571
572 /// Return the closest enclosing repetitive region around the given block.
573 Region *getEnclosingRepetitiveRegion(Block *block,
574 const BufferizationOptions &options);
575
576 virtual void resetCache();
577
578 /// Checks whether `op0` and `op1` are inside mutually exclusive regions.
579 /// The logic defers to `mlir::insideMutuallyExclusiveRegions`, but the
580 /// result is cached.
581 bool insideMutuallyExclusiveRegions(Operation *op0, Operation *op1);
582
583protected:
584 AnalysisState(const BufferizationOptions &options, TypeID type);
585
586private:
587 /// A reference to current bufferization options.
588 const BufferizationOptions &options;
589
590 /// The type of analysis.
591 TypeID type;
592
593 /// Cache containing closest ancestor repetitive Region.
594 DenseMap<std::variant<Operation *, Block *, Region *, Value>, Region *>
595 enclosingRepetitiveRegionCache;
596
597 /// Cache that specifies whether the two operations are in mutually exclusive
598 /// regions.
599 DenseMap<std::pair<Operation *, Operation *>, bool>
600 insideMutuallyExclusiveRegionsCache;
601};
602
603/// BufferizationState provides information about the state of the IR during the
604/// bufferization process.
605class BufferizationState {
606public:
607 /// Get a reference to the collection of cached symbol tables.
608 SymbolTableCollection &getSymbolTables();
609 /// Const overload so callers can reuse the cache from a const state.
610 SymbolTableCollection &getSymbolTables() const;
611
612private:
613 /// The cached symbol tables.
614 /// The user is expected to update / invalidate the cached symbol tables if
615 /// the bufferized operation has the Symbol or SymbolTable traits.
616 mutable SymbolTableCollection symbolTables;
617};
618
619/// Create an AllocTensorOp for the given shaped value (memref or tensor).
620/// If `copy` is set, the shaped value is copied. Otherwise, a tensor with
621/// undefined contents is allocated.
622FailureOr<Value>
623allocateTensorForShapedValue(OpBuilder &b, Location loc, Value shapedValue,
624 const BufferizationOptions &options,
625 const BufferizationState &state, bool copy = true);
626
627/// Lookup the buffer for the given value. If the value was not bufferized
628/// yet, wrap it in a ToBufferOp. Otherwise, it is the result of a ToTensorOp,
629/// from which the memref operand is returned.
630FailureOr<Value> getBuffer(RewriterBase &rewriter, Value value,
631 const BufferizationOptions &options,
632 const BufferizationState &state);
633
634/// Return the buffer type for a given Value (tensor) after bufferization
635/// without bufferizing any IR.
636///
637/// Note: It should be sufficient to call `getBuffer()->getType()` in most
638/// cases. However, when a buffer type should be predicted without modifying any
639/// IR, this function can be used.
640///
641/// This function is a wrapper around BufferizableOpInterface::getBufferType.
642FailureOr<BufferLikeType> getBufferType(Value value,
643 const BufferizationOptions &options,
644 const BufferizationState &state);
645
646/// Return the buffer type for a given Value (tensor) after bufferization
647/// without bufferizing any IR. This function (and not the other overload
648/// without `invocationStack`) can be used from `getBufferType` implementations
649/// of the `BufferizableOpInterface`.
650///
651/// Note: It should be sufficient to call `getBuffer()->getType()` in most
652/// cases. However, when a buffer type should be predicted without modifying any
653/// IR, this function can be used.
654///
655/// This function is a wrapper around `BufferizableOpInterface::getBufferType`.
656FailureOr<BufferLikeType> getBufferType(Value value,
657 const BufferizationOptions &options,
658 const BufferizationState &state,
659 SmallVector<Value> &invocationStack);
660
661/// Return "true" if the given op has tensor semantics and should be bufferized.
662/// If the op is bufferizable, the BufferizableOpInterface is queried.
663/// Otherwise, an op has tensor semantics if it has tensor operands, tensor
664/// op results and/or tensor block arguments.
665bool hasTensorSemantics(Operation *op);
666
667/// Replace an op with replacement values. The op is deleted. Tensor OpResults
668/// must be replaced with memref values.
669void replaceOpWithBufferizedValues(RewriterBase &rewriter, Operation *op,
670 ValueRange values);
671
672/// Replace an op with a new op. The new op must have the same number of
673/// results as the replaced op. The new op may not return any tensor values.
674template <typename OpTy, typename... Args>
675OpTy replaceOpWithNewBufferizedOp(RewriterBase &rewriter, Operation *op,
676 Args &&...args) {
677 auto newOp =
678 OpTy::create(rewriter, op->getLoc(), std::forward<Args>(args)...);
679 replaceOpWithBufferizedValues(rewriter, op, newOp->getResults());
680 return newOp;
681}
682
683/// Return a MemRef type with fully dynamic layout. If the given tensor type
684/// is unranked, return an unranked MemRef type.
685BaseMemRefType
686getMemRefTypeWithFullyDynamicLayout(TensorType tensorType,
687 Attribute memorySpace = nullptr);
688
689/// Return a MemRef type with a static identity layout (i.e., no layout map). If
690/// the given tensor type is unranked, return an unranked MemRef type.
691BaseMemRefType
692getMemRefTypeWithStaticIdentityLayout(TensorType tensorType,
693 Attribute memorySpace = nullptr);
694
695/// Return the owner of the given value. In case of a BlockArgument that is the
696/// owner of the block. In case of an OpResult that is the defining op.
697Operation *getOwnerOfValue(Value value);
698
699/// Assuming that the given region is repetitive, find the next enclosing
700/// repetitive region.
701Region *getNextEnclosingRepetitiveRegion(Region *region,
702 const BufferizationOptions &options);
703
704/// If `region` is a parallel region, return `region`. Otherwise, find the first
705/// enclosing parallel region of `region`. If there is no such region, return
706/// "nullptr".
707///
708/// Note: Whether a region is parallel or sequential is queried from the
709/// `BufferizableOpInterface`.
710Region *getParallelRegion(Region *region, const BufferizationOptions &options);
711
712namespace detail {
713/// This is the default implementation of
714/// BufferizableOpInterface::getAliasingOpOperands. Should not be called from
715/// other places.
716AliasingOpOperandList defaultGetAliasingOpOperands(Value value,
717 const AnalysisState &state);
718
719/// This is the default implementation of
720/// BufferizableOpInterface::getBufferType. Should not be called from other
721/// places.
722FailureOr<BufferLikeType>
723defaultGetBufferType(Value value, const BufferizationOptions &options,
724 const BufferizationState &state,
725 SmallVector<Value> &invocationStack);
726
727/// This is the default implementation of
728/// BufferizableOpInterface::resultBufferizesToMemoryWrite. Should not be called
729/// from other places.
730bool defaultResultBufferizesToMemoryWrite(OpResult opResult,
731 const AnalysisState &state);
732
733/// This is the default implementation of
734/// BufferizableOpInterface::isRepetitiveRegion. Should not be called from other
735/// places.
736bool defaultIsRepetitiveRegion(BufferizableOpInterface bufferizableOp,
737 unsigned index);
738
739/// This is the default implementation of getAliasingOpOperands in case the
740/// defining op does not implement the BufferizableOpInterface.
741AliasingOpOperandList unknownGetAliasingOpOperands(Value value);
742
743/// This is the default implementation of getAliasingValues in case the owner
744/// op does not implement the BufferizableOpInterface.
745AliasingValueList unknownGetAliasingValues(OpOperand &opOperand);
746
747/// This is the default implementation of
748/// BufferizableOpInterface::hasTensorSemantics
749bool defaultHasTensorSemantics(Operation *op);
750
751/// This is a helper function used when buffer type is guaranteed to be memref.
752/// It performs two actions: failure state checking and an explicit llvm::cast<>
753/// from the buffer-like type interface to a BaseMemRefType. This allows easier
754/// management of differences in C++ types at the API boundaries. Valid buffer
755/// type is casted to the memref type. Otherwise, the failure state is
756/// propagated i.e. asMemRefType(mlir::failure()) returns mlir::failure().
757FailureOr<BaseMemRefType> asMemRefType(FailureOr<BufferLikeType> bufferType);
758
759/// This function is a free-standing helper that relies on
760/// bufferization::TensorLikeTypeInterface to verify the types in tensor and
761/// buffer worlds match.
762bool typesMatchAfterBufferization(Operation &op, Value tensor, Value buffer);
763} // namespace detail
764
765} // namespace bufferization
766} // namespace mlir
767
768MLIR_DECLARE_EXPLICIT_TYPE_ID(mlir::bufferization::AnalysisState)
769
770//===----------------------------------------------------------------------===//
771// Bufferization Interfaces
772//===----------------------------------------------------------------------===//
773
774#include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h.inc"
775
776#endif // MLIR_DIALECT_BUFFERIZATION_IR_BUFFERIZABLEOPINTERFACE_H_
bufferization::BufferResultsToOutParamsOpts::AllocationFn AllocationFn
bufferization::BufferResultsToOutParamsOpts::MemCpyFn MemCpyFn
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static llvm::ManagedStatic< PassManagerOptions > options
static RankedTensorType getBufferType(const SparseTensorType &stt, bool needTmpCOO)
#define MLIR_DECLARE_EXPLICIT_TYPE_ID(CLASS_NAME)
Definition TypeID.h:321
static Operation * getOwnerOfValue(Value value)
This class helps build Operations.
Definition Builders.h:209
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
bool insideMutuallyExclusiveRegions(Operation *a, Operation *b)
Return true if a and b are in mutually exclusive regions as per RegionBranchOpInterface.
Region * getEnclosingRepetitiveRegion(Operation *op)
Return the first enclosing region of the given op that may be executed repetitively as per RegionBran...