33#define GEN_PASS_DEF_OWNERSHIPBASEDBUFFERDEALLOCATIONPASS
34#include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
46 return arith::ConstantOp::create(builder, loc, builder.
getBoolAttr(value));
66 if (llvm::any_of(region.front().getArguments(),
isMemref))
81 using BlockSetT = SmallPtrSet<Block *, 16>;
82 using BackedgeSetT = llvm::DenseSet<std::pair<Block *, Block *>>;
86 Backedges(Operation *op) { recurse(op); }
89 size_t size()
const {
return edgeSet.size(); }
92 BackedgeSetT::const_iterator begin()
const {
return edgeSet.begin(); }
95 BackedgeSetT::const_iterator end()
const {
return edgeSet.end(); }
101 bool enter(
Block ¤t,
Block *predecessor) {
102 bool inserted = visited.insert(¤t).second;
104 edgeSet.insert(std::make_pair(predecessor, ¤t));
109 void exit(
Block ¤t) { visited.erase(¤t); }
113 void recurse(Operation *op) {
117 if (isa<BranchOpInterface>(op)) {
119 recurse(*succ, current);
125 recurse(region.front(), current);
131 void recurse(
Block &block,
Block *predecessor) {
134 if (!enter(block, predecessor))
149 BackedgeSetT edgeSet;
161class BufferDeallocation {
163 BufferDeallocation(Operation *op, DeallocationOptions
options,
164 SymbolTableCollection &symbolTables)
168 LogicalResult deallocate(FunctionOpInterface op);
172 template <
typename... T>
173 typename std::enable_if<
sizeof...(T) == 0, FailureOr<Operation *>>::type
174 handleOp(Operation *op) {
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);
216 return FailureOr<Operation *>(
nullptr);
217 return handleOp<InterfacesU...>(next);
221 FailureOr<Operation *> handleAllInterfaces(Operation *op) {
222 if (
auto deallocOpInterface = dyn_cast<BufferDeallocationOpInterface>(op))
223 return deallocOpInterface.process(state,
options);
225 if (
failed(verifyOperationPreconditions(op)))
228 return handleOp<MemoryEffectOpInterface, RegionBranchOpInterface,
229 CallOpInterface, BranchOpInterface,
230 RegionBranchTerminatorOpInterface>(op);
253 FailureOr<Operation *> handleInterface(BranchOpInterface op);
285 FailureOr<Operation *> handleInterface(RegionBranchOpInterface op);
314 FailureOr<Operation *> handleInterface(CallOpInterface op);
334 FailureOr<Operation *> handleInterface(MemoryEffectOpInterface op);
357 FailureOr<Operation *> handleInterface(RegionBranchTerminatorOpInterface op);
366 Operation *appendOpResults(Operation *op, ArrayRef<Type> types);
370 template <
typename OpTy>
371 OpTy appendOpResults(OpTy op, ArrayRef<Type> types) {
372 return cast<OpTy>(appendOpResults(op.getOperation(), types));
378 LogicalResult deallocate(
Block *block);
391 void populateRemainingOwnerships(Operation *op);
396 Value materializeMemrefWithGuaranteedOwnership(OpBuilder &builder,
397 Value memref,
Block *block);
402 bool isFunctionWithoutDynamicOwnership(Operation *op);
411 std::pair<Value, Value>
412 materializeUniqueOwnership(OpBuilder &builder, Value memref,
Block *block);
418 static LogicalResult verifyFunctionPreconditions(FunctionOpInterface op);
434 static LogicalResult verifyOperationPreconditions(Operation *op);
441 static LogicalResult updateFunctionSignature(FunctionOpInterface op);
447 DeallocationState state;
459std::pair<Value, Value>
464 if (
memref.getParentBlock() != block)
465 return state.getMemrefWithUniqueOwnership(builder,
memref, block);
469 owner =
memref.getParentBlock()->getParentOp();
473 if (
auto deallocOpInterface = dyn_cast<BufferDeallocationOpInterface>(owner))
474 return deallocOpInterface.materializeUniqueOwnershipForMemref(
478 return state.getMemrefWithUniqueOwnership(builder,
memref, block);
482BufferDeallocation::verifyFunctionPreconditions(FunctionOpInterface op) {
485 Backedges backedges(op);
486 if (backedges.size()) {
487 op->emitError(
"Only structured control-flow loops are supported.");
494LogicalResult BufferDeallocation::verifyOperationPreconditions(
Operation *op) {
502 if (isa<DeallocOp>(op))
504 "No deallocation operations must be present when running this pass!");
516 "ops with unknown memory side effects are not supported");
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.");
537 return op->
emitError(
"NoTerminator trait is not supported");
542 if (!isa<BranchOpInterface, RegionBranchTerminatorOpInterface>(op) ||
543 (isa<BranchOpInterface>(op) &&
544 isa<RegionBranchTerminatorOpInterface>(op)))
547 "Terminators must implement either BranchOpInterface or "
548 "RegionBranchTerminatorOpInterface (but not both)!");
554 return op->
emitError(
"Terminators with more than one successor "
555 "are not supported!");
562BufferDeallocation::updateFunctionSignature(FunctionOpInterface op) {
564 op.getFunctionBody().getOps<RegionBranchTerminatorOpInterface>(),
565 [&](RegionBranchTerminatorOpInterface branchOp) {
566 return branchOp.getSuccessorOperands(RegionSuccessor::parent())
569 if (!llvm::all_equal(returnOperandTypes))
570 return op->emitError(
571 "there are multiple return operations with different operand types");
573 TypeRange resultTypes = op.getResultTypes();
578 if (!returnOperandTypes.empty())
579 resultTypes = returnOperandTypes[0];
581 op.setFunctionTypeAttr(TypeAttr::get(FunctionType::get(
582 op->getContext(), op.getFunctionBody().front().getArgumentTypes(),
588LogicalResult BufferDeallocation::deallocate(FunctionOpInterface op) {
590 if (
failed(verifyFunctionPreconditions(op)))
596 if (
failed(deallocate(block)))
600 if (
result.wasInterrupted())
605 return updateFunctionSignature(op);
608LogicalResult BufferDeallocation::deallocate(
Block *block) {
613 state.getLiveMemrefsIn(block, liveMemrefs);
614 for (
auto li : liveMemrefs) {
618 if (li.getParentRegion() == block->
getParent()) {
619 state.updateOwnership(li, state.getOwnership(li, li.getParentBlock()),
621 state.addMemrefToDeallocate(li, block);
625 if (li.getParentRegion()->isProperAncestor(block->
getParent())) {
627 state.updateOwnership(li, falseVal, block);
638 if (isa<FunctionOpInterface>(block->
getParentOp()) &&
641 state.updateOwnership(arg, newArg);
642 state.addMemrefToDeallocate(arg, block);
648 state.updateOwnership(arg, newArg);
649 state.addMemrefToDeallocate(arg, block);
654 for (
Operation &op : llvm::make_early_inc_range(*block)) {
655 FailureOr<Operation *>
result = handleAllInterfaces(&op);
661 populateRemainingOwnerships(*
result);
676 newTypes.append(types.begin(), types.end());
681 for (
auto [oldRegion, newRegion] :
682 llvm::zip(op->
getRegions(), newOp->getRegions()))
683 newRegion.takeBody(oldRegion);
691 for (
auto [oldResult, newResult] :
692 llvm::zip(oldResults, newOp->getResults().take_front(oldResults.size())))
693 state.mapValue(oldResult, newResult);
698FailureOr<Operation *>
699BufferDeallocation::handleInterface(RegionBranchOpInterface op) {
717 assert(!regions.empty() &&
"Must have at least one successor region");
719 op.getEntrySuccessorOperands(regions.front()));
720 unsigned numMemrefOperands = llvm::count_if(entryOperands,
isMemref);
725 op->insertOperands(op->getNumOperands(),
728 int counter = op->getNumResults();
729 unsigned numMemrefResults = llvm::count_if(op->getResults(),
isMemref);
731 RegionBranchOpInterface newOp = appendOpResults(op, ownershipResults);
733 for (
auto result : llvm::make_filter_range(newOp->getResults(),
isMemref)) {
734 state.updateOwnership(
result, newOp->getResult(counter++));
735 state.addMemrefToDeallocate(
result, newOp->getBlock());
738 return newOp.getOperation();
741Value BufferDeallocation::materializeMemrefWithGuaranteedOwnership(
744 std::pair<Value, Value> newMemrefAndOnwership =
745 materializeUniqueOwnership(builder,
memref, block);
746 Value newMemref = newMemrefAndOnwership.first;
747 Value condition = newMemrefAndOnwership.second;
757 Value maybeClone = scf::IfOp::create(
758 builder,
memref.getLoc(), condition,
760 scf::YieldOp::create(builder, loc, newMemref);
763 Value clone = bufferization::CloneOp::create(
764 builder, loc, newMemref);
765 scf::YieldOp::create(builder, loc, clone);
769 state.updateOwnership(maybeClone, trueVal);
770 state.addMemrefToDeallocate(maybeClone, maybeClone.
getParentBlock());
774FailureOr<Operation *>
775BufferDeallocation::handleInterface(BranchOpInterface op) {
776 if (op->getNumSuccessors() > 1)
777 return op->emitError(
"BranchOpInterface operations with multiple "
778 "successors are not supported yet");
780 if (op->getNumSuccessors() != 1)
782 "only BranchOpInterface operations with exactly "
783 "one successor are supported yet");
785 if (op.getSuccessorOperands(0).getProducedOperandCount() > 0)
786 return op.emitError(
"produced operands are not supported");
790 Block *block = op->getBlock();
793 if (
failed(state.getMemrefsAndConditionsToDeallocate(
794 builder, op.getLoc(), block, memrefs, conditions)))
798 op.getSuccessorOperands(0).getForwardedOperands();
799 state.getMemrefsToRetain(block, op->getSuccessor(0), forwardedOperands,
802 auto deallocOp = bufferization::DeallocOp::create(
803 builder, op.getLoc(), memrefs, conditions, toRetain);
807 state.resetOwnerships(deallocOp.getRetained(), block);
808 for (
auto [retained, ownership] :
809 llvm::zip(deallocOp.getRetained(), deallocOp.getUpdatedConditions())) {
810 state.updateOwnership(retained, ownership, block);
813 unsigned numAdditionalReturns = llvm::count_if(forwardedOperands,
isMemref);
815 auto additionalConditions =
816 deallocOp.getUpdatedConditions().take_front(numAdditionalReturns);
817 newOperands.append(additionalConditions.begin(), additionalConditions.end());
818 op.getSuccessorOperands(0).getMutableForwardedOperands().assign(newOperands);
820 return op.getOperation();
823FailureOr<Operation *> BufferDeallocation::handleInterface(CallOpInterface op) {
830 Operation *funcOp = op.resolveCallableInTable(state.getSymbolTable());
831 bool isPrivate =
false;
832 if (
auto symbol = dyn_cast_or_null<SymbolOpInterface>(funcOp))
833 isPrivate = symbol.isPrivate() && !symbol.isDeclaration();
839 if (
options.privateFuncDynamicOwnership && isPrivate) {
840 unsigned numMemrefs = llvm::count_if(op->getResults(),
isMemref);
842 unsigned ownershipCounter = op->getNumResults();
843 op = appendOpResults(op, ownershipTypesToAppend);
845 for (
auto result : llvm::make_filter_range(op->getResults(),
isMemref)) {
846 state.updateOwnership(
result, op->getResult(ownershipCounter++));
847 state.addMemrefToDeallocate(
result,
result.getParentBlock());
850 return op.getOperation();
857 for (
auto result : llvm::make_filter_range(op->getResults(),
isMemref)) {
858 state.updateOwnership(
result, trueVal);
859 state.addMemrefToDeallocate(
result,
result.getParentBlock());
862 return op.getOperation();
865FailureOr<Operation *>
866BufferDeallocation::handleInterface(MemoryEffectOpInterface op) {
867 auto *block = op->getBlock();
870 for (
auto operand : llvm::make_filter_range(op->getOperands(),
isMemref)) {
878 if (!op->hasAttr(BufferizationDialect::kManualDeallocation))
879 return op->emitError(
880 "memory free side-effect on MemRef value not supported!");
888 Ownership ownership = state.getOwnership(operand, block);
890 Value ownershipInverted = arith::XOrIOp::create(
893 cf::AssertOp::create(builder, op.getLoc(), ownershipInverted,
894 "expected that the block does not have ownership");
899 for (
auto res : llvm::make_filter_range(op->getResults(),
isMemref)) {
901 if (allocEffect.has_value()) {
902 if (isa<SideEffects::AutomaticAllocationScopeResource>(
903 allocEffect->getResource())) {
911 state.resetOwnerships(res, block);
912 state.updateOwnership(res,
buildBoolValue(builder, op.getLoc(),
false));
916 if (op->hasAttr(BufferizationDialect::kManualDeallocation)) {
920 state.resetOwnerships(res, block);
921 state.updateOwnership(res,
buildBoolValue(builder, op.getLoc(),
false));
925 state.updateOwnership(res,
buildBoolValue(builder, op.getLoc(),
true));
926 state.addMemrefToDeallocate(res, block);
930 return op.getOperation();
933FailureOr<Operation *>
934BufferDeallocation::handleInterface(RegionBranchTerminatorOpInterface op) {
941 bool funcWithoutDynamicOwnership =
942 isFunctionWithoutDynamicOwnership(op->getParentOp());
943 if (funcWithoutDynamicOwnership) {
944 for (
OpOperand &val : op->getOpOperands()) {
948 val.set(materializeMemrefWithGuaranteedOwnership(builder, val.get(),
967 if (!funcWithoutDynamicOwnership) {
969 newOperands.append(updatedOwnerships.begin(), updatedOwnerships.end());
970 operands.
assign(newOperands);
973 return op.getOperation();
976bool BufferDeallocation::isFunctionWithoutDynamicOwnership(
Operation *op) {
977 auto funcOp = dyn_cast<FunctionOpInterface>(op);
978 return funcOp && (!
options.privateFuncDynamicOwnership ||
979 !funcOp.isPrivate() || funcOp.isExternal());
982void BufferDeallocation::populateRemainingOwnerships(
Operation *op) {
986 if (!state.getOwnership(res, op->
getBlock()).isUninitialized())
996 state.updateOwnership(
997 res, state.getOwnership(operand, operand.getParentBlock()),
1007 if (state.getOwnership(res, op->
getBlock()).isUninitialized()) {
1023struct OwnershipBasedBufferDeallocationPass
1025 OwnershipBasedBufferDeallocationPass> {
1030 options.privateFuncDynamicOwnership = privateFuncDynamicOwnership;
1032 mlir::SymbolTableCollection symbolTables;
1034 auto status =
getOperation()->walk([&](func::FuncOp func) {
1035 if (func.isExternal())
1043 if (status.wasInterrupted())
1058 BufferDeallocation deallocation(op,
options, symbolTables);
1061 return deallocation.deallocate(op);
static bool isMemref(Value v)
static Value buildBoolValue(OpBuilder &builder, Location loc, bool value)
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
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.
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.
OpListType & getOperations()
SuccessorRange getSuccessors()
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
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 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.
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.
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 * 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.
OpT getOperation()
Return the current operation being transformed.
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.
Block * getBlock()
Returns the operation block that contains this operation.
unsigned getNumRegions()
Returns the number of regions held by this operation.
Location getLoc()
The source location the operation was defined or derived from.
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.
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
OperationName getName()
The name of an operation is the key identifier for it.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
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.
virtual void runOnOperation()=0
The polymorphic API that runs the pass over the currently held operation.
void signalPassFailure()
Signal that some invariant was broken when running.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
static RegionSuccessor parent()
Initialize a successor that branches after/out of the parent operation.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
This class represents a collection of SymbolTables.
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.
static WalkResult advance()
static WalkResult interrupt()
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.
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
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.
bool hasUnknownEffects(Operation *op)
Return "true" if op has unknown effects.
bool mightHaveEffect(Operation *op)
Returns "true" if op might have an effect of type EffectTy.
This iterator enumerates elements according to their dominance relationship.
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.