Skip to content

Commit 522c1d0

Browse files
authored
[mlir][gpu][bufferization] Implement BufferDeallocationOpInterface for gpu.terminator (#66880)
This is necessary to support deallocation of IR with gpu.launch operations because it does not implement the RegionBranchOpInterface. Implementing the interface would require it to support regions with unstructured control flow and produced arguments/results.
1 parent 97ae760 commit 522c1d0

File tree

10 files changed

+149
-53
lines changed

10 files changed

+149
-53
lines changed

mlir/include/mlir/Dialect/Bufferization/IR/BufferDeallocationOpInterface.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,23 @@ class DeallocationState {
205205
Liveness liveness;
206206
};
207207

208+
namespace deallocation_impl {
209+
/// Insert a `bufferization.dealloc` operation right before `op` which has to be
210+
/// a terminator without any successors. Note that it is not required to have
211+
/// the ReturnLike trait attached. The MemRef values in the `operands` argument
212+
/// will be added to the list of retained values and their updated ownership
213+
/// values will be appended to the `updatedOperandOwnerships` list. `op` is not
214+
/// modified in any way. Returns failure if at least one of the MemRefs to
215+
/// deallocate does not have 'Unique' ownership (likely as a result of an
216+
/// incorrect implementation of the `process` or
217+
/// `materializeUniqueOwnershipForMemref` interface method) or the original
218+
/// `op`.
219+
FailureOr<Operation *>
220+
insertDeallocOpForReturnLike(DeallocationState &state, Operation *op,
221+
ValueRange operands,
222+
SmallVectorImpl<Value> &updatedOperandOwnerships);
223+
} // namespace deallocation_impl
224+
208225
} // namespace bufferization
209226
} // namespace mlir
210227

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===- BufferDeallocationOpInterfaceImpl.h ----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef MLIR_DIALECT_GPU_TRANSFORMS_BUFFERDEALLOCATIONOPINTERFACEIMPL_H
10+
#define MLIR_DIALECT_GPU_TRANSFORMS_BUFFERDEALLOCATIONOPINTERFACEIMPL_H
11+
12+
namespace mlir {
13+
14+
class DialectRegistry;
15+
16+
namespace gpu {
17+
void registerBufferDeallocationOpInterfaceExternalModels(
18+
DialectRegistry &registry);
19+
} // namespace gpu
20+
} // namespace mlir
21+
22+
#endif // MLIR_DIALECT_GPU_TRANSFORMS_BUFFERDEALLOCATIONOPINTERFACEIMPL_H

