21#include "llvm/Support/Debug.h"
24#define DEBUG_TYPE "static-memory-planner"
28#define GEN_PASS_DEF_STATICMEMORYPLANNERANALYSISPASS
29#include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
38struct AllocationCandidate {
39 memref::AllocOp alloc;
40 memref::DeallocOp dealloc;
42 int64_t sizeInBytes = 0;
43 int64_t alignment = 1;
52static memref::DeallocOp findUniqueDealloc(
Value allocValue) {
53 memref::DeallocOp deallocOp =
nullptr;
55 if (
auto dealloc = dyn_cast<memref::DeallocOp>(user)) {
65static int64_t computeSizeInBytes(MemRefType memrefType) {
66 int64_t numElements = memrefType.getNumElements();
67 unsigned elementSizeInBits = memrefType.getElementTypeBitWidth();
68 return (numElements * elementSizeInBits + 7) / 8;
77 for (
auto &candidate : candidates) {
82 Block *block = candidate.alloc->getBlock();
85 if (&op == candidate.alloc.getOperation())
87 if (&op == candidate.dealloc.getOperation())
92 allocInfos.push_back(info);
93 arenaAlignment = std::lcm(arenaAlignment, candidate.alignment);
95 return arenaAlignment;
102collectCandidates(FunctionOpInterface funcOp, llvm::Statistic &numSkipDynamic,
103 llvm::Statistic &numSkipNoDealloc,
104 llvm::Statistic &numEligible) {
107 funcOp->walk([&](memref::AllocOp allocOp) {
108 MemRefType memrefType = allocOp.getType();
111 if (!memrefType.hasStaticShape()) {
117 memref::DeallocOp deallocOp = findUniqueDealloc(allocOp.getResult());
123 if (deallocOp->getBlock() != allocOp->getBlock()) {
130 AllocationCandidate candidate;
131 candidate.alloc = allocOp;
132 candidate.dealloc = deallocOp;
133 candidate.sizeInBytes = computeSizeInBytes(memrefType);
134 candidate.alignment = allocOp.getAlignment().value_or(1);
135 candidates.push_back(candidate);
143static FailureOr<Value> createArena(
OpBuilder &builder,
144 FunctionOpInterface funcOp,
145 StringRef arenaMode,
int64_t totalSize,
149 if (arenaMode ==
"allocate") {
150 auto arenaType = MemRefType::get({totalSize}, builder.
getI8Type());
152 memref::AllocOp::create(builder, loc, arenaType,
ValueRange{},
154 LLVM_DEBUG(llvm::dbgs()
155 <<
"[static-memory-planner] created arena via AllocOp: size="
156 << totalSize <<
" bytes, alignment=" << arenaAlignment
158 return arenaAlloc.getResult();
161 if (arenaMode ==
"arg") {
162 if (funcOp.getNumArguments() == 0)
163 return funcOp->emitError(
164 "arena-mode=arg requires at least one function argument");
166 Value arenaValue = funcOp.getArgument(0);
167 auto arenaType = dyn_cast<MemRefType>(arenaValue.
getType());
168 if (!arenaType || !arenaType.getElementType().isInteger(8) ||
169 arenaType.getRank() != 1)
170 return funcOp->emitError(
171 "arena-mode=arg requires first argument to be memref<...xi8>");
173 LLVM_DEBUG(llvm::dbgs()
174 <<
"[static-memory-planner] using arena from function arg 0\n");
178 return funcOp->emitError(
"invalid arena-mode: '" + arenaMode +
179 "' (must be 'allocate' or 'arg')");
185 for (
auto &candidate : candidates) {
187 Location loc = candidate.alloc.getLoc();
188 MemRefType originalType = candidate.alloc.getType();
192 auto view = memref::ViewOp::create(builder, loc, originalType, arenaValue,
195 candidate.alloc.getResult().replaceAllUsesWith(view.getResult());
196 candidate.alloc.erase();
197 candidate.dealloc.erase();
205struct StaticMemoryPlannerAnalysisPass
207 StaticMemoryPlannerAnalysisPass> {
209 using Base = bufferization::impl::StaticMemoryPlannerAnalysisPassBase<
210 StaticMemoryPlannerAnalysisPass>;
213 void runOnOperation()
override;
216void StaticMemoryPlannerAnalysisPass::runOnOperation() {
217 auto funcOp = llvm::cast<FunctionOpInterface>(getOperation());
220 for (Type resultType : funcOp.getResultTypes()) {
221 if (isa<BaseMemRefType>(resultType)) {
222 funcOp->emitError(
"static-memory-planner does not support functions "
223 "with memref return types");
224 return signalPassFailure();
229 SmallVector<AllocationCandidate> candidates =
230 collectCandidates(funcOp, numSkipDynamic, numSkipNoDealloc, numEligible);
232 if (candidates.empty())
236 SmallVector<bufferization::MemoryPlannerAlloc> allocInfos;
237 int64_t arenaAlignment = buildAllocInfos(candidates, allocInfos);
240 SmallVector<int64_t> offsets;
242 case bufferization::MemoryPlannerAlgorithm::Trivial:
245 case bufferization::MemoryPlannerAlgorithm::BestFit:
251 int64_t totalSize = 0;
252 for (
size_t i = 0; i < candidates.size(); ++i) {
253 candidates[i].offset = offsets[i];
254 totalSize = std::max(totalSize, offsets[i] + candidates[i].sizeInBytes);
255 LLVM_DEBUG(llvm::dbgs()
256 <<
"[static-memory-planner] offset=" << candidates[i].offset
257 <<
" size=" << candidates[i].sizeInBytes
258 <<
" alignment=" << candidates[i].alignment <<
"\n");
262 Operation *firstAlloc = candidates.front().alloc;
263 OpBuilder builder(firstAlloc);
264 FailureOr<Value> arenaValue =
265 createArena(builder, funcOp, arenaMode, totalSize, arenaAlignment);
267 return signalPassFailure();
270 rewriteAllocations(candidates, *arenaValue);
Block represents an ordered list of Operations.
IntegerAttr getI64IntegerAttr(int64_t value)
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
Operation is the basic unit of execution within MLIR.
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
user_range getUsers() const
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
StaticMemoryPlannerAnalysisPassBase Base
llvm::SmallVector< int64_t > trivialMemoryPlanner(int64_t arenaAlignment, llvm::ArrayRef< MemoryPlannerAlloc > allocs)
Sequential packing without lifetime overlap.
llvm::SmallVector< int64_t > bestFitMemoryPlanner(int64_t arenaAlignment, llvm::ArrayRef< MemoryPlannerAlloc > allocs)
Best-fit packing with lifetime-aware gap reuse.
Include the generated interface declarations.
Descriptor for a single allocation to be placed by the memory planner.