Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit 4fbbb7a

Browse files
[mlir][bufferization] Fix ownership computation of unknown ops (llvm#70773)
No ownership is assumed for memref results of ops that implement none of the relevant interfaces and have no memref operands. This fixes llvm#68948.
1 parent 604eff6 commit 4fbbb7a

File tree

2 files changed

+45
-10
lines changed

2 files changed

+45
-10
lines changed

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -973,16 +973,9 @@ void BufferDeallocation::populateRemainingOwnerships(Operation *op) {
973973
if (!state.getOwnership(res, op->getBlock()).isUninitialized())
974974
continue;
975975

976-
// Don't take ownership of a returned memref if no allocate side-effect is
977-
// present, relevant for memref.get_global, for example.
978-
if (op->getNumOperands() == 0) {
979-
OpBuilder builder(op);
980-
state.updateOwnership(res, buildBoolValue(builder, op->getLoc(), false));
981-
continue;
982-
}
983-
984-
// Assume the result may alias with any operand and thus combine all their
985-
// ownerships.
976+
// The op does not allocate memory, otherwise, it would have been assigned
977+
// an ownership during `handleInterface`. Assume the result may alias with
978+
// any memref operand and thus combine all their ownerships.
986979
for (auto operand : op->getOperands()) {
987980
if (!isMemref(operand))
988981
continue;
@@ -991,6 +984,17 @@ void BufferDeallocation::populateRemainingOwnerships(Operation *op) {
991984
res, state.getOwnership(operand, operand.getParentBlock()),
992985
op->getBlock());
993986
}
987+
988+
// If the ownership value is still uninitialized (e.g., because the op has
989+
// no memref operands), assume that no ownership is taken. E.g., this is the
990+
// case for "memref.get_global".
991+
//
992+
// Note: This can lead to memory leaks if memory side effects are not
993+
// properly specified on the op.
994+
if (state.getOwnership(res, op->getBlock()).isUninitialized()) {
995+
OpBuilder builder(op);
996+
state.updateOwnership(res, buildBoolValue(builder, op->getLoc(), false));
997+
}
994998
}
995999
}
9961000

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: mlir-opt -verify-diagnostics -ownership-based-buffer-deallocation \
2+
// RUN: --buffer-deallocation-simplification -canonicalize -split-input-file %s | FileCheck %s
3+
4+
// No ownership is assumed for ops that do not implement any interface and have
5+
// no memref operands.
6+
7+
// CHECK-LABEL: func private @no_interface_no_operands(
8+
// CHECK-NEXT: %[[m:.*]] = bufferization.to_memref
9+
// CHECK-NEXT: %[[clone:.*]] = bufferization.clone %[[m]]
10+
// CHECK-NEXT: return %[[clone]]
11+
func.func private @no_interface_no_operands(%t : tensor<?x?x?xf16>) -> memref<?x?x?xf16> {
12+
%0 = bufferization.to_memref %t : memref<?x?x?xf16>
13+
return %0 : memref<?x?x?xf16>
14+
}
15+
16+
// -----
17+
18+
// If an op does not implement any interface but has memref operands, the
19+
// ownership of the memref results is computed from the operands.
20+
21+
// CHECK-LABEL: func private @no_interface(
22+
// CHECK: %[[true:.*]] = arith.constant true
23+
// CHECK: %[[alloc:.*]] = memref.alloc
24+
// CHECK: %[[foo:.*]] = "test.foo"(%[[alloc]])
25+
// CHECK: %[[r:.*]] = bufferization.dealloc (%[[alloc]] : {{.*}}) if (%[[true]]) retain (%[[foo]] : {{.*}})
26+
// CHECK: return %[[foo]]
27+
func.func private @no_interface() -> memref<5xf32> {
28+
%0 = memref.alloc() : memref<5xf32>
29+
%1 = "test.foo"(%0) : (memref<5xf32>) -> (memref<5xf32>)
30+
return %1 : memref<5xf32>
31+
}

0 commit comments

Comments
 (0)