mlir/include/mlir/InitAllDialects.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "mlir/Dialect/EmitC/IR/EmitC.h"
3737
#include "mlir/Dialect/Func/IR/FuncOps.h"
3838
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
39+
#include "mlir/Dialect/GPU/Transforms/BufferDeallocationOpInterfaceImpl.h"
3940
#include "mlir/Dialect/IRDL/IR/IRDL.h"
4041
#include "mlir/Dialect/Index/IR/IndexDialect.h"
4142
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
@@ -144,6 +145,7 @@ inline void registerAllDialects(DialectRegistry &registry) {
144145
builtin::registerCastOpInterfaceExternalModels(registry);
145146
cf::registerBufferizableOpInterfaceExternalModels(registry);
146147
cf::registerBufferDeallocationOpInterfaceExternalModels(registry);
148+
gpu::registerBufferDeallocationOpInterfaceExternalModels(registry);
147149
linalg::registerBufferizableOpInterfaceExternalModels(registry);
148150
linalg::registerTilingInterfaceExternalModels(registry);
149151
linalg::registerValueBoundsOpInterfaceExternalModels(registry);

mlir/lib/Dialect/Bufferization/IR/BufferDeallocationOpInterface.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,44 @@ bool ValueComparator::operator()(const Value &lhs, const Value &rhs) const {
272272
assert(lhsRegion && "this should only happen if lhs == rhs");
273273
return false;
274274
}
275+
276+
//===----------------------------------------------------------------------===//
277+
// Implementation utilities
278+
//===----------------------------------------------------------------------===//
279+
280+
FailureOr<Operation *> deallocation_impl::insertDeallocOpForReturnLike(
281+
DeallocationState &state, Operation *op, ValueRange operands,
282+
SmallVectorImpl<Value> &updatedOperandOwnerships) {
283+
assert(op->hasTrait<OpTrait::IsTerminator>() && "must be a terminator");
284+
assert(!op->hasSuccessors() && "must not have any successors");
285+
// Collect the values to deallocate and retain and use them to create the
286+
// dealloc operation.
287+
OpBuilder builder(op);
288+
Block *block = op->getBlock();
289+
SmallVector<Value> memrefs, conditions, toRetain;
290+
if (failed(state.getMemrefsAndConditionsToDeallocate(
291+
builder, op->getLoc(), block, memrefs, conditions)))
292+
return failure();
293+
294+
state.getMemrefsToRetain(block, /*toBlock=*/nullptr, operands, toRetain);
295+
if (memrefs.empty() && toRetain.empty())
296+
return op;
297+
298+
auto deallocOp = builder.create<bufferization::DeallocOp>(
299+
op->getLoc(), memrefs, conditions, toRetain);
300+
301+
// We want to replace the current ownership of the retained values with the
302+
// result values of the dealloc operation as they are always unique.
303+
state.resetOwnerships(deallocOp.getRetained(), block);
304+
for (auto [retained, ownership] :
305+
llvm::zip(deallocOp.getRetained(), deallocOp.getUpdatedConditions()))
306+
state.updateOwnership(retained, ownership, block);
307+
308+
unsigned numMemrefOperands = llvm::count_if(operands, isMemref);
309+
auto newOperandOwnerships =
310+
deallocOp.getUpdatedConditions().take_front(numMemrefOperands);
311+
updatedOperandOwnerships.append(newOperandOwnerships.begin(),
312+
newOperandOwnerships.end());
313+
314+
return op;
315+
}

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

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@ static Value buildBoolValue(OpBuilder &builder, Location loc, bool value) {
4747

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

50-
static bool isMemrefOperand(OpOperand &operand) {
51-
return isMemref(operand.get());
52-
}
53-
5450
//===----------------------------------------------------------------------===//
5551
// Backedges analysis
5652
//===----------------------------------------------------------------------===//
@@ -917,35 +913,16 @@ BufferDeallocation::handleInterface(RegionBranchTerminatorOpInterface op) {
917913
MutableOperandRange operands =
918914
op.getMutableSuccessorOperands(RegionBranchPoint::parent());
919915

920-
// Collect the values to deallocate and retain and use them to create the
921-
// dealloc operation.
922-
Block *block = op->getBlock();
923-
SmallVector<Value> memrefs, conditions, toRetain;
924-
if (failed(state.getMemrefsAndConditionsToDeallocate(
925-
builder, op.getLoc(), block, memrefs, conditions)))
926-
return failure();
927-
928-
state.getMemrefsToRetain(block, nullptr, OperandRange(operands), toRetain);
929-
if (memrefs.empty() && toRetain.empty())
930-
return op.getOperation();
931-
932-
auto deallocOp = builder.create<bufferization::DeallocOp>(
933-
op.getLoc(), memrefs, conditions, toRetain);
934-
935-
// We want to replace the current ownership of the retained values with the
936-
// result values of the dealloc operation as they are always unique.
937-
state.resetOwnerships(deallocOp.getRetained(), block);
938-
for (auto [retained, ownership] :
939-
llvm::zip(deallocOp.getRetained(), deallocOp.getUpdatedConditions()))
940-
state.updateOwnership(retained, ownership, block);
916+
SmallVector<Value> updatedOwnerships;
917+
auto result = deallocation_impl::insertDeallocOpForReturnLike(
918+
state, op, OperandRange(operands), updatedOwnerships);
919+
if (failed(result) || !*result)
920+
return result;
941921

942922
// Add an additional operand for every MemRef for the ownership indicator.
943923
if (!funcWithoutDynamicOwnership) {
944-
unsigned numMemRefs = llvm::count_if(operands, isMemrefOperand);
945924
SmallVector<Value> newOperands{OperandRange(operands)};
946-
auto ownershipValues =
947-
deallocOp.getUpdatedConditions().take_front(numMemRefs);
948-
newOperands.append(ownershipValues.begin(), ownershipValues.end());
925+
newOperands.append(updatedOwnerships.begin(), updatedOwnerships.end());
949926
operands.assign(newOperands);
950927
}
951928

mlir/lib/Dialect/GPU/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ add_mlir_dialect_library(MLIRGPUDialect
4848
add_mlir_dialect_library(MLIRGPUTransforms
4949
Transforms/AllReduceLowering.cpp
5050
Transforms/AsyncRegionRewriter.cpp
51+
Transforms/BufferDeallocationOpInterfaceImpl.cpp
5152
Transforms/DecomposeMemrefs.cpp
5253
Transforms/GlobalIdRewriter.cpp
5354
Transforms/KernelOutlining.cpp
@@ -79,6 +80,7 @@ add_mlir_dialect_library(MLIRGPUTransforms
7980
MLIRAffineUtils
8081
MLIRArithDialect
8182
MLIRAsyncDialect
83+
MLIRBufferizationDialect
8284
MLIRBuiltinToLLVMIRTranslation
8385
MLIRDataLayoutInterfaces
8486
MLIRExecutionEngineUtils
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===- BufferDeallocationOpInterfaceImpl.cpp ------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "mlir/Dialect/GPU/Transforms/BufferDeallocationOpInterfaceImpl.h"
10+
#include "mlir/Dialect/Bufferization/IR/BufferDeallocationOpInterface.h"
11+
#include "mlir/Dialect/Bufferization/IR/Bufferization.h"
12+
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
13+
14+
using namespace mlir;
15+
using namespace mlir::bufferization;
16+
17+
namespace {
18+
///
19+
struct GPUTerminatorOpInterface
20+
: public BufferDeallocationOpInterface::ExternalModel<
21+
GPUTerminatorOpInterface, gpu::TerminatorOp> {
22+
FailureOr<Operation *> process(Operation *op, DeallocationState &state,
23+
const DeallocationOptions &options) const {
24+
SmallVector<Value> updatedOperandOwnerships;
25+
return deallocation_impl::insertDeallocOpForReturnLike(
26+
state, op, {}, updatedOperandOwnerships);
27+
}
28+
};
29+
30+
} // namespace
31+
32+
void mlir::gpu::registerBufferDeallocationOpInterfaceExternalModels(
33+
DialectRegistry &registry) {
34+
registry.addExtension(+[](MLIRContext *ctx, GPUDialect *dialect) {
35+
gpu::TerminatorOp::attachInterface<GPUTerminatorOpInterface>(*ctx);
36+
});
37+
}

mlir/lib/Dialect/SCF/Transforms/BufferDeallocationOpInterfaceImpl.cpp

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,33 +47,12 @@ struct InParallelOpInterface
4747
FailureOr<Operation *> process(Operation *op, DeallocationState &state,
4848
const DeallocationOptions &options) const {
4949
auto inParallelOp = cast<scf::InParallelOp>(op);
50-
OpBuilder builder(op);
5150
if (!inParallelOp.getBody()->empty())
5251
return op->emitError("only supported when nested region is empty");
5352

54-
// Collect the values to deallocate and retain and use them to create the
55-
// dealloc operation.
56-
Block *block = op->getBlock();
57-
SmallVector<Value> memrefs, conditions, toRetain;
58-
if (failed(state.getMemrefsAndConditionsToDeallocate(
59-
builder, op->getLoc(), block, memrefs, conditions)))
60-
return failure();
61-
62-
state.getMemrefsToRetain(block, /*toBlock=*/nullptr, {}, toRetain);
63-
if (memrefs.empty() && toRetain.empty())
64-
return op;
65-
66-
auto deallocOp = builder.create<bufferization::DeallocOp>(
67-
op->getLoc(), memrefs, conditions, toRetain);
68-
69-
// We want to replace the current ownership of the retained values with the
70-
// result values of the dealloc operation as they are always unique.
71-
state.resetOwnerships(deallocOp.getRetained(), block);
72-
for (auto [retained, ownership] :
73-
llvm::zip(deallocOp.getRetained(), deallocOp.getUpdatedConditions()))
74-
state.updateOwnership(retained, ownership, block);
75-
76-
return op;
53+
SmallVector<Value> updatedOperandOwnership;
54+
return deallocation_impl::insertDeallocOpForReturnLike(
55+
state, op, {}, updatedOperandOwnership);
7756
}
7857
};
7958

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// RUN: mlir-opt %s -buffer-deallocation-pipeline --allow-unregistered-dialect | FileCheck %s
2+
3+
func.func @gpu_launch() {
4+
%c1 = arith.constant 1 : index
5+
gpu.launch blocks(%arg0, %arg1, %arg2) in (%arg6 = %c1, %arg7 = %c1, %arg8 = %c1)
6+
threads(%arg3, %arg4, %arg5) in (%arg9 = %c1, %arg10 = %c1, %arg11 = %c1) {
7+
%alloc = memref.alloc() : memref<2xf32>
8+
"test.memref_user"(%alloc) : (memref<2xf32>) -> ()
9+
gpu.terminator
10+
}
11+
return
12+
}
13+
14+
// CHECK-LABEL: func @gpu_launch
15+
// CHECK: gpu.launch
16+
// CHECK: [[ALLOC:%.+]] = memref.alloc(
17+
// CHECK: memref.dealloc [[ALLOC]]
18+
// CHECK: gpu.terminator

utils/bazel/llvm-project-overlay/mlir/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4927,6 +4927,7 @@ cc_library(
49274927
":ArithDialect",
49284928
":AsmParser",
49294929
":AsyncDialect",
4930+
":BufferizationDialect",
49304931
":ControlFlowDialect",
49314932
":DLTIDialect",
49324933
":DialectUtils",

0 commit comments

Comments
 (0)