Skip to content

[mlir][bufferization] OwnershipBasedBufferDeallocation: support unstructured control flow loops #66657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ struct DeallocationOptions {
// pass the ownership of MemRef values instead of adhering to the function
// boundary ABI.
bool privateFuncDynamicOwnership = false;

// Allows the pass to insert `bufferization.clone` operations. This is useful
// for supporting IR that does not adhere to the function boundary ABI
// initially (excl. external functions) and to support operations with results
// with 'Unknown' ownership. However, it requires that all buffer writes
// dominate all buffer reads (i.e., only enable this option if your IR is
// guaranteed to have this property).
bool allowCloning = false;
};

/// This class collects all the state that we need to perform the buffer
Expand Down Expand Up @@ -142,8 +150,9 @@ class DeallocationState {
/// a new SSA value, returned as the first element of the pair, which has
/// 'Unique' ownership and can be used instead of the passed Value with the
/// the ownership indicator returned as the second element of the pair.
std::pair<Value, Value>
getMemrefWithUniqueOwnership(OpBuilder &builder, Value memref, Block *block);
FailureOr<std::pair<Value, Value>>
getMemrefWithUniqueOwnership(const DeallocationOptions &options,
OpBuilder &builder, Value memref, Block *block);

/// Given two basic blocks and the values passed via block arguments to the
/// destination block, compute the list of MemRefs that have to be retained in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def BufferDeallocationOpInterface :
method (which is especially important if operations are created that
cannot be easily canonicalized away anymore).
}],
/*retType=*/"std::pair<Value, Value>",
/*retType=*/"FailureOr<std::pair<Value, Value>>",
/*methodName=*/"materializeUniqueOwnershipForMemref",
/*args=*/(ins "DeallocationState &":$state,
"const DeallocationOptions &":$options,
Expand All @@ -65,7 +65,7 @@ def BufferDeallocationOpInterface :
/*methodBody=*/[{}],
/*defaultImplementation=*/[{
return state.getMemrefWithUniqueOwnership(
builder, memref, memref.getParentBlock());
options, builder, memref, memref.getParentBlock());
}]>,
];
}
Expand Down
17 changes: 17 additions & 0 deletions mlir/include/mlir/Dialect/Bufferization/Pipelines/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

namespace mlir {
namespace bufferization {
struct DeallocationOptions;

/// Options for the buffer deallocation pipeline.
struct BufferDeallocationPipelineOptions
Expand All @@ -28,6 +29,22 @@ struct BufferDeallocationPipelineOptions
"dynamically pass ownership of memrefs to callees. This can enable "
"earlier deallocations."),
llvm::cl::init(false)};
PassOptions::Option<bool> allowCloning{
*this, "allow-cloning",
llvm::cl::desc(
"Allows the pass to insert `bufferization.clone` operations. This is "
"useful for supporting IR that does not adhere to the function "
"boundary ABI initially (excl. external functions) and to support "
"operations with results with 'Unknown' ownership. However, it "
"requires that all buffer writes dominate all buffer reads (i.e., "
"only enable this option if your IR is guaranteed to have this "
"property)."),
llvm::cl::init(false)};

/// Convert this BufferDeallocationPipelineOptions struct to a
/// DeallocationOptions struct to be passed to the
/// OwnershipBasedBufferDeallocationPass.
DeallocationOptions asDeallocationOptions() const;
};

//===----------------------------------------------------------------------===//
Expand Down
8 changes: 5 additions & 3 deletions mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef MLIR_DIALECT_BUFFERIZATION_TRANSFORMS_PASSES_H
#define MLIR_DIALECT_BUFFERIZATION_TRANSFORMS_PASSES_H

#include "mlir/Dialect/Bufferization/IR/BufferDeallocationOpInterface.h"
#include "mlir/Pass/Pass.h"

namespace mlir {
Expand Down Expand Up @@ -31,7 +32,7 @@ std::unique_ptr<Pass> createBufferDeallocationPass();
/// Creates an instance of the OwnershipBasedBufferDeallocation pass to free all
/// allocated buffers.
std::unique_ptr<Pass> createOwnershipBasedBufferDeallocationPass(
bool privateFuncDynamicOwnership = false);
const DeallocationOptions &options = DeallocationOptions());

/// Creates a pass that optimizes `bufferization.dealloc` operations. For
/// example, it reduces the number of alias checks needed at runtime using
Expand Down Expand Up @@ -134,8 +135,9 @@ func::FuncOp buildDeallocationLibraryFunction(OpBuilder &builder, Location loc,
LogicalResult deallocateBuffers(Operation *op);

/// Run ownership basedbuffer deallocation.
LogicalResult deallocateBuffersOwnershipBased(FunctionOpInterface op,
bool privateFuncDynamicOwnership);
LogicalResult deallocateBuffersOwnershipBased(
FunctionOpInterface op,
const DeallocationOptions &options = DeallocationOptions());

/// Creates a pass that moves allocations upwards to reduce the number of
/// required copies that are inserted during the BufferDeallocation pass.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ def OwnershipBasedBufferDeallocation : Pass<
"Allows to add additional arguments to private functions to "
"dynamically pass ownership of memrefs to callees. This can enable "
"earlier deallocations.">,
Option<"allowCloning", "allow-cloning", "bool", /*default=*/"false",
"Allows the pass to insert `bufferization.clone` operations. This "
"is useful for supporting IR that does not adhere to the function "
"boundary ABI initially (excl. external functions) and to support "
"operations with results with 'Unknown' ownership. However, it "
"requires that all buffer writes dominate all buffer reads (i.e., "
"only enable this option if your IR is guaranteed to have this "
"property).">,
];
let constructor = "mlir::bufferization::createOwnershipBasedBufferDeallocationPass()";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ struct SelectOpInterface
return op; // nothing to do
}

std::pair<Value, Value>
FailureOr<std::pair<Value, Value>>
materializeUniqueOwnershipForMemref(Operation *op, DeallocationState &state,
const DeallocationOptions &options,
OpBuilder &builder, Value value) const {
Expand All @@ -64,14 +64,14 @@ struct SelectOpInterface
Block *block = value.getParentBlock();
if (!state.getOwnership(selectOp.getTrueValue(), block).isUnique() ||
!state.getOwnership(selectOp.getFalseValue(), block).isUnique())
return state.getMemrefWithUniqueOwnership(builder, value,
return state.getMemrefWithUniqueOwnership(options, builder, value,
value.getParentBlock());

Value ownership = builder.create<arith::SelectOp>(
op->getLoc(), selectOp.getCondition(),
state.getOwnership(selectOp.getTrueValue(), block).getIndicator(),
state.getOwnership(selectOp.getFalseValue(), block).getIndicator());
return {selectOp.getResult(), ownership};
return std::make_pair(selectOp.getResult(), ownership);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,21 @@ void DeallocationState::getLiveMemrefsIn(Block *block,
memrefs.append(liveMemrefs);
}

std::pair<Value, Value>
DeallocationState::getMemrefWithUniqueOwnership(OpBuilder &builder,
Value memref, Block *block) {
FailureOr<std::pair<Value, Value>>
DeallocationState::getMemrefWithUniqueOwnership(
const DeallocationOptions &options, OpBuilder &builder, Value memref,
Block *block) {
auto iter = ownershipMap.find({memref, block});
assert(iter != ownershipMap.end() &&
"Value must already have been registered in the ownership map");

Ownership ownership = iter->second;
if (ownership.isUnique())
return {memref, ownership.getIndicator()};
return std::make_pair(memref, ownership.getIndicator());

if (!options.allowCloning)
return emitError(memref.getLoc(),
"MemRef value does not have valid ownership");

// Instead of inserting a clone operation we could also insert a dealloc
// operation earlier in the block and use the updated ownerships returned by
Expand All @@ -155,7 +160,7 @@ DeallocationState::getMemrefWithUniqueOwnership(OpBuilder &builder,
Value newMemref = cloneOp.getResult();
updateOwnership(newMemref, condition);
memrefsToDeallocatePerBlock[newMemref.getParentBlock()].push_back(newMemref);
return {newMemref, condition};
return std::make_pair(newMemref, condition);
}

void DeallocationState::getMemrefsToRetain(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,35 @@

#include "mlir/Dialect/Bufferization/Pipelines/Passes.h"

#include "mlir/Dialect/Bufferization/IR/BufferDeallocationOpInterface.h"
#include "mlir/Dialect/Bufferization/Transforms/Passes.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/MemRef/Transforms/Passes.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Transforms/Passes.h"

using namespace mlir;
using namespace bufferization;

//===----------------------------------------------------------------------===//
// Pipeline implementation.
//===----------------------------------------------------------------------===//

DeallocationOptions
BufferDeallocationPipelineOptions::asDeallocationOptions() const {
DeallocationOptions opts;
opts.privateFuncDynamicOwnership = privateFunctionDynamicOwnership.getValue();
opts.allowCloning = allowCloning.getValue();
return opts;
}

void mlir::bufferization::buildBufferDeallocationPipeline(
OpPassManager &pm, const BufferDeallocationPipelineOptions &options) {
pm.addNestedPass<func::FuncOp>(
memref::createExpandReallocPass(/*emitDeallocs=*/false));
pm.addNestedPass<func::FuncOp>(createCanonicalizerPass());
pm.addNestedPass<func::FuncOp>(createOwnershipBasedBufferDeallocationPass(
options.privateFunctionDynamicOwnership.getValue()));
options.asDeallocationOptions()));
pm.addNestedPass<func::FuncOp>(createCanonicalizerPass());
pm.addNestedPass<func::FuncOp>(createBufferDeallocationSimplificationPass());
pm.addPass(createLowerDeallocationsPass());
Expand Down
1 change: 1 addition & 0 deletions mlir/lib/Dialect/Bufferization/Pipelines/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_mlir_dialect_library(MLIRBufferizationPipelines
${MLIR_MAIN_INCLUDE_DIR}/mlir/Dialect/Bufferization

LINK_LIBS PUBLIC
MLIRBufferizationDialect
MLIRBufferizationTransforms
MLIRMemRefTransforms
MLIRFuncDialect
Expand Down
Loading