MLIR  22.0.0git
OwnershipBasedBufferDeallocation.cpp
Go to the documentation of this file.
1 //===- OwnershipBasedBufferDeallocation.cpp - impl. for buffer dealloc. ---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements logic for computing correct `bufferization.dealloc`
10 // positions. Furthermore, buffer deallocation also adds required new clone
11 // operations to ensure that memrefs returned by functions never alias an
12 // argument.
13 //
14 // TODO:
15 // The current implementation does not support explicit-control-flow loops and
16 // the resulting code will be invalid with respect to program semantics.
17 // However, structured control-flow loops are fully supported.
18 //
19 //===----------------------------------------------------------------------===//
20 
28 #include "mlir/IR/Iterators.h"
30 
31 namespace mlir {
32 namespace bufferization {
33 #define GEN_PASS_DEF_OWNERSHIPBASEDBUFFERDEALLOCATIONPASS
34 #include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
35 } // namespace bufferization
36 } // namespace mlir
37 
38 using namespace mlir;
39 using namespace mlir::bufferization;
40 
41 //===----------------------------------------------------------------------===//
42 // Helpers
43 //===----------------------------------------------------------------------===//
44 
45 static Value buildBoolValue(OpBuilder &builder, Location loc, bool value) {
46  return arith::ConstantOp::create(builder, loc, builder.getBoolAttr(value));
47 }
48 
49 static bool isMemref(Value v) { return isa<BaseMemRefType>(v.getType()); }
50 
51 /// Return "true" if the given op is guaranteed to have neither "Allocate" nor
52 /// "Free" side effects.
54  return !mightHaveEffect<MemoryEffects::Allocate>(op) &&
55  !mightHaveEffect<MemoryEffects::Free>(op);
56 }
57 
58 /// Return "true" if the given op has buffer semantics. I.e., it has buffer
59 /// operands, buffer results and/or buffer region entry block arguments.
60 static bool hasBufferSemantics(Operation *op) {
61  if (llvm::any_of(op->getOperands(), isMemref) ||
62  llvm::any_of(op->getResults(), isMemref))
63  return true;
64  for (Region &region : op->getRegions())
65  if (!region.empty())
66  if (llvm::any_of(region.front().getArguments(), isMemref))
67  return true;
68  return false;
69 }
70 
71 //===----------------------------------------------------------------------===//
72 // Backedges analysis
73 //===----------------------------------------------------------------------===//
74 
75 namespace {
76 
77 /// A straight-forward program analysis which detects loop backedges induced by
78 /// explicit control flow.
79 class Backedges {
80 public:
81  using BlockSetT = SmallPtrSet<Block *, 16>;
82  using BackedgeSetT = llvm::DenseSet<std::pair<Block *, Block *>>;
83 
84 public:
85  /// Constructs a new backedges analysis using the op provided.
86  Backedges(Operation *op) { recurse(op); }
87 
88  /// Returns the number of backedges formed by explicit control flow.
89  size_t size() const { return edgeSet.size(); }
90 
91  /// Returns the start iterator to loop over all backedges.
92  BackedgeSetT::const_iterator begin() const { return edgeSet.begin(); }
93 
94  /// Returns the end iterator to loop over all backedges.
95  BackedgeSetT::const_iterator end() const { return edgeSet.end(); }
96 
97 private:
98  /// Enters the current block and inserts a backedge into the `edgeSet` if we
99  /// have already visited the current block. The inserted edge links the given
100  /// `predecessor` with the `current` block.
101  bool enter(Block &current, Block *predecessor) {
102  bool inserted = visited.insert(&current).second;
103  if (!inserted)
104  edgeSet.insert(std::make_pair(predecessor, &current));
105  return inserted;
106  }
107 
108  /// Leaves the current block.
109  void exit(Block &current) { visited.erase(&current); }
110 
111  /// Recurses into the given operation while taking all attached regions into
112  /// account.
113  void recurse(Operation *op) {
114  Block *current = op->getBlock();
115  // If the current op implements the `BranchOpInterface`, there can be
116  // cycles in the scope of all successor blocks.
117  if (isa<BranchOpInterface>(op)) {
118  for (Block *succ : current->getSuccessors())
119  recurse(*succ, current);
120  }
121  // Recurse into all distinct regions and check for explicit control-flow
122  // loops.
123  for (Region &region : op->getRegions()) {
124  if (!region.empty())
125  recurse(region.front(), current);
126  }
127  }
128 
129  /// Recurses into explicit control-flow structures that are given by
130  /// the successor relation defined on the block level.
131  void recurse(Block &block, Block *predecessor) {
132  // Try to enter the current block. If this is not possible, we are
133  // currently processing this block and can safely return here.
134  if (!enter(block, predecessor))
135  return;
136 
137  // Recurse into all operations and successor blocks.
138  for (Operation &op : block.getOperations())
139  recurse(&op);
140 
141  // Leave the current block.
142  exit(block);
143  }
144 
145  /// Stores all blocks that are currently visited and on the processing stack.
146  BlockSetT visited;
147 
148  /// Stores all backedges in the format (source, target).
149  BackedgeSetT edgeSet;
150 };
151 
152 } // namespace
153 
154 //===----------------------------------------------------------------------===//
155 // BufferDeallocation
156 //===----------------------------------------------------------------------===//
157 
158 namespace {
159 /// The buffer deallocation transformation which ensures that all allocs in the
160 /// program have a corresponding de-allocation.
161 class BufferDeallocation {
162 public:
163  BufferDeallocation(Operation *op, DeallocationOptions options,
164  SymbolTableCollection &symbolTables)
165  : state(op, symbolTables), options(options) {}
166 
167  /// Performs the actual placement/creation of all dealloc operations.
168  LogicalResult deallocate(FunctionOpInterface op);
169 
170 private:
171  /// The base case for the recursive template below.
172  template <typename... T>
173  typename std::enable_if<sizeof...(T) == 0, FailureOr<Operation *>>::type
174  handleOp(Operation *op) {
175  return op;
176  }
177 
178  /// Applies all the handlers of the interfaces in the template list
179  /// implemented by 'op'. In particular, if an operation implements more than
180  /// one of the interfaces in the template list, all the associated handlers
181  /// will be applied to the operation in the same order as the template list
182  /// specifies. If a handler reports a failure or removes the operation without
183  /// replacement (indicated by returning 'nullptr'), no further handlers are
184  /// applied and the return value is propagated to the caller of 'handleOp'.
185  ///
186  /// The interface handlers job is to update the deallocation state, most
187  /// importantly the ownership map and list of memrefs to potentially be
188  /// deallocated per block, but also to insert `bufferization.dealloc`
189  /// operations where needed. Obviously, no MemRefs that may be used at a later
190  /// point in the control-flow may be deallocated and the ownership map has to
191  /// be updated to reflect potential ownership changes caused by the dealloc
192  /// operation (e.g., if two interfaces on the same op insert a dealloc
193  /// operation each, the second one should query the ownership map and use them
194  /// as deallocation condition such that MemRefs already deallocated in the
195  /// first dealloc operation are not deallocated a second time (double-free)).
196  /// Note that currently only the interfaces on terminators may insert dealloc
197  /// operations and it is verified as a precondition that a terminator op must
198  /// implement exactly one of the interfaces handling dealloc insertion.
199  ///
200  /// The return value of the 'handleInterface' functions should be a
201  /// FailureOr<Operation *> indicating whether there was a failure or otherwise
202  /// returning the operation itself or a replacement operation.
203  ///
204  /// Note: The difference compared to `TypeSwitch` is that all
205  /// matching cases are applied instead of just the first match.
206  template <typename InterfaceT, typename... InterfacesU>
207  FailureOr<Operation *> handleOp(Operation *op) {
208  Operation *next = op;
209  if (auto concreteOp = dyn_cast<InterfaceT>(op)) {
210  FailureOr<Operation *> result = handleInterface(concreteOp);
211  if (failed(result))
212  return failure();
213  next = *result;
214  }
215  if (!next)
216  return FailureOr<Operation *>(nullptr);
217  return handleOp<InterfacesU...>(next);
218  }
219 
220  /// Apply all supported interface handlers to the given op.
221  FailureOr<Operation *> handleAllInterfaces(Operation *op) {
222  if (auto deallocOpInterface = dyn_cast<BufferDeallocationOpInterface>(op))
223  return deallocOpInterface.process(state, options);
224 
225  if (failed(verifyOperationPreconditions(op)))
226  return failure();
227 
228  return handleOp<MemoryEffectOpInterface, RegionBranchOpInterface,
229  CallOpInterface, BranchOpInterface,
230  RegionBranchTerminatorOpInterface>(op);
231  }
232 
233  /// Make sure that for each forwarded MemRef value, an ownership indicator
234  /// `i1` value is forwarded as well such that the successor block knows
235  /// whether the MemRef has to be deallocated.
236  ///
237  /// Example:
238  /// ```
239  /// ^bb1:
240  /// <more ops...>
241  /// cf.br ^bb2(<forward-to-bb2>)
242  /// ```
243  /// becomes
244  /// ```
245  /// // let (m, c) = getMemrefsAndConditionsToDeallocate(bb1)
246  /// // let r = getMemrefsToRetain(bb1, bb2, <forward-to-bb2>)
247  /// ^bb1:
248  /// <more ops...>
249  /// o = bufferization.dealloc m if c retain r
250  /// // replace ownership(r) with o element-wise
251  /// cf.br ^bb2(<forward-to-bb2>, o)
252  /// ```
253  FailureOr<Operation *> handleInterface(BranchOpInterface op);
254 
255  /// Add an ownership indicator for every forwarding MemRef operand and result.
256  /// Nested regions never take ownership of MemRefs owned by a parent region
257  /// (neither via forwarding operand nor when captured implicitly when the
258  /// region is not isolated from above). Ownerships will only be passed to peer
259  /// regions (when an operation has multiple regions, such as scf.while), or to
260  /// parent regions.
261  /// Note that the block arguments in the nested region are currently handled
262  /// centrally in the 'dealloc' function, but better interface support could
263  /// allow us to do this here for the nested region specifically to reduce the
264  /// amount of assumptions we make on the structure of ops implementing this
265  /// interface.
266  ///
267  /// Example:
268  /// ```
269  /// %ret = scf.for %i = %c0 to %c10 step %c1 iter_args(%m = %memref) {
270  /// <more ops...>
271  /// scf.yield %m : memref<2xi32>, i1
272  /// }
273  /// ```
274  /// becomes
275  /// ```
276  /// %ret:2 = scf.for %i = %c0 to %c10 step %c1
277  /// iter_args(%m = %memref, %own = %false) {
278  /// <more ops...>
279  /// // Note that the scf.yield is handled by the
280  /// // RegionBranchTerminatorOpInterface (not this handler)
281  /// // let o = getMemrefWithUniqueOwnership(%own)
282  /// scf.yield %m, o : memref<2xi32>, i1
283  /// }
284  /// ```
285  FailureOr<Operation *> handleInterface(RegionBranchOpInterface op);
286 
287  /// If the private-function-dynamic-ownership pass option is enabled and the
288  /// called function is private, additional results are added for each MemRef
289  /// result to pass the dynamic ownership indicator along. Otherwise, updates
290  /// the ownership map and list of memrefs to be deallocated according to the
291  /// function boundary ABI, i.e., assume ownership of all returned MemRefs.
292  ///
293  /// Example (assume `private-function-dynamic-ownership` is enabled):
294  /// ```
295  /// func.func @f(%arg0: memref<2xi32>) -> memref<2xi32> {...}
296  /// func.func private @g(%arg0: memref<2xi32>) -> memref<2xi32> {...}
297  ///
298  /// %ret_f = func.call @f(%memref) : (memref<2xi32>) -> memref<2xi32>
299  /// %ret_g = func.call @g(%memref) : (memref<2xi32>) -> memref<2xi32>
300  /// ```
301  /// becomes
302  /// ```
303  /// func.func @f(%arg0: memref<2xi32>) -> memref<2xi32> {...}
304  /// func.func private @g(%arg0: memref<2xi32>) -> (memref<2xi32>, i1) {...}
305  ///
306  /// %ret_f = func.call @f(%memref) : (memref<2xi32>) -> memref<2xi32>
307  /// // set ownership(%ret_f) := true
308  /// // remember to deallocate %ret_f
309  ///
310  /// %ret_g:2 = func.call @g(%memref) : (memref<2xi32>) -> (memref<2xi32>, i1)
311  /// // set ownership(%ret_g#0) := %ret_g#1
312  /// // remember to deallocate %ret_g if it comes with ownership
313  /// ```
314  FailureOr<Operation *> handleInterface(CallOpInterface op);
315 
316  /// Takes care of allocation and free side-effects. It collects allocated
317  /// MemRefs that we have to add to manually deallocate, but also removes
318  /// values again that are already deallocated before the end of the block. It
319  /// also updates the ownership map accordingly.
320  ///
321  /// Example:
322  /// ```
323  /// %alloc = memref.alloc()
324  /// %alloca = memref.alloca()
325  /// ```
326  /// becomes
327  /// ```
328  /// %alloc = memref.alloc()
329  /// %alloca = memref.alloca()
330  /// // set ownership(alloc) := true
331  /// // set ownership(alloca) := false
332  /// // remember to deallocate %alloc
333  /// ```
334  FailureOr<Operation *> handleInterface(MemoryEffectOpInterface op);
335 
336  /// Takes care that the function boundary ABI is adhered to if the parent
337  /// operation implements FunctionOpInterface, inserting a
338  /// `bufferization.clone` if necessary, and inserts the
339  /// `bufferization.dealloc` operation according to the ops operands.
340  ///
341  /// Example:
342  /// ```
343  /// ^bb1:
344  /// <more ops...>
345  /// func.return <return-vals>
346  /// ```
347  /// becomes
348  /// ```
349  /// // let (m, c) = getMemrefsAndConditionsToDeallocate(bb1)
350  /// // let r = getMemrefsToRetain(bb1, nullptr, <return-vals>)
351  /// ^bb1:
352  /// <more ops...>
353  /// o = bufferization.dealloc m if c retain r
354  /// func.return <return-vals>
355  /// (if !isFunctionWithoutDynamicOwnership: append o)
356  /// ```
357  FailureOr<Operation *> handleInterface(RegionBranchTerminatorOpInterface op);
358 
359  /// Construct a new operation which is exactly the same as the passed 'op'
360  /// except that the OpResults list is appended by new results of the passed
361  /// 'types'.
362  /// TODO: ideally, this would be implemented using an OpInterface because it
363  /// is used to append function results, loop iter_args, etc. and thus makes
364  /// some assumptions that the variadic list of those is at the end of the
365  /// OpResults range.
366  Operation *appendOpResults(Operation *op, ArrayRef<Type> types);
367 
368  /// A convenience template for the generic 'appendOpResults' function above to
369  /// avoid manual casting of the result.
370  template <typename OpTy>
371  OpTy appendOpResults(OpTy op, ArrayRef<Type> types) {
372  return cast<OpTy>(appendOpResults(op.getOperation(), types));
373  }
374 
375  /// Performs deallocation of a single basic block. This is a private function
376  /// because some internal data structures have to be set up beforehand and
377  /// this function has to be called on blocks in a region in dominance order.
378  LogicalResult deallocate(Block *block);
379 
380  /// After all relevant interfaces of an operation have been processed by the
381  /// 'handleInterface' functions, this function sets the ownership of operation
382  /// results that have not been set yet by the 'handleInterface' functions. It
383  /// generally assumes that each result can alias with every operand of the
384  /// operation, if there are MemRef typed results but no MemRef operands it
385  /// assigns 'false' as ownership. This happens, e.g., for the
386  /// memref.get_global operation. It would also be possible to query some alias
387  /// analysis to get more precise ownerships, however, the analysis would have
388  /// to be updated according to the IR modifications this pass performs (e.g.,
389  /// re-building operations to have more result values, inserting clone
390  /// operations, etc.).
391  void populateRemainingOwnerships(Operation *op);
392 
393  /// Given an SSA value of MemRef type, returns the same of a new SSA value
394  /// which has 'Unique' ownership where the ownership indicator is guaranteed
395  /// to be always 'true'.
396  Value materializeMemrefWithGuaranteedOwnership(OpBuilder &builder,
397  Value memref, Block *block);
398 
399  /// Returns whether the given operation implements FunctionOpInterface, has
400  /// private visibility, and the private-function-dynamic-ownership pass option
401  /// is enabled.
402  bool isFunctionWithoutDynamicOwnership(Operation *op);
403 
404  /// Given an SSA value of MemRef type, this function queries the
405  /// BufferDeallocationOpInterface of the defining operation of 'memref' for a
406  /// materialized ownership indicator for 'memref'. If the op does not
407  /// implement the interface or if the block for which the materialized value
408  /// is requested does not match the block in which 'memref' is defined, the
409  /// default implementation in
410  /// `DeallocationState::getMemrefWithUniqueOwnership` is queried instead.
411  std::pair<Value, Value>
412  materializeUniqueOwnership(OpBuilder &builder, Value memref, Block *block);
413 
414  /// Checks all the preconditions for operations implementing the
415  /// FunctionOpInterface that have to hold for the deallocation to be
416  /// applicable:
417  /// (1) Checks that there are not explicit control flow loops.
418  static LogicalResult verifyFunctionPreconditions(FunctionOpInterface op);
419 
420  /// Checks all the preconditions for operations inside the region of
421  /// operations implementing the FunctionOpInterface that have to hold for the
422  /// deallocation to be applicable:
423  /// (1) Checks if all operations that have at least one attached region
424  /// implement the RegionBranchOpInterface. This is not required in edge cases,
425  /// where we have a single attached region and the parent operation has no
426  /// results.
427  /// (2) Checks that no deallocations already exist. Especially deallocations
428  /// in nested regions are not properly supported yet since this requires
429  /// ownership of the memref to be transferred to the nested region, which does
430  /// not happen by default. This constrained can be lifted in the future.
431  /// (3) Checks that terminators with more than one successor except
432  /// `cf.cond_br` are not present and that either BranchOpInterface or
433  /// RegionBranchTerminatorOpInterface is implemented.
434  static LogicalResult verifyOperationPreconditions(Operation *op);
435 
436  /// When the 'private-function-dynamic-ownership' pass option is enabled,
437  /// additional `i1` return values are added for each MemRef result in the
438  /// function signature. This function takes care of updating the
439  /// `function_type` attribute of the function according to the actually
440  /// returned values from the terminators.
441  static LogicalResult updateFunctionSignature(FunctionOpInterface op);
442 
443 private:
444  /// Collects all analysis state and including liveness, caches, ownerships of
445  /// already processed values and operations, and the MemRefs that have to be
446  /// deallocated at the end of each block.
447  DeallocationState state;
448 
449  /// Collects all pass options in a single place.
451 };
452 
453 } // namespace
454 
455 //===----------------------------------------------------------------------===//
456 // BufferDeallocation Implementation
457 //===----------------------------------------------------------------------===//
458 
459 std::pair<Value, Value>
460 BufferDeallocation::materializeUniqueOwnership(OpBuilder &builder, Value memref,
461  Block *block) {
462  // The interface can only materialize ownership indicators in the same block
463  // as the defining op.
464  if (memref.getParentBlock() != block)
465  return state.getMemrefWithUniqueOwnership(builder, memref, block);
466 
467  Operation *owner = memref.getDefiningOp();
468  if (!owner)
469  owner = memref.getParentBlock()->getParentOp();
470 
471  // If the op implements the interface, query it for a materialized ownership
472  // value.
473  if (auto deallocOpInterface = dyn_cast<BufferDeallocationOpInterface>(owner))
474  return deallocOpInterface.materializeUniqueOwnershipForMemref(
475  state, options, builder, memref);
476 
477  // Otherwise use the default implementation.
478  return state.getMemrefWithUniqueOwnership(builder, memref, block);
479 }
480 
481 LogicalResult
482 BufferDeallocation::verifyFunctionPreconditions(FunctionOpInterface op) {
483  // (1) Ensure that there are supported loops only (no explicit control flow
484  // loops).
485  Backedges backedges(op);
486  if (backedges.size()) {
487  op->emitError("Only structured control-flow loops are supported.");
488  return failure();
489  }
490 
491  return success();
492 }
493 
494 LogicalResult BufferDeallocation::verifyOperationPreconditions(Operation *op) {
495  // We do not care about ops that do not operate on buffers and have no
496  // Allocate/Free side effect.
498  return success();
499 
500  // (1) The pass does not work properly when deallocations are already present.
501  // Alternatively, we could also remove all deallocations as a pre-pass.
502  if (isa<DeallocOp>(op))
503  return op->emitError(
504  "No deallocation operations must be present when running this pass!");
505 
506  // (2) Memory side effects of unregistered ops are unknown. In particular, we
507  // do not know whether an unregistered op allocates memory or not.
508  // - Ops with recursive memory effects are allowed. All nested ops in the
509  // regions of `op` will be analyzed separately.
510  // - Call ops are allowed even though they typically do not implement the
511  // MemoryEffectOpInterface. They usually do not have side effects apart
512  // from the callee, which will be analyzed separately. (This is similar to
513  // "recursive memory effects".)
514  if (hasUnknownEffects(op) && !isa<CallOpInterface>(op))
515  return op->emitError(
516  "ops with unknown memory side effects are not supported");
517 
518  // (3) Check that the control flow structures are supported.
519  auto regions = op->getRegions();
520  // Check that if the operation has at
521  // least one region it implements the RegionBranchOpInterface. If there
522  // is an operation that does not fulfill this condition, we cannot apply
523  // the deallocation steps. Furthermore, we accept cases, where we have a
524  // region that returns no results, since, in that case, the intra-region
525  // control flow does not affect the transformation.
526  size_t size = regions.size();
527  if (((size == 1 && !op->getResults().empty()) || size > 1) &&
528  !dyn_cast<RegionBranchOpInterface>(op)) {
529  return op->emitError("All operations with attached regions need to "
530  "implement the RegionBranchOpInterface.");
531  }
532 
533  // (3) Check that terminators with more than one successor except `cf.cond_br`
534  // are not present and that either BranchOpInterface or
535  // RegionBranchTerminatorOpInterface is implemented.
536  if (op->hasTrait<OpTrait::NoTerminator>())
537  return op->emitError("NoTerminator trait is not supported");
538 
539  if (op->hasTrait<OpTrait::IsTerminator>()) {
540  // Either one of those interfaces has to be implemented on terminators, but
541  // not both.
542  if (!isa<BranchOpInterface, RegionBranchTerminatorOpInterface>(op) ||
543  (isa<BranchOpInterface>(op) &&
544  isa<RegionBranchTerminatorOpInterface>(op)))
545 
546  return op->emitError(
547  "Terminators must implement either BranchOpInterface or "
548  "RegionBranchTerminatorOpInterface (but not both)!");
549 
550  // We only support terminators with 0 or 1 successors for now and
551  // special-case the conditional branch op.
552  if (op->getSuccessors().size() > 1)
553 
554  return op->emitError("Terminators with more than one successor "
555  "are not supported!");
556  }
557 
558  return success();
559 }
560 
561 LogicalResult
562 BufferDeallocation::updateFunctionSignature(FunctionOpInterface op) {
563  SmallVector<TypeRange> returnOperandTypes(llvm::map_range(
564  op.getFunctionBody().getOps<RegionBranchTerminatorOpInterface>(),
565  [](RegionBranchTerminatorOpInterface op) {
566  return op.getSuccessorOperands(RegionBranchPoint::parent()).getTypes();
567  }));
568  if (!llvm::all_equal(returnOperandTypes))
569  return op->emitError(
570  "there are multiple return operations with different operand types");
571 
572  TypeRange resultTypes = op.getResultTypes();
573  // Check if we found a return operation because that doesn't necessarily
574  // always have to be the case, e.g., consider a function with one block that
575  // has a cf.br at the end branching to itself again (i.e., an infinite loop).
576  // In that case we don't want to crash but just not update the return types.
577  if (!returnOperandTypes.empty())
578  resultTypes = returnOperandTypes[0];
579 
580  op.setFunctionTypeAttr(TypeAttr::get(FunctionType::get(
581  op->getContext(), op.getFunctionBody().front().getArgumentTypes(),
582  resultTypes)));
583 
584  return success();
585 }
586 
587 LogicalResult BufferDeallocation::deallocate(FunctionOpInterface op) {
588  // Stop and emit a proper error message if we don't support the input IR.
589  if (failed(verifyFunctionPreconditions(op)))
590  return failure();
591 
592  // Process the function block by block.
593  auto result = op->walk<WalkOrder::PostOrder, ForwardDominanceIterator<>>(
594  [&](Block *block) {
595  if (failed(deallocate(block)))
596  return WalkResult::interrupt();
597  return WalkResult::advance();
598  });
599  if (result.wasInterrupted())
600  return failure();
601 
602  // Update the function signature if the function is private, dynamic ownership
603  // is enabled, and the function has memrefs as arguments or results.
604  return updateFunctionSignature(op);
605 }
606 
607 LogicalResult BufferDeallocation::deallocate(Block *block) {
608  OpBuilder builder = OpBuilder::atBlockBegin(block);
609 
610  // Compute liveness transfers of ownership to this block.
611  SmallVector<Value> liveMemrefs;
612  state.getLiveMemrefsIn(block, liveMemrefs);
613  for (auto li : liveMemrefs) {
614  // Ownership of implicitly captured memrefs from other regions is never
615  // taken, but ownership of memrefs in the same region (but different block)
616  // is taken.
617  if (li.getParentRegion() == block->getParent()) {
618  state.updateOwnership(li, state.getOwnership(li, li.getParentBlock()),
619  block);
620  state.addMemrefToDeallocate(li, block);
621  continue;
622  }
623 
624  if (li.getParentRegion()->isProperAncestor(block->getParent())) {
625  Value falseVal = buildBoolValue(builder, li.getLoc(), false);
626  state.updateOwnership(li, falseVal, block);
627  }
628  }
629 
630  for (unsigned i = 0, e = block->getNumArguments(); i < e; ++i) {
631  BlockArgument arg = block->getArgument(i);
632  if (!isMemref(arg))
633  continue;
634 
635  // Adhere to function boundary ABI: no ownership of function argument
636  // MemRefs is taken.
637  if (isa<FunctionOpInterface>(block->getParentOp()) &&
638  block->isEntryBlock()) {
639  Value newArg = buildBoolValue(builder, arg.getLoc(), false);
640  state.updateOwnership(arg, newArg);
641  state.addMemrefToDeallocate(arg, block);
642  continue;
643  }
644 
645  // Pass MemRef ownerships along via `i1` values.
646  Value newArg = block->addArgument(builder.getI1Type(), arg.getLoc());
647  state.updateOwnership(arg, newArg);
648  state.addMemrefToDeallocate(arg, block);
649  }
650 
651  // For each operation in the block, handle the interfaces that affect aliasing
652  // and ownership of memrefs.
653  for (Operation &op : llvm::make_early_inc_range(*block)) {
654  FailureOr<Operation *> result = handleAllInterfaces(&op);
655  if (failed(result))
656  return failure();
657  if (!*result)
658  continue;
659 
660  populateRemainingOwnerships(*result);
661  }
662 
663  // TODO: if block has no terminator, handle dealloc insertion here.
664  return success();
665 }
666 
667 Operation *BufferDeallocation::appendOpResults(Operation *op,
668  ArrayRef<Type> types) {
669  SmallVector<Type> newTypes(op->getResultTypes());
670  newTypes.append(types.begin(), types.end());
671  auto *newOp = Operation::create(op->getLoc(), op->getName(), newTypes,
672  op->getOperands(), op->getAttrDictionary(),
673  op->getPropertiesStorage(),
674  op->getSuccessors(), op->getNumRegions());
675  for (auto [oldRegion, newRegion] :
676  llvm::zip(op->getRegions(), newOp->getRegions()))
677  newRegion.takeBody(oldRegion);
678 
679  OpBuilder(op).insert(newOp);
680  op->replaceAllUsesWith(newOp->getResults().take_front(op->getNumResults()));
681  op->erase();
682 
683  return newOp;
684 }
685 
686 FailureOr<Operation *>
687 BufferDeallocation::handleInterface(RegionBranchOpInterface op) {
688  OpBuilder builder = OpBuilder::atBlockBegin(op->getBlock());
689 
690  // TODO: the RegionBranchOpInterface does not provide all the necessary
691  // methods to perform this transformation without additional assumptions on
692  // the structure. In particular, that
693  // * additional values to be passed to the next region can be added to the end
694  // of the operand list, the end of the block argument list, and the end of
695  // the result value list. However, it seems to be the general guideline for
696  // operations implementing this interface to follow this structure.
697  // * and that the block arguments and result values match the forwarded
698  // operands one-to-one (i.e., that there are no other values appended to the
699  // front).
700  // These assumptions are satisfied by the `scf.if`, `scf.for`, and `scf.while`
701  // operations.
702 
704  op.getSuccessorRegions(RegionBranchPoint::parent(), regions);
705  assert(!regions.empty() && "Must have at least one successor region");
706  SmallVector<Value> entryOperands(
707  op.getEntrySuccessorOperands(regions.front()));
708  unsigned numMemrefOperands = llvm::count_if(entryOperands, isMemref);
709 
710  // No ownership is acquired for any MemRefs that are passed to the region from
711  // the outside.
712  Value falseVal = buildBoolValue(builder, op.getLoc(), false);
713  op->insertOperands(op->getNumOperands(),
714  SmallVector<Value>(numMemrefOperands, falseVal));
715 
716  int counter = op->getNumResults();
717  unsigned numMemrefResults = llvm::count_if(op->getResults(), isMemref);
718  SmallVector<Type> ownershipResults(numMemrefResults, builder.getI1Type());
719  RegionBranchOpInterface newOp = appendOpResults(op, ownershipResults);
720 
721  for (auto result : llvm::make_filter_range(newOp->getResults(), isMemref)) {
722  state.updateOwnership(result, newOp->getResult(counter++));
723  state.addMemrefToDeallocate(result, newOp->getBlock());
724  }
725 
726  return newOp.getOperation();
727 }
728 
729 Value BufferDeallocation::materializeMemrefWithGuaranteedOwnership(
730  OpBuilder &builder, Value memref, Block *block) {
731  // First, make sure we at least have 'Unique' ownership already.
732  std::pair<Value, Value> newMemrefAndOnwership =
733  materializeUniqueOwnership(builder, memref, block);
734  Value newMemref = newMemrefAndOnwership.first;
735  Value condition = newMemrefAndOnwership.second;
736 
737  // Avoid inserting additional IR if ownership is already guaranteed. In
738  // particular, this is already the case when we had 'Unknown' ownership
739  // initially and a clone was inserted to get to 'Unique' ownership.
740  if (matchPattern(condition, m_One()))
741  return newMemref;
742 
743  // Insert a runtime check and only clone if we still don't have ownership at
744  // runtime.
745  Value maybeClone = scf::IfOp::create(
746  builder, memref.getLoc(), condition,
747  [&](OpBuilder &builder, Location loc) {
748  scf::YieldOp::create(builder, loc, newMemref);
749  },
750  [&](OpBuilder &builder, Location loc) {
751  Value clone = bufferization::CloneOp::create(
752  builder, loc, newMemref);
753  scf::YieldOp::create(builder, loc, clone);
754  })
755  .getResult(0);
756  Value trueVal = buildBoolValue(builder, memref.getLoc(), true);
757  state.updateOwnership(maybeClone, trueVal);
758  state.addMemrefToDeallocate(maybeClone, maybeClone.getParentBlock());
759  return maybeClone;
760 }
761 
762 FailureOr<Operation *>
763 BufferDeallocation::handleInterface(BranchOpInterface op) {
764  if (op->getNumSuccessors() > 1)
765  return op->emitError("BranchOpInterface operations with multiple "
766  "successors are not supported yet");
767 
768  if (op->getNumSuccessors() != 1)
769  return emitError(op.getLoc(),
770  "only BranchOpInterface operations with exactly "
771  "one successor are supported yet");
772 
773  if (op.getSuccessorOperands(0).getProducedOperandCount() > 0)
774  return op.emitError("produced operands are not supported");
775 
776  // Collect the values to deallocate and retain and use them to create the
777  // dealloc operation.
778  Block *block = op->getBlock();
779  OpBuilder builder(op);
780  SmallVector<Value> memrefs, conditions, toRetain;
781  if (failed(state.getMemrefsAndConditionsToDeallocate(
782  builder, op.getLoc(), block, memrefs, conditions)))
783  return failure();
784 
785  OperandRange forwardedOperands =
786  op.getSuccessorOperands(0).getForwardedOperands();
787  state.getMemrefsToRetain(block, op->getSuccessor(0), forwardedOperands,
788  toRetain);
789 
790  auto deallocOp = bufferization::DeallocOp::create(
791  builder, op.getLoc(), memrefs, conditions, toRetain);
792 
793  // We want to replace the current ownership of the retained values with the
794  // result values of the dealloc operation as they are always unique.
795  state.resetOwnerships(deallocOp.getRetained(), block);
796  for (auto [retained, ownership] :
797  llvm::zip(deallocOp.getRetained(), deallocOp.getUpdatedConditions())) {
798  state.updateOwnership(retained, ownership, block);
799  }
800 
801  unsigned numAdditionalReturns = llvm::count_if(forwardedOperands, isMemref);
802  SmallVector<Value> newOperands(forwardedOperands);
803  auto additionalConditions =
804  deallocOp.getUpdatedConditions().take_front(numAdditionalReturns);
805  newOperands.append(additionalConditions.begin(), additionalConditions.end());
806  op.getSuccessorOperands(0).getMutableForwardedOperands().assign(newOperands);
807 
808  return op.getOperation();
809 }
810 
811 FailureOr<Operation *> BufferDeallocation::handleInterface(CallOpInterface op) {
812  OpBuilder builder(op);
813 
814  // Lookup the function operation and check if it has private visibility. If
815  // the function is referenced by SSA value instead of a Symbol, it's assumed
816  // to be public. (And we cannot easily change the type of the SSA value
817  // anyway.)
818  Operation *funcOp = op.resolveCallableInTable(state.getSymbolTable());
819  bool isPrivate = false;
820  if (auto symbol = dyn_cast_or_null<SymbolOpInterface>(funcOp))
821  isPrivate = symbol.isPrivate() && !symbol.isDeclaration();
822 
823  // If the private-function-dynamic-ownership option is enabled and we are
824  // calling a private function, we need to add an additional `i1` result for
825  // each MemRef result to dynamically pass the current ownership indicator
826  // rather than adhering to the function boundary ABI.
827  if (options.privateFuncDynamicOwnership && isPrivate) {
828  unsigned numMemrefs = llvm::count_if(op->getResults(), isMemref);
829  SmallVector<Type> ownershipTypesToAppend(numMemrefs, builder.getI1Type());
830  unsigned ownershipCounter = op->getNumResults();
831  op = appendOpResults(op, ownershipTypesToAppend);
832 
833  for (auto result : llvm::make_filter_range(op->getResults(), isMemref)) {
834  state.updateOwnership(result, op->getResult(ownershipCounter++));
835  state.addMemrefToDeallocate(result, result.getParentBlock());
836  }
837 
838  return op.getOperation();
839  }
840 
841  // According to the function boundary ABI we are guaranteed to get ownership
842  // of all MemRefs returned by the function. Thus we set ownership to constant
843  // 'true' and remember to deallocate it.
844  Value trueVal = buildBoolValue(builder, op.getLoc(), true);
845  for (auto result : llvm::make_filter_range(op->getResults(), isMemref)) {
846  state.updateOwnership(result, trueVal);
847  state.addMemrefToDeallocate(result, result.getParentBlock());
848  }
849 
850  return op.getOperation();
851 }
852 
853 FailureOr<Operation *>
854 BufferDeallocation::handleInterface(MemoryEffectOpInterface op) {
855  auto *block = op->getBlock();
856  OpBuilder builder = OpBuilder::atBlockBegin(block);
857 
858  for (auto operand : llvm::make_filter_range(op->getOperands(), isMemref)) {
859  if (op.getEffectOnValue<MemoryEffects::Free>(operand).has_value()) {
860  // The bufferization.manual_deallocation attribute can be attached to ops
861  // with an allocation and/or deallocation side effect. It indicates that
862  // the op is under a "manual deallocation" scheme. Deallocation ops are
863  // usually forbidden in the input IR (not supported by the buffer
864  // deallocation pass). However, if they are under manual deallocation,
865  // they can be safely ignored by the buffer deallocation pass.
866  if (!op->hasAttr(BufferizationDialect::kManualDeallocation))
867  return op->emitError(
868  "memory free side-effect on MemRef value not supported!");
869 
870  // Buffers that were allocated under "manual deallocation" may be
871  // manually deallocated. We insert a runtime assertion to cover certain
872  // cases of invalid IR where an automatically managed buffer allocation
873  // is manually deallocated. This is not a bulletproof check!
874  OpBuilder::InsertionGuard g(builder);
875  builder.setInsertionPoint(op);
876  Ownership ownership = state.getOwnership(operand, block);
877  if (ownership.isUnique()) {
878  Value ownershipInverted = arith::XOrIOp::create(
879  builder, op.getLoc(), ownership.getIndicator(),
880  buildBoolValue(builder, op.getLoc(), true));
881  cf::AssertOp::create(builder, op.getLoc(), ownershipInverted,
882  "expected that the block does not have ownership");
883  }
884  }
885  }
886 
887  for (auto res : llvm::make_filter_range(op->getResults(), isMemref)) {
888  auto allocEffect = op.getEffectOnValue<MemoryEffects::Allocate>(res);
889  if (allocEffect.has_value()) {
890  if (isa<SideEffects::AutomaticAllocationScopeResource>(
891  allocEffect->getResource())) {
892  // Make sure that the ownership of auto-managed allocations is set to
893  // false. This is important for operations that have at least one memref
894  // typed operand. E.g., consider an operation like `bufferization.clone`
895  // that lowers to a `memref.alloca + memref.copy` instead of a
896  // `memref.alloc`. If we wouldn't set the ownership of the result here,
897  // the default ownership population in `populateRemainingOwnerships`
898  // would assume aliasing with the MemRef operand.
899  state.resetOwnerships(res, block);
900  state.updateOwnership(res, buildBoolValue(builder, op.getLoc(), false));
901  continue;
902  }
903 
904  if (op->hasAttr(BufferizationDialect::kManualDeallocation)) {
905  // This allocation will be deallocated manually. Assign an ownership of
906  // "false", so that it will never be deallocated by the buffer
907  // deallocation pass.
908  state.resetOwnerships(res, block);
909  state.updateOwnership(res, buildBoolValue(builder, op.getLoc(), false));
910  continue;
911  }
912 
913  state.updateOwnership(res, buildBoolValue(builder, op.getLoc(), true));
914  state.addMemrefToDeallocate(res, block);
915  }
916  }
917 
918  return op.getOperation();
919 }
920 
921 FailureOr<Operation *>
922 BufferDeallocation::handleInterface(RegionBranchTerminatorOpInterface op) {
923  OpBuilder builder(op);
924 
925  // If this is a return operation of a function that is not private or the
926  // dynamic function boundary ownership is disabled, we need to return memref
927  // values for which we have guaranteed ownership to pass on to adhere to the
928  // function boundary ABI.
929  bool funcWithoutDynamicOwnership =
930  isFunctionWithoutDynamicOwnership(op->getParentOp());
931  if (funcWithoutDynamicOwnership) {
932  for (OpOperand &val : op->getOpOperands()) {
933  if (!isMemref(val.get()))
934  continue;
935 
936  val.set(materializeMemrefWithGuaranteedOwnership(builder, val.get(),
937  op->getBlock()));
938  }
939  }
940 
941  // TODO: getSuccessorRegions is not implemented by all operations we care
942  // about, but we would need to check how many successors there are and under
943  // which condition they are taken, etc.
944 
945  MutableOperandRange operands =
946  op.getMutableSuccessorOperands(RegionBranchPoint::parent());
947 
948  SmallVector<Value> updatedOwnerships;
950  state, op, operands.getAsOperandRange(), updatedOwnerships);
951  if (failed(result) || !*result)
952  return result;
953 
954  // Add an additional operand for every MemRef for the ownership indicator.
955  if (!funcWithoutDynamicOwnership) {
956  SmallVector<Value> newOperands{operands.getAsOperandRange()};
957  newOperands.append(updatedOwnerships.begin(), updatedOwnerships.end());
958  operands.assign(newOperands);
959  }
960 
961  return op.getOperation();
962 }
963 
964 bool BufferDeallocation::isFunctionWithoutDynamicOwnership(Operation *op) {
965  auto funcOp = dyn_cast<FunctionOpInterface>(op);
966  return funcOp && (!options.privateFuncDynamicOwnership ||
967  !funcOp.isPrivate() || funcOp.isExternal());
968 }
969 
970 void BufferDeallocation::populateRemainingOwnerships(Operation *op) {
971  for (auto res : op->getResults()) {
972  if (!isMemref(res))
973  continue;
974  if (!state.getOwnership(res, op->getBlock()).isUninitialized())
975  continue;
976 
977  // The op does not allocate memory, otherwise, it would have been assigned
978  // an ownership during `handleInterface`. Assume the result may alias with
979  // any memref operand and thus combine all their ownerships.
980  for (auto operand : op->getOperands()) {
981  if (!isMemref(operand))
982  continue;
983 
984  state.updateOwnership(
985  res, state.getOwnership(operand, operand.getParentBlock()),
986  op->getBlock());
987  }
988 
989  // If the ownership value is still uninitialized (e.g., because the op has
990  // no memref operands), assume that no ownership is taken. E.g., this is the
991  // case for "memref.get_global".
992  //
993  // Note: This can lead to memory leaks if memory side effects are not
994  // properly specified on the op.
995  if (state.getOwnership(res, op->getBlock()).isUninitialized()) {
996  OpBuilder builder(op);
997  state.updateOwnership(res, buildBoolValue(builder, op->getLoc(), false));
998  }
999  }
1000 }
1001 
1002 //===----------------------------------------------------------------------===//
1003 // OwnershipBasedBufferDeallocationPass
1004 //===----------------------------------------------------------------------===//
1005 
1006 namespace {
1007 
1008 /// The actual buffer deallocation pass that inserts and moves dealloc nodes
1009 /// into the right positions. Furthermore, it inserts additional clones if
1010 /// necessary. It uses the algorithm described at the top of the file.
1011 struct OwnershipBasedBufferDeallocationPass
1012  : public bufferization::impl::OwnershipBasedBufferDeallocationPassBase<
1013  OwnershipBasedBufferDeallocationPass> {
1014  using Base::Base;
1015 
1016  void runOnOperation() override {
1018  options.privateFuncDynamicOwnership = privateFuncDynamicOwnership;
1019 
1020  mlir::SymbolTableCollection symbolTables;
1021 
1022  auto status = getOperation()->walk([&](func::FuncOp func) {
1023  if (func.isExternal())
1024  return WalkResult::skip();
1025 
1026  if (failed(deallocateBuffersOwnershipBased(func, options, symbolTables)))
1027  return WalkResult::interrupt();
1028 
1029  return WalkResult::advance();
1030  });
1031  if (status.wasInterrupted())
1032  signalPassFailure();
1033  }
1034 };
1035 
1036 } // namespace
1037 
1038 //===----------------------------------------------------------------------===//
1039 // Implement bufferization API
1040 //===----------------------------------------------------------------------===//
1041 
1043  FunctionOpInterface op, DeallocationOptions options,
1044  SymbolTableCollection &symbolTables) {
1045  // Gather all required allocation nodes and prepare the deallocation phase.
1046  BufferDeallocation deallocation(op, options, symbolTables);
1047 
1048  // Place all required temporary clone and dealloc nodes.
1049  return deallocation.deallocate(op);
1050 }
static bool isMemref(Value v)
static Value buildBoolValue(OpBuilder &builder, Location loc, bool value)
static bool hasBufferSemantics(Operation *op)
Return "true" if the given op has buffer semantics.
static bool hasNeitherAllocateNorFreeSideEffect(Operation *op)
Return "true" if the given op is guaranteed to have neither "Allocate" nor "Free" side effects.
static llvm::ManagedStatic< PassManagerOptions > options
This class represents an argument of a Block.
Definition: Value.h:309
Location getLoc() const
Return the location for this argument.
Definition: Value.h:324
Block represents an ordered list of Operations.
Definition: Block.h:33
BlockArgument getArgument(unsigned i)
Definition: Block.h:129
unsigned getNumArguments()
Definition: Block.h:128
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition: Block.cpp:27
SuccessorRange getSuccessors()
Definition: Block.h:270
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition: Block.cpp:153
OpListType & getOperations()
Definition: Block.h:137
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition: Block.cpp:36
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition: Block.cpp:31
BoolAttr getBoolAttr(bool value)
Definition: Builders.cpp:99
IntegerType getI1Type()
Definition: Builders.cpp:52
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:76
This class provides a mutable adaptor for a range of operands.
Definition: ValueRange.h:118
OperandRange getAsOperandRange() const
Explicit conversion to an OperandRange.
void assign(ValueRange values)
Assign this range to the given values.
RAII guard to reset the insertion point of the builder when destroyed.
Definition: Builders.h:348
This class helps build Operations.
Definition: Builders.h:207
static OpBuilder atBlockBegin(Block *block, Listener *listener=nullptr)
Create a builder and set the insertion point to before the first operation in the block but still ins...
Definition: Builders.h:240
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition: Builders.h:398
Operation * insert(Operation *op)
Insert the given operation at the current insertion point and return it.
Definition: Builders.cpp:420
This class represents an operand of an operation.
Definition: Value.h:257
This class provides the API for ops that are known to be terminators.
Definition: OpDefinition.h:773
This class indicates that the regions associated with this op don't have terminators.
Definition: OpDefinition.h:769
This class implements the operand iterators for the Operation class.
Definition: ValueRange.h:43
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
Definition: Operation.cpp:295
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition: Operation.h:749
unsigned getNumRegions()
Returns the number of regions held by this operation.
Definition: Operation.h:674
Location getLoc()
The source location the operation was defined or derived from.
Definition: Operation.h:223
static Operation * create(Location location, OperationName name, TypeRange resultTypes, ValueRange operands, NamedAttrList &&attributes, OpaqueProperties properties, BlockRange successors, unsigned numRegions)
Create a new Operation with the specific fields.
Definition: Operation.cpp:66
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:267
Block * getBlock()
Returns the operation block that contains this operation.
Definition: Operation.h:213
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition: Operation.h:677
OperationName getName()
The name of an operation is the key identifier for it.
Definition: Operation.h:119
result_type_range getResultTypes()
Definition: Operation.h:428
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition: Operation.h:378
void replaceAllUsesWith(ValuesT &&values)
Replace all uses of results of this operation with the provided 'values'.
Definition: Operation.h:272
SuccessorRange getSuccessors()
Definition: Operation.h:703
result_range getResults()
Definition: Operation.h:415
OpaqueProperties getPropertiesStorage()
Returns the properties storage.
Definition: Operation.h:900
void erase()
Remove this operation from its parent block and delete it.
Definition: Operation.cpp:538
unsigned getNumResults()
Return the number of results held by this operation.
Definition: Operation.h:404
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
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 represents a collection of SymbolTables.
Definition: SymbolTable.h:283
This class provides an abstraction over the various different ranges of value types.
Definition: TypeRange.h:37
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
Type getType() const
Return the type of this value.
Definition: Value.h:105
Block * getParentBlock()
Return the Block in which this Value is defined.
Definition: Value.cpp:46
Location getLoc() const
Return the location of this value.
Definition: Value.cpp:24
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:18
static WalkResult skip()
Definition: WalkResult.h:48
static WalkResult advance()
Definition: WalkResult.h:47
static WalkResult interrupt()
Definition: WalkResult.h:46
This class collects all the state that we need to perform the buffer deallocation pass with associate...
This class is used to track the ownership of values.
bool isUnique() const
Check if this ownership value is in the 'Unique' state.
Value getIndicator() const
If this ownership value is in 'Unique' state, this function can be used to get the indicator paramete...
FailureOr< Operation * > insertDeallocOpForReturnLike(DeallocationState &state, Operation *op, ValueRange operands, SmallVectorImpl< Value > &updatedOperandOwnerships)
Insert a bufferization.dealloc operation right before op which has to be a terminator without any suc...
LogicalResult deallocateBuffersOwnershipBased(FunctionOpInterface op, DeallocationOptions options, SymbolTableCollection &symbolTables)
Run the ownership-based buffer deallocation.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition: Matchers.h:490
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
Definition: Matchers.h:478
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
bool hasUnknownEffects(Operation *op)
Return "true" if op has unknown effects.
This iterator enumerates elements according to their dominance relationship.
Definition: Iterators.h:48
The following effect indicates that the operation allocates from some resource.
The following effect indicates that the operation frees some resource that has been allocated.
Options for BufferDeallocationOpInterface-based buffer deallocation.