Skip to content

Commit ef53714

Browse files
[mlir][bufferization] Buffer deallocation: skip ops that do not operate on buffers
Skip ops that do not operate on buffers during ownership-based buffer deallocation. Such ops can be ignored during buffer deallocation. (Except for terminators.)
1 parent 5dd1fc7 commit ef53714

File tree

3 files changed

+50
-34
lines changed

3 files changed

+50
-34
lines changed

mlir/lib/Dialect/Bufferization/Transforms/OwnershipBasedBufferDeallocation.cpp

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,32 @@ static Value buildBoolValue(OpBuilder &builder, Location loc, bool value) {
4848

4949
static bool isMemref(Value v) { return v.getType().isa<BaseMemRefType>(); }
5050

51+
/// Return "true" if the given op is guaranteed to have no "Allocate" or "Free"
52+
/// side effect.
53+
static bool hasNoAllocateOrFreeSideEffect(Operation *op) {
54+
if (isa<MemoryEffectOpInterface>(op))
55+
return hasEffect<MemoryEffects::Allocate>(op) ||
56+
hasEffect<MemoryEffects::Free>(op);
57+
// If the op does not implement the MemoryEffectOpInterface but has has
58+
// recursive memory effects, then this op in isolation (without its body) does
59+
// not have any side effects. The ops inside the regions of this op will be
60+
// processed separately.
61+
return op->hasTrait<OpTrait::HasRecursiveMemoryEffects>();
62+
}
63+
64+
/// Return "true" if the given op has buffer semantics. I.e., it has buffer
65+
/// operands, buffer results and/or buffer region entry block arguments.
66+
static bool hasBufferSemantics(Operation *op) {
67+
if (llvm::any_of(op->getOperands(), isMemref) ||
68+
llvm::any_of(op->getResults(), isMemref))
69+
return true;
70+
for (Region &region : op->getRegions())
71+
if (!region.empty())
72+
if (llvm::any_of(region.front().getArguments(), isMemref))
73+
return true;
74+
return false;
75+
}
76+
5177
//===----------------------------------------------------------------------===//
5278
// Backedges analysis
5379
//===----------------------------------------------------------------------===//
@@ -462,31 +488,6 @@ BufferDeallocation::materializeUniqueOwnership(OpBuilder &builder, Value memref,
462488
return state.getMemrefWithUniqueOwnership(builder, memref, block);
463489
}
464490

465-
static bool regionOperatesOnMemrefValues(Region &region) {
466-
auto checkBlock = [](Block *block) {
467-
if (llvm::any_of(block->getArguments(), isMemref))
468-
return WalkResult::interrupt();
469-
for (Operation &op : *block) {
470-
if (llvm::any_of(op.getOperands(), isMemref))
471-
return WalkResult::interrupt();
472-
if (llvm::any_of(op.getResults(), isMemref))
473-
return WalkResult::interrupt();
474-
}
475-
return WalkResult::advance();
476-
};
477-
WalkResult result = region.walk(checkBlock);
478-
if (result.wasInterrupted())
479-
return true;
480-
481-
// Note: Block::walk/Region::walk visits only blocks that are nested under
482-
// nested operations, but not direct children.
483-
for (Block &block : region)
484-
if (checkBlock(&block).wasInterrupted())
485-
return true;
486-
487-
return false;
488-
}
489-
490491
LogicalResult
491492
BufferDeallocation::verifyFunctionPreconditions(FunctionOpInterface op) {
492493
// (1) Ensure that there are supported loops only (no explicit control flow
@@ -512,9 +513,8 @@ LogicalResult BufferDeallocation::verifyOperationPreconditions(Operation *op) {
512513
size_t size = regions.size();
513514
if (((size == 1 && !op->getResults().empty()) || size > 1) &&
514515
!dyn_cast<RegionBranchOpInterface>(op)) {
515-
if (llvm::any_of(regions, regionOperatesOnMemrefValues))
516-
return op->emitError("All operations with attached regions need to "
517-
"implement the RegionBranchOpInterface.");
516+
return op->emitError("All operations with attached regions need to "
517+
"implement the RegionBranchOpInterface.");
518518
}
519519

520520
// (2) The pass does not work properly when deallocations are already present.
@@ -648,6 +648,12 @@ LogicalResult BufferDeallocation::deallocate(Block *block) {
648648
// For each operation in the block, handle the interfaces that affect aliasing
649649
// and ownership of memrefs.
650650
for (Operation &op : llvm::make_early_inc_range(*block)) {
651+
// Skip ops that do not operate on buffers, have no Allocate/Free side
652+
// effect and are not terminators. (bufferization.dealloc ops are inserted
653+
// in front of terminators, so terminators cannot be skipped.)
654+
if (!op.hasTrait<OpTrait::IsTerminator>() && !hasBufferSemantics(&op) &&
655+
hasNoAllocateOrFreeSideEffect(&op))
656+
continue;
651657
FailureOr<Operation *> result = handleAllInterfaces(&op);
652658
if (failed(result))
653659
return failure();

mlir/test/Dialect/Bufferization/Transforms/OwnershipBasedBufferDeallocation/dealloc-region-branchop-interface.mlir

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,8 @@ func.func @assumingOp(
516516
// does not deal with any MemRef values.
517517

518518
func.func @noRegionBranchOpInterface() {
519-
%0 = "test.bar"() ({
520-
%1 = "test.bar"() ({
519+
%0 = "test.one_region_with_recursive_memory_effects"() ({
520+
%1 = "test.one_region_with_recursive_memory_effects"() ({
521521
"test.yield"() : () -> ()
522522
}) : () -> (i32)
523523
"test.yield"() : () -> ()
@@ -531,7 +531,7 @@ func.func @noRegionBranchOpInterface() {
531531
// This is not allowed in buffer deallocation.
532532

533533
func.func @noRegionBranchOpInterface() {
534-
%0 = "test.bar"() ({
534+
%0 = "test.one_region_with_recursive_memory_effects"() ({
535535
// expected-error@+1 {{All operations with attached regions need to implement the RegionBranchOpInterface.}}
536536
%1 = "test.bar"() ({
537537
%2 = "test.get_memref"() : () -> memref<2xi32>
@@ -545,11 +545,10 @@ func.func @noRegionBranchOpInterface() {
545545
// -----
546546

547547
// Test Case: The op "test.bar" does not implement the RegionBranchOpInterface.
548-
// This is not allowed in buffer deallocation.
548+
// But it also does not operate on buffers, so we don't care.
549549

550550
func.func @noRegionBranchOpInterface() {
551-
// expected-error@+1 {{All operations with attached regions need to implement the RegionBranchOpInterface.}}
552-
%0 = "test.bar"() ({
551+
%0 = "test.one_region_with_recursive_memory_effects"() ({
553552
%2 = "test.get_memref"() : () -> memref<2xi32>
554553
%3 = "test.foo"(%2) : (memref<2xi32>) -> (i32)
555554
"test.yield"(%3) : (i32) -> ()

mlir/test/lib/Dialect/Test/TestOps.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,17 @@ def IsolatedRegionsOp : TEST_Op<"isolated_regions", [IsolatedFromAbove]> {
480480
let assemblyFormat = "attr-dict-with-keyword $regions";
481481
}
482482

483+
def OneRegionWithRecursiveMemoryEffectsOp
484+
: TEST_Op<"one_region_with_recursive_memory_effects", [
485+
RecursiveMemoryEffects]> {
486+
let description = [{
487+
Op that has one region and recursive side effects. The
488+
RegionBranchOpInterface is not implemented on this op.
489+
}];
490+
let results = (outs AnyType:$result);
491+
let regions = (region SizedRegion<1>:$body);
492+
}
493+
483494
//===----------------------------------------------------------------------===//
484495
// NoTerminator Operation
485496
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)