60 #include "llvm/ADT/SetOperations.h"
63 namespace bufferization {
64 #define GEN_PASS_DEF_BUFFERDEALLOCATION
65 #include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
76 for (
Block &block : *region) {
77 Operation *terminator = block.getTerminator();
79 if (
auto regionTerminator =
80 dyn_cast<RegionBranchTerminatorOpInterface>(terminator)) {
81 if (failed(func(regionTerminator)))
104 size_t size = regions.size();
105 if (((size == 1 && !operation->
getResults().empty()) || size > 1) &&
106 !dyn_cast<RegionBranchOpInterface>(operation)) {
107 operation->emitError(
"All operations with attached regions need to "
108 "implement the RegionBranchOpInterface.");
131 Backedges(
Operation *op) { recurse(op); }
134 size_t size()
const {
return edgeSet.size(); }
137 BackedgeSetT::const_iterator begin()
const {
return edgeSet.begin(); }
140 BackedgeSetT::const_iterator end()
const {
return edgeSet.end(); }
146 bool enter(
Block ¤t,
Block *predecessor) {
147 bool inserted = visited.insert(¤t).second;
149 edgeSet.insert(std::make_pair(predecessor, ¤t));
154 void exit(
Block ¤t) { visited.erase(¤t); }
162 if (isa<BranchOpInterface>(op)) {
164 recurse(*succ, current);
170 recurse(region.
front(), current);
176 void recurse(
Block &block,
Block *predecessor) {
179 if (!enter(block, predecessor))
194 BackedgeSetT edgeSet;
206 using AliasAllocationMapT =
211 postDominators(op) {}
218 LogicalResult prepare() {
221 Value alloc = std::get<0>(entry);
222 auto allocationInterface =
226 if (!std::get<1>(entry) && !allocationInterface) {
228 "Allocation is not deallocated explicitly nor does the operation "
229 "implement the AllocationOpInterface.");
233 aliasToAllocations[alloc] = allocationInterface;
236 for (
Value alias : aliases.resolve(alloc)) {
240 aliasToAllocations[alias] = allocationInterface;
248 LogicalResult deallocate() {
250 if (failed(introduceClones()))
254 return placeDeallocs();
259 LogicalResult introduceClones() {
264 llvm::SmallDenseSet<std::tuple<Value, Block *>> visitedValues;
271 auto findUnsafeValues = [&](
Value source,
Block *definingBlock) {
272 auto it = aliases.find(source);
273 if (it == aliases.end())
275 for (
Value value : it->second) {
276 if (valuesToFree.count(value) > 0)
283 if (!dominators.dominates(definingBlock, parentBlock) ||
284 (definingBlock == parentBlock && isa<BlockArgument>(value))) {
285 toProcess.emplace_back(value, parentBlock);
286 valuesToFree.insert(value);
287 }
else if (visitedValues.insert(std::make_tuple(value, definingBlock))
289 toProcess.emplace_back(value, definingBlock);
295 Value allocValue = std::get<0>(entry);
300 while (!toProcess.empty()) {
301 auto current = toProcess.pop_back_val();
302 findUnsafeValues(std::get<0>(current), std::get<1>(current));
307 aliases.remove(valuesToFree);
310 for (
Value value : valuesToFree) {
311 if (failed(isa<BlockArgument>(value)
312 ? introduceBlockArgCopy(cast<BlockArgument>(value))
313 : introduceValueCopyForRegionResult(value)))
319 allocs.registerAlloc(std::make_tuple(value,
nullptr));
326 LogicalResult introduceBlockArgCopy(
BlockArgument blockArg) {
334 Operation *terminator = (*it)->getTerminator();
335 auto branchInterface = cast<BranchOpInterface>(terminator);
337 branchInterface.getSuccessorOperands(it.getSuccessorIndex());
346 auto clone = introduceCloneBuffers(sourceValue, terminator);
358 RegionBranchOpInterface regionInterface;
359 if (&argRegion->
front() != block ||
360 !(regionInterface = dyn_cast<RegionBranchOpInterface>(parentOp)))
363 if (failed(introduceClonesForRegionSuccessors(
367 return successorRegion.getSuccessor() == argRegion;
378 llvm::find_if(successorRegions, [&](
RegionSuccessor &successorRegion) {
381 if (it == successorRegions.end())
386 auto operands = regionInterface.getEntrySuccessorOperands(argRegion);
387 size_t operandIndex =
388 llvm::find(it->getSuccessorInputs(), blockArg).getIndex() +
389 operands.getBeginOperandIndex();
392 operands[operandIndex - operands.getBeginOperandIndex()] &&
393 "region interface operands don't match parentOp operands");
394 auto clone = introduceCloneBuffers(operand, parentOp);
404 LogicalResult introduceValueCopyForRegionResult(
Value value) {
407 auto regionInterface = cast<RegionBranchOpInterface>(operation);
419 return introduceClonesForRegionSuccessors(
420 regionInterface, operation->
getRegions(), value, regionPredicate);
426 template <
typename TPredicate>
427 LogicalResult introduceClonesForRegionSuccessors(
429 Value argValue,
const TPredicate ®ionPredicate) {
430 for (
Region ®ion : regions) {
434 regionInterface.getSuccessorRegions(region, successorRegions);
437 llvm::find_if(successorRegions, regionPredicate);
438 if (regionSuccessor == successorRegions.end())
442 size_t operandIndex =
450 ®ion, [&](RegionBranchTerminatorOpInterface terminator) {
452 auto terminatorOperands =
453 terminator.getMutableSuccessorOperands(*regionSuccessor);
457 OperandRange immutableTerminatorOperands = terminatorOperands;
458 Value sourceValue = immutableTerminatorOperands[operandIndex];
460 auto clone = introduceCloneBuffers(sourceValue, terminator);
464 terminatorOperands.slice(operandIndex, 1).assign(*
clone);
475 FailureOr<Value> introduceCloneBuffers(
Value sourceValue,
485 if (clonedValues.contains(sourceValue))
489 auto clone = buildClone(terminator, sourceValue);
490 if (succeeded(
clone)) {
492 clonedValues.insert(*
clone);
500 LogicalResult placeDeallocs() {
506 Value alloc = std::get<0>(entry);
507 auto aliasesSet = aliases.resolve(alloc);
508 assert(!aliasesSet.empty() &&
"must contain at least one alias");
512 Block *placementBlock =
515 liveness.getLiveness(placementBlock);
525 for (
Value alias : aliasesSet) {
530 if (alias.getDefiningOp() &&
532 *alias.getDefiningOp())))
540 if (aliasEndOperation->
getBlock() == placementBlock &&
542 endOperation = aliasEndOperation;
548 Operation *deallocOperation = std::get<1>(entry);
549 if (deallocOperation) {
550 deallocOperation->
moveAfter(endOperation);
554 Operation *nextOp = endOperation->getNextNode();
558 if (failed(buildDealloc(nextOp, alloc)))
571 auto it = aliasToAllocations.find(alloc);
572 if (it != aliasToAllocations.end()) {
575 auto dealloc = it->second.buildDealloc(builder, alloc);
578 <<
"allocations without compatible deallocations are "
582 builder.create<memref::DeallocOp>(alloc.
getLoc(), alloc);
593 auto it = aliasToAllocations.find(alloc);
594 if (it != aliasToAllocations.end()) {
597 auto clone = it->second.buildClone(builder, alloc);
601 <<
"allocations without compatible clone ops "
602 "are not supported");
605 return builder.
create<bufferization::CloneOp>(alloc.
getLoc(), alloc)
618 ValueSetT clonedValues;
621 AliasAllocationMapT aliasToAllocations;
631 struct BufferDeallocationPass
632 :
public bufferization::impl::BufferDeallocationBase<
633 BufferDeallocationPass> {
635 registry.
insert<bufferization::BufferizationDialect>();
636 registry.
insert<memref::MemRefDialect>();
639 void runOnOperation()
override {
640 func::FuncOp func = getOperation();
641 if (func.isExternal())
652 if (isa<ModuleOp>(op)) {
662 Backedges backedges(op);
663 if (backedges.size()) {
664 op->
emitError(
"Only structured control-flow loops are supported.");
673 BufferDeallocation deallocation(op);
677 if (failed(deallocation.prepare()))
681 if (failed(deallocation.deallocate()))
692 return std::make_unique<BufferDeallocationPass>();
static LogicalResult walkReturnOperations(Region *region, llvm::function_ref< LogicalResult(RegionBranchTerminatorOpInterface)> func)
Walks over all immediate return-like terminators in the given region.
static bool validateSupportedControlFlow(Operation *op)
Checks if all operations that have at least one attached region implement the RegionBranchOpInterface...
This class represents an argument of a Block.
Block * getOwner() const
Returns the block that owns this argument.
unsigned getArgNumber() const
Returns the number of this argument.
Block represents an ordered list of Operations.
Operation * findAncestorOpInBlock(Operation &op)
Returns 'op' if 'op' lies in this block, or otherwise finds the ancestor operation of 'op' that lies ...
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
pred_iterator pred_begin()
SuccessorRange getSuccessors()
OpListType & getOperations()
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
A class for computing basic dominance information.
This class represents liveness information on block level.
Operation * getEndOperation(Value value, Operation *startOperation) const
Gets the end operation for the given value using the start operation provided (must be referenced in ...
void assign(ValueRange values)
Assign this range to the given values.
This class helps build Operations.
This class implements the operand iterators for the Operation class.
Operation is the basic unit of execution within MLIR.
Value getOperand(unsigned idx)
void setOperand(unsigned idx, Value value)
bool isBeforeInBlock(Operation *other)
Given an operation 'other' that is within the same parent block, return whether the current 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),...
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...
Block * getBlock()
Returns the operation block that contains this operation.
OpTy getParentOfType()
Return the closest surrounding parent operation that is of type 'OpTy'.
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
result_range getResults()
void moveAfter(Operation *existingOp)
Unlink this operation from its current block and insert it right after existingOp which may be in the...
A class for computing basic postdominance information.
static constexpr RegionBranchPoint parent()
Returns an instance of RegionBranchPoint representing the parent operation.
This class represents a successor of a region.
ValueRange getSuccessorInputs() const
Return the inputs to the successor that are remapped by the exit values of the current region.
Region * getSuccessor() const
Return the given region successor.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Operation * getParentOp()
Return the parent operation this region is attached to.
This class models how operands are forwarded to block arguments in control flow.
MutableOperandRange slice(unsigned subStart, unsigned subLen) const
Get a slice of the operands forwarded to the successor.
This class represents an instance of an SSA value in the MLIR system, representing a computable 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:
bool wasSkipped() const
Returns true if the walk was skipped.
static WalkResult advance()
bool wasInterrupted() const
Returns true if the walk was interrupted.
static WalkResult interrupt()
std::tuple< Value, Operation * > AllocEntry
Represents a tuple of allocValue and deallocOperation.
Block * findCommonDominator(Value value, const BufferViewFlowAnalysis::ValueSetT &values, const DominatorT &doms)
Finds a common dominator for the given value while taking the positions of the values in the value se...
LogicalResult deallocateBuffers(Operation *op)
Run buffer deallocation.
std::unique_ptr< Pass > createBufferDeallocationPass()
Creates an instance of the BufferDeallocation pass to free all allocated buffers.
Include the generated interface declarations.
Operation * clone(OpBuilder &b, Operation *op, TypeRange newResultTypes, ValueRange newOperands)