27#define GEN_PASS_DEF_BUFFERHOISTINGPASS
28#define GEN_PASS_DEF_BUFFERLOOPHOISTINGPASS
29#define GEN_PASS_DEF_PROMOTEBUFFERSTOSTACKPASS
30#include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
40 return isa<LoopLikeOpInterface, RegionBranchOpInterface>(op);
50 if (isa<LoopLikeOpInterface>(op))
55 auto regionInterface = dyn_cast<RegionBranchOpInterface>(op);
59 return regionInterface.hasLoop();
71 auto allocOp = dyn_cast<AllocationOpInterface>(op);
79 auto allocOp = dyn_cast<AllocationOpInterface>(op);
88 unsigned maxRankOfAllocatedMemRef) {
89 auto type = dyn_cast<ShapedType>(alloc.
getType());
92 if (!type.hasStaticShape()) {
98 if (type.getRank() <= maxRankOfAllocatedMemRef) {
101 return operand.getDefiningOp<memref::RankOp>();
111 std::optional<int64_t> numElements = type.tryGetNumElements();
116 *numElements >
static_cast<int64_t>(maximumSizeInBytes * 8ULL / bitwidth))
118 return *numElements * bitwidth <= maximumSizeInBytes * 8;
126 for (
auto *use : alias.getUsers()) {
130 if (isa<RegionBranchTerminatorOpInterface>(use) &&
131 use->getParentRegion() == parentRegion)
167struct BufferAllocationHoistingStateBase {
175 Block *placementBlock;
179 Block *placementBlock)
180 : dominators(dominators), allocValue(allocValue),
181 placementBlock(placementBlock) {}
185template <
typename StateT>
190 postDominators(op), scopeOp(op) {}
196 allocsAndAllocas.push_back(std::get<0>(entry));
197 scopeOp->walk([&](memref::AllocaOp op) {
198 allocsAndAllocas.push_back(op.getMemref());
201 for (
auto allocValue : allocsAndAllocas) {
202 if (!StateT::shouldHoistOpType(allocValue.getDefiningOp()))
204 Operation *definingOp = allocValue.getDefiningOp();
205 assert(definingOp &&
"No defining op");
209 if (!dominators.isReachableFromEntry(allocValue.getParentBlock()))
212 auto resultAliases = aliases.resolve(allocValue);
217 StateT state(&dominators, allocValue, allocValue.getParentBlock());
220 Block *dependencyBlock =
nullptr;
225 Block *depBlock = depValue.getParentBlock();
226 if (!dependencyBlock || dominators.dominates(dependencyBlock, depBlock))
227 dependencyBlock = depBlock;
233 Block *placementBlock = findPlacementBlock(
234 state, state.computeUpperBound(dominatorBlock, dependencyBlock));
236 allocValue, placementBlock, liveness);
239 Operation *allocOperation = allocValue.getDefiningOp();
248 Block *findPlacementBlock(StateT &state,
Block *upperBound) {
249 Block *currentBlock = state.placementBlock;
260 (parentBlock = parentOp->
getBlock()) &&
262 dominators.properlyDominates(upperBound, currentBlock))) {
269 idom = dominators.getNode(currentBlock)->getIDom();
271 if (idom && dominators.properlyDominates(parentBlock, idom->getBlock())) {
274 currentBlock = idom->getBlock();
275 state.recordMoveToDominator(currentBlock);
283 !state.isLegalPlacement(parentOp))
287 currentBlock = parentBlock;
288 state.recordMoveToParent(currentBlock);
292 return state.placementBlock;
297 DominanceInfo dominators;
301 PostDominanceInfo postDominators;
304 llvm::DenseMap<Value, Block *> placementBlocks;
314struct BufferAllocationHoistingState : BufferAllocationHoistingStateBase {
315 using BufferAllocationHoistingStateBase::BufferAllocationHoistingStateBase;
318 Block *computeUpperBound(
Block *dominatorBlock,
Block *dependencyBlock) {
321 if (!dependencyBlock)
322 return dominatorBlock;
326 return dominators->properlyDominates(dominatorBlock, dependencyBlock)
332 bool isLegalPlacement(Operation *op) {
return !
isLoop(op); }
335 static bool shouldHoistOpType(Operation *op) {
340 void recordMoveToDominator(
Block *block) { placementBlock = block; }
343 void recordMoveToParent(
Block *block) { recordMoveToDominator(block); }
348struct BufferAllocationLoopHoistingState : BufferAllocationHoistingStateBase {
349 using BufferAllocationHoistingStateBase::BufferAllocationHoistingStateBase;
352 Block *aliasDominatorBlock =
nullptr;
355 Block *computeUpperBound(
Block *dominatorBlock,
Block *dependencyBlock) {
356 aliasDominatorBlock = dominatorBlock;
359 return dependencyBlock ? dependencyBlock :
nullptr;
367 bool isLegalPlacement(Operation *op) {
369 !dominators->dominates(aliasDominatorBlock, op->
getBlock());
373 static bool shouldHoistOpType(Operation *op) {
379 void recordMoveToDominator(
Block *block) {}
382 void recordMoveToParent(
Block *block) { placementBlock = block; }
392 BufferPlacementPromotion(Operation *op)
393 : BufferPlacementTransformationBase(op) {}
398 Value alloc = std::get<0>(entry);
399 Operation *dealloc = std::get<1>(entry);
404 if (!isSmallAlloc(alloc) || dealloc ||
412 OpBuilder builder(startOperation);
414 if (
auto allocInterface = dyn_cast<AllocationOpInterface>(allocOp)) {
415 std::optional<Operation *> alloca =
416 allocInterface.buildPromotedAlloc(builder, alloc);
433struct BufferHoistingPass
436 void runOnOperation()
override {
438 BufferAllocationHoisting<BufferAllocationHoistingState> optimizer(
445struct BufferLoopHoistingPass
447 BufferLoopHoistingPass> {
449 void runOnOperation()
override {
457class PromoteBuffersToStackPass
459 PromoteBuffersToStackPass> {
463 explicit PromoteBuffersToStackPass(std::function<
bool(Value)> isSmallAlloc)
464 : isSmallAlloc(std::move(isSmallAlloc)) {}
466 LogicalResult
initialize(MLIRContext *context)
override {
467 if (isSmallAlloc ==
nullptr) {
468 isSmallAlloc = [=](Value alloc) {
470 maxRankOfAllocatedMemRef);
476 void runOnOperation()
override {
478 BufferPlacementPromotion optimizer(getOperation());
479 optimizer.promote(isSmallAlloc);
483 std::function<bool(Value)> isSmallAlloc;
489 BufferAllocationHoisting<BufferAllocationLoopHoistingState> optimizer(op);
494 std::function<
bool(
Value)> isSmallAlloc) {
495 return std::make_unique<PromoteBuffersToStackPass>(std::move(isSmallAlloc));
static bool leavesAllocationScope(Region *parentRegion, const BufferViewFlowAnalysis::ValueSetT &aliases)
Checks whether the given aliases leave the allocation scope.
static bool isKnownControlFlowInterface(Operation *op)
Returns true if the given operation implements a known high-level region- based control-flow interfac...
static bool hasAllocationScope(Value alloc, const BufferViewFlowAnalysis &aliasAnalysis)
Checks, if an automated allocation scope for a given alloc value exists.
static bool isSequentialLoop(Operation *op)
Return whether the given operation is a loop with sequential execution semantics.
static bool isLoop(Operation *op)
Returns true if the given operation represents a loop by testing whether it implements the LoopLikeOp...
static bool allowAllocDominateBlockHoisting(Operation *op)
Returns true if the given operation implements the AllocationOpInterface and it supports the dominate...
static bool allowAllocLoopHoisting(Operation *op)
Returns true if the given operation implements the AllocationOpInterface and it supports the loop hoi...
static bool defaultIsSmallAlloc(Value alloc, unsigned maximumSizeInBytes, unsigned maxRankOfAllocatedMemRef)
Check if the size of the allocation is less than the given size.
LogicalResult initialize(unsigned origNumLoops, ArrayRef< ReassociationIndices > foldedIterationDims)
Block represents an ordered list of Operations.
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
A straight-forward alias analysis which ensures that all dependencies of all values will be determine...
SmallPtrSet< Value, 16 > ValueSetT
ValueSetT resolve(Value value) const
Find all immediate and indirect views upon this value.
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
llvm::TypeSize getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
A class for computing basic dominance information.
A trait of region holding operations that define a new scope for automatic allocations,...
Operation is the basic unit of execution within MLIR.
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Block * getBlock()
Returns the operation block that contains this operation.
operand_range getOperands()
Returns an iterator on the underlying Value's.
void moveBefore(Operation *existingOp)
Unlink this operation from its current block and insert it right before existingOp which may be in th...
void replaceAllUsesWith(ValuesT &&values)
Replace all uses of results of this operation with the provided 'values'.
void erase()
Remove this operation from its parent block and delete it.
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Operation * getParentOp()
Return the parent operation this region is attached to.
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.
Block * getParentBlock()
Return the Block in which this Value is defined.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Region * getParentRegion()
Return the Region in which this Value is defined.
static Operation * getStartOperation(Value allocValue, Block *placementBlock, const Liveness &liveness)
Get the start operation to place the given alloc value within the specified placement block.
std::tuple< Value, Operation * > AllocEntry
Represents a tuple of allocValue and deallocOperation.
void hoistBuffersFromLoops(Operation *op)
Within the given operation, hoist buffers from loops where possible.
std::unique_ptr<::mlir::Pass > createPromoteBuffersToStackPass()
Block * findCommonDominator(Value value, const BufferViewFlowAnalysis::ValueSetT &values, const DominatorT &doms)
Finds a common dominator for the given value while taking the positions of the values in the value se...
void promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
Include the generated interface declarations.
llvm::DomTreeNodeBase< Block > DominanceInfoNode
llvm::function_ref< Fn > function_ref