32 namespace bufferization {
33 #define GEN_PASS_DEF_OWNERSHIPBASEDBUFFERDEALLOCATION
34 #include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
69 size_t size()
const {
return edgeSet.size(); }
72 BackedgeSetT::const_iterator begin()
const {
return edgeSet.begin(); }
75 BackedgeSetT::const_iterator end()
const {
return edgeSet.end(); }
81 bool enter(
Block ¤t,
Block *predecessor) {
82 bool inserted = visited.insert(¤t).second;
84 edgeSet.insert(std::make_pair(predecessor, ¤t));
89 void exit(
Block ¤t) { visited.erase(¤t); }
97 if (isa<BranchOpInterface>(op)) {
99 recurse(*succ, current);
105 recurse(region.front(), current);
111 void recurse(
Block &block,
Block *predecessor) {
114 if (!enter(block, predecessor))
129 BackedgeSetT edgeSet;
141 class BufferDeallocation {
143 BufferDeallocation(
Operation *op,
bool privateFuncDynamicOwnership)
145 options.privateFuncDynamicOwnership = privateFuncDynamicOwnership;
153 template <
typename... T>
187 template <
typename InterfaceT,
typename... InterfacesU>
190 if (
auto concreteOp = dyn_cast<InterfaceT>(op)) {
198 return handleOp<InterfacesU...>(next);
203 if (
auto deallocOpInterface = dyn_cast<BufferDeallocationOpInterface>(op))
204 return deallocOpInterface.process(state,
options);
206 if (
failed(verifyOperationPreconditions(op)))
209 return handleOp<MemoryEffectOpInterface, RegionBranchOpInterface,
210 CallOpInterface, BranchOpInterface,
211 RegionBranchTerminatorOpInterface>(op);
354 template <
typename OpTy>
356 return cast<OpTy>(appendOpResults(op.getOperation(), types));
375 void populateRemainingOwnerships(
Operation *op);
380 Value materializeMemrefWithGuaranteedOwnership(
OpBuilder &builder,
386 bool isFunctionWithoutDynamicOwnership(
Operation *op);
395 std::pair<Value, Value>
402 static LogicalResult verifyFunctionPreconditions(FunctionOpInterface op);
425 static LogicalResult updateFunctionSignature(FunctionOpInterface op);
443 std::pair<Value, Value>
444 BufferDeallocation::materializeUniqueOwnership(
OpBuilder &builder,
Value memref,
449 return state.getMemrefWithUniqueOwnership(builder, memref, block);
457 if (
auto deallocOpInterface = dyn_cast<BufferDeallocationOpInterface>(owner))
458 return deallocOpInterface.materializeUniqueOwnershipForMemref(
459 state,
options, builder, memref);
462 return state.getMemrefWithUniqueOwnership(builder, memref, block);
481 BufferDeallocation::verifyFunctionPreconditions(FunctionOpInterface op) {
484 Backedges backedges(op);
485 if (backedges.size()) {
486 op->
emitError(
"Only structured control-flow loops are supported.");
502 size_t size = regions.size();
503 if (((size == 1 && !op->
getResults().empty()) || size > 1) &&
504 !dyn_cast<RegionBranchOpInterface>(op)) {
506 return op->
emitError(
"All operations with attached regions need to "
507 "implement the RegionBranchOpInterface.");
512 if (isa<DeallocOp>(op))
514 "No deallocation operations must be present when running this pass!");
520 return op->
emitError(
"NoTerminator trait is not supported");
525 if (!isa<BranchOpInterface, RegionBranchTerminatorOpInterface>(op) ||
526 (isa<BranchOpInterface>(op) &&
527 isa<RegionBranchTerminatorOpInterface>(op)))
530 "Terminators must implement either BranchOpInterface or "
531 "RegionBranchTerminatorOpInterface (but not both)!");
537 return op->
emitError(
"Terminators with more than one successor "
538 "are not supported!");
545 BufferDeallocation::updateFunctionSignature(FunctionOpInterface op) {
547 op.getFunctionBody().getOps<RegionBranchTerminatorOpInterface>(),
548 [](RegionBranchTerminatorOpInterface op) {
549 return op.getSuccessorOperands(RegionBranchPoint::parent()).getTypes();
551 if (!llvm::all_equal(returnOperandTypes))
553 "there are multiple return operations with different operand types");
560 if (!returnOperandTypes.empty())
561 resultTypes = returnOperandTypes[0];
568 op->
getContext(), op.getFunctionBody().front().getArgumentTypes(),
574 LogicalResult BufferDeallocation::deallocate(FunctionOpInterface op) {
576 if (
failed(verifyFunctionPreconditions(op)))
582 if (
failed(deallocate(block)))
586 if (result.wasInterrupted())
591 return updateFunctionSignature(op);
599 state.getLiveMemrefsIn(block, liveMemrefs);
600 for (
auto li : liveMemrefs) {
604 if (li.getParentRegion() == block->
getParent()) {
605 state.updateOwnership(li, state.getOwnership(li, li.getParentBlock()),
607 state.addMemrefToDeallocate(li, block);
611 if (li.getParentRegion()->isProperAncestor(block->
getParent())) {
613 state.updateOwnership(li, falseVal, block);
624 if (isFunctionWithoutDynamicOwnership(block->
getParentOp()) &&
627 state.updateOwnership(arg, newArg);
628 state.addMemrefToDeallocate(arg, block);
634 state.updateOwnership(arg, newArg);
635 state.addMemrefToDeallocate(arg, block);
640 for (
Operation &op : llvm::make_early_inc_range(*block)) {
647 populateRemainingOwnerships(*result);
657 newTypes.append(types.begin(), types.end());
662 for (
auto [oldRegion, newRegion] :
663 llvm::zip(op->
getRegions(), newOp->getRegions()))
664 newRegion.takeBody(oldRegion);
674 BufferDeallocation::handleInterface(RegionBranchOpInterface op) {
692 assert(!regions.empty() &&
"Must have at least one successor region");
694 op.getEntrySuccessorOperands(regions.front()));
695 unsigned numMemrefOperands = llvm::count_if(entryOperands,
isMemref);
706 RegionBranchOpInterface newOp = appendOpResults(op, ownershipResults);
708 for (
auto result : llvm::make_filter_range(newOp->getResults(),
isMemref)) {
709 state.updateOwnership(result, newOp->getResult(counter++));
710 state.addMemrefToDeallocate(result, newOp->getBlock());
713 return newOp.getOperation();
716 Value BufferDeallocation::materializeMemrefWithGuaranteedOwnership(
719 std::pair<Value, Value> newMemrefAndOnwership =
720 materializeUniqueOwnership(builder, memref, block);
721 Value newMemref = newMemrefAndOnwership.first;
722 Value condition = newMemrefAndOnwership.second;
735 memref.
getLoc(), condition,
737 builder.create<scf::YieldOp>(loc, newMemref);
741 builder.
create<bufferization::CloneOp>(loc, newMemref);
746 state.updateOwnership(maybeClone, trueVal);
747 state.addMemrefToDeallocate(maybeClone, maybeClone.
getParentBlock());
752 BufferDeallocation::handleInterface(BranchOpInterface op) {
754 return op->
emitError(
"BranchOpInterface operations with multiple "
755 "successors are not supported yet");
759 "only BranchOpInterface operations with exactly "
760 "one successor are supported yet");
762 if (op.getSuccessorOperands(0).getProducedOperandCount() > 0)
763 return op.
emitError(
"produced operands are not supported");
770 if (
failed(state.getMemrefsAndConditionsToDeallocate(
771 builder, op.
getLoc(), block, memrefs, conditions)))
775 op.getSuccessorOperands(0).getForwardedOperands();
776 state.getMemrefsToRetain(block, op->
getSuccessor(0), forwardedOperands,
779 auto deallocOp = builder.create<bufferization::DeallocOp>(
780 op.
getLoc(), memrefs, conditions, toRetain);
784 state.resetOwnerships(deallocOp.getRetained(), block);
785 for (
auto [retained, ownership] :
786 llvm::zip(deallocOp.getRetained(), deallocOp.getUpdatedConditions())) {
787 state.updateOwnership(retained, ownership, block);
790 unsigned numAdditionalReturns = llvm::count_if(forwardedOperands,
isMemref);
792 auto additionalConditions =
793 deallocOp.getUpdatedConditions().take_front(numAdditionalReturns);
794 newOperands.append(additionalConditions.begin(), additionalConditions.end());
795 op.getSuccessorOperands(0).getMutableForwardedOperands().assign(newOperands);
797 return op.getOperation();
806 Operation *funcOp = op.resolveCallable(state.getSymbolTable());
807 bool isPrivate =
true;
808 if (
auto symbol = dyn_cast<SymbolOpInterface>(funcOp))
809 isPrivate = symbol.isPrivate() && !symbol.isDeclaration();
816 if (
options.privateFuncDynamicOwnership && isPrivate) {
818 for (
Value operand : op.getArgOperands()) {
820 newOperands.push_back(operand);
823 auto [memref, condition] =
824 materializeUniqueOwnership(builder, operand, op->
getBlock());
825 newOperands.push_back(memref);
826 ownershipIndicatorsToAdd.push_back(condition);
828 newOperands.append(ownershipIndicatorsToAdd.begin(),
829 ownershipIndicatorsToAdd.end());
830 op.getArgOperandsMutable().assign(newOperands);
835 op = appendOpResults(op, ownershipTypesToAppend);
838 state.updateOwnership(result, op->
getResult(ownershipCounter++));
839 state.addMemrefToDeallocate(result, result.getParentBlock());
842 return op.getOperation();
850 state.updateOwnership(result, trueVal);
851 state.addMemrefToDeallocate(result, result.getParentBlock());
854 return op.getOperation();
858 BufferDeallocation::handleInterface(MemoryEffectOpInterface op) {
864 if (!op->
hasAttr(BufferizationDialect::kManualDeallocation))
866 "memory free side-effect on MemRef value not supported!");
874 Ownership ownership = state.getOwnership(operand, block);
876 Value ownershipInverted = builder.
create<arith::XOrIOp>(
879 builder.
create<cf::AssertOp>(
880 op.
getLoc(), ownershipInverted,
881 "expected that the block does not have ownership");
888 if (allocEffect.has_value()) {
889 if (isa<SideEffects::AutomaticAllocationScopeResource>(
890 allocEffect->getResource())) {
898 state.resetOwnerships(res, block);
903 if (op->
hasAttr(BufferizationDialect::kManualDeallocation)) {
907 state.resetOwnerships(res, block);
913 state.addMemrefToDeallocate(res, block);
917 return op.getOperation();
921 BufferDeallocation::handleInterface(RegionBranchTerminatorOpInterface op) {
928 bool funcWithoutDynamicOwnership =
929 isFunctionWithoutDynamicOwnership(op->
getParentOp());
930 if (funcWithoutDynamicOwnership) {
935 val.set(materializeMemrefWithGuaranteedOwnership(builder, val.get(),
950 if (
failed(result) || !*result)
954 if (!funcWithoutDynamicOwnership) {
956 newOperands.append(updatedOwnerships.begin(), updatedOwnerships.end());
957 operands.
assign(newOperands);
960 return op.getOperation();
963 bool BufferDeallocation::isFunctionWithoutDynamicOwnership(
Operation *op) {
964 auto funcOp = dyn_cast<FunctionOpInterface>(op);
965 return funcOp && (!
options.privateFuncDynamicOwnership ||
966 !funcOp.isPrivate() || funcOp.isExternal());
969 void BufferDeallocation::populateRemainingOwnerships(
Operation *op) {
973 if (!state.getOwnership(res, op->
getBlock()).isUninitialized())
983 state.updateOwnership(
984 res, state.getOwnership(operand, operand.getParentBlock()),
994 if (state.getOwnership(res, op->
getBlock()).isUninitialized()) {
1010 struct OwnershipBasedBufferDeallocationPass
1011 :
public bufferization::impl::OwnershipBasedBufferDeallocationBase<
1012 OwnershipBasedBufferDeallocationPass> {
1013 OwnershipBasedBufferDeallocationPass() =
default;
1014 OwnershipBasedBufferDeallocationPass(
bool privateFuncDynamicOwnership)
1015 : OwnershipBasedBufferDeallocationPass() {
1016 this->privateFuncDynamicOwnership.setValue(privateFuncDynamicOwnership);
1018 void runOnOperation()
override {
1019 auto status = getOperation()->walk([&](func::FuncOp func) {
1020 if (func.isExternal())
1024 privateFuncDynamicOwnership)))
1029 if (status.wasInterrupted())
1030 signalPassFailure();
1041 FunctionOpInterface op,
bool privateFuncDynamicOwnership) {
1043 BufferDeallocation deallocation(op, privateFuncDynamicOwnership);
1046 return deallocation.deallocate(op);
1053 std::unique_ptr<Pass>
1055 bool privateFuncDynamicOwnership) {
1056 return std::make_unique<OwnershipBasedBufferDeallocationPass>(
1057 privateFuncDynamicOwnership);
static bool regionOperatesOnMemrefValues(Region ®ion)
static bool isMemref(Value v)
static Value buildBoolValue(OpBuilder &builder, Location loc, bool value)
static llvm::ManagedStatic< PassManagerOptions > options
This class provides a shared interface for ranked and unranked memref types.
This class represents an argument of a Block.
Location getLoc() const
Return the location for this argument.
Block represents an ordered list of Operations.
BlockArgument getArgument(unsigned i)
unsigned getNumArguments()
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
SuccessorRange getSuccessors()
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
OpListType & getOperations()
BlockArgListType getArguments()
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
BoolAttr getBoolAttr(bool value)
This class provides support for representing a failure result, or a valid value of type T.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class provides a mutable adaptor for a range of operands.
void assign(ValueRange values)
Assign this range to the given values.
RAII guard to reset the insertion point of the builder when destroyed.
This class helps build Operations.
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...
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Operation * insert(Operation *op)
Insert the given operation at the current insertion point and return it.
This class represents an operand of an operation.
This class provides the API for ops that are known to be terminators.
This class indicates that the regions associated with this op don't have terminators.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
DictionaryAttr getAttrDictionary()
Return all of the attributes on this operation as a DictionaryAttr.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
void insertOperands(unsigned index, ValueRange operands)
Insert the given operands into the operand list at the given 'index'.
Block * getSuccessor(unsigned index)
bool hasAttr(StringAttr name)
Return true if the operation has an attribute with the provided name, false otherwise.
unsigned getNumSuccessors()
OpResult getResult(unsigned idx)
Get the 'idx'th result of this operation.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
MLIRContext * getContext()
Return the context this operation is associated with.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
unsigned getNumOperands()
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.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Block * getBlock()
Returns the operation block that contains this operation.
void setAttr(StringAttr name, Attribute value)
If the an attribute exists with the specified name, change it to the new value.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
OperationName getName()
The name of an operation is the key identifier for it.
MutableArrayRef< OpOperand > getOpOperands()
result_type_range getResultTypes()
operand_range getOperands()
Returns an iterator on the underlying Value's.
void replaceAllUsesWith(ValuesT &&values)
Replace all uses of results of this operation with the provided 'values'.
SuccessorRange getSuccessors()
result_range getResults()
OpaqueProperties getPropertiesStorage()
Returns the properties storage.
void erase()
Remove this operation from its parent block and delete it.
unsigned getNumResults()
Return the number of results held by this operation.
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.
std::enable_if_t< std::is_same< RetT, void >::value, RetT > walk(FnT &&callback)
Walk the operations in this region.
This class provides an abstraction over the various different ranges of value types.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
Block * getParentBlock()
Return the Block in which this Value is defined.
Location getLoc() const
Return the location of this value.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
A utility result that is used to signal how to proceed with an ongoing walk:
static WalkResult advance()
bool wasInterrupted() const
Returns true if the walk was interrupted.
static WalkResult interrupt()
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...
std::unique_ptr< Pass > createOwnershipBasedBufferDeallocationPass(bool privateFuncDynamicOwnership=false)
Creates an instance of the OwnershipBasedBufferDeallocation pass to free all allocated buffers.
LogicalResult deallocateBuffersOwnershipBased(FunctionOpInterface op, bool privateFuncDynamicOwnership)
Run ownership basedbuffer deallocation.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
This iterator enumerates elements according to their dominance relationship.
This class represents an efficient way to signal success or failure.
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.