26 namespace bufferization {
27 #define GEN_PASS_DEF_BUFFERHOISTING
28 #define GEN_PASS_DEF_BUFFERLOOPHOISTING
29 #define GEN_PASS_DEF_PROMOTEBUFFERSTOSTACK
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>();
108 return type.getNumElements() * bitwidth <= maximumSizeInBytes * 8;
115 for (
Value alias : aliases) {
116 for (
auto *use : alias.getUsers()) {
120 if (isa<RegionBranchTerminatorOpInterface>(use) &&
121 use->getParentRegion() == parentRegion)
157 struct BufferAllocationHoistingStateBase {
165 Block *placementBlock;
169 Block *placementBlock)
170 : dominators(dominators), allocValue(allocValue),
171 placementBlock(placementBlock) {}
175 template <
typename StateT>
180 postDominators(op), scopeOp(op) {}
186 allocsAndAllocas.push_back(std::get<0>(entry));
187 scopeOp->walk([&](memref::AllocaOp op) {
188 allocsAndAllocas.push_back(op.getMemref());
191 for (
auto allocValue : allocsAndAllocas) {
192 if (!StateT::shouldHoistOpType(allocValue.getDefiningOp()))
194 Operation *definingOp = allocValue.getDefiningOp();
195 assert(definingOp &&
"No defining op");
197 auto resultAliases = aliases.resolve(allocValue);
199 Block *dominatorBlock =
202 StateT state(&dominators, allocValue, allocValue.getParentBlock());
205 Block *dependencyBlock =
nullptr;
209 for (
Value depValue : operands) {
210 Block *depBlock = depValue.getParentBlock();
211 if (!dependencyBlock || dominators.dominates(dependencyBlock, depBlock))
212 dependencyBlock = depBlock;
218 Block *placementBlock = findPlacementBlock(
219 state, state.computeUpperBound(dominatorBlock, dependencyBlock));
221 allocValue, placementBlock, liveness);
224 Operation *allocOperation = allocValue.getDefiningOp();
233 Block *findPlacementBlock(StateT &state,
Block *upperBound) {
234 Block *currentBlock = state.placementBlock;
245 (parentBlock = parentOp->
getBlock()) &&
247 dominators.properlyDominates(upperBound, currentBlock))) {
254 idom = dominators.getNode(currentBlock)->getIDom();
256 if (idom && dominators.properlyDominates(parentBlock, idom->getBlock())) {
259 currentBlock = idom->getBlock();
260 state.recordMoveToDominator(currentBlock);
268 !state.isLegalPlacement(parentOp))
272 currentBlock = parentBlock;
273 state.recordMoveToParent(currentBlock);
277 return state.placementBlock;
299 struct BufferAllocationHoistingState : BufferAllocationHoistingStateBase {
300 using BufferAllocationHoistingStateBase::BufferAllocationHoistingStateBase;
303 Block *computeUpperBound(
Block *dominatorBlock,
Block *dependencyBlock) {
306 if (!dependencyBlock)
307 return dominatorBlock;
311 return dominators->properlyDominates(dominatorBlock, dependencyBlock)
320 static bool shouldHoistOpType(
Operation *op) {
325 void recordMoveToDominator(
Block *block) { placementBlock = block; }
328 void recordMoveToParent(
Block *block) { recordMoveToDominator(block); }
333 struct BufferAllocationLoopHoistingState : BufferAllocationHoistingStateBase {
334 using BufferAllocationHoistingStateBase::BufferAllocationHoistingStateBase;
337 Block *aliasDominatorBlock =
nullptr;
340 Block *computeUpperBound(
Block *dominatorBlock,
Block *dependencyBlock) {
341 aliasDominatorBlock = dominatorBlock;
344 return dependencyBlock ? dependencyBlock :
nullptr;
354 !dominators->dominates(aliasDominatorBlock, op->
getBlock());
358 static bool shouldHoistOpType(
Operation *op) {
364 void recordMoveToDominator(
Block *block) {}
367 void recordMoveToParent(
Block *block) { placementBlock = block; }
383 Value alloc = std::get<0>(entry);
389 if (!isSmallAlloc(alloc) || dealloc ||
399 if (
auto allocInterface = dyn_cast<AllocationOpInterface>(allocOp)) {
400 std::optional<Operation *> alloca =
401 allocInterface.buildPromotedAlloc(builder, alloc);
418 struct BufferHoistingPass
419 :
public bufferization::impl::BufferHoistingBase<BufferHoistingPass> {
421 void runOnOperation()
override {
423 BufferAllocationHoisting<BufferAllocationHoistingState> optimizer(
430 struct BufferLoopHoistingPass
431 :
public bufferization::impl::BufferLoopHoistingBase<
432 BufferLoopHoistingPass> {
434 void runOnOperation()
override {
442 class PromoteBuffersToStackPass
443 :
public bufferization::impl::PromoteBuffersToStackBase<
444 PromoteBuffersToStackPass> {
446 PromoteBuffersToStackPass(
unsigned maxAllocSizeInBytes,
447 unsigned maxRankOfAllocatedMemRef) {
448 this->maxAllocSizeInBytes = maxAllocSizeInBytes;
449 this->maxRankOfAllocatedMemRef = maxRankOfAllocatedMemRef;
452 explicit PromoteBuffersToStackPass(std::function<
bool(
Value)> isSmallAlloc)
453 : isSmallAlloc(std::move(isSmallAlloc)) {}
455 LogicalResult initialize(
MLIRContext *context)
override {
456 if (isSmallAlloc ==
nullptr) {
457 isSmallAlloc = [=](
Value alloc) {
459 maxRankOfAllocatedMemRef);
465 void runOnOperation()
override {
467 BufferPlacementPromotion optimizer(getOperation());
468 optimizer.promote(isSmallAlloc);
472 std::function<bool(
Value)> isSmallAlloc;
478 BufferAllocationHoisting<BufferAllocationLoopHoistingState> optimizer(op);
483 return std::make_unique<BufferHoistingPass>();
487 return std::make_unique<BufferLoopHoistingPass>();
491 unsigned maxAllocSizeInBytes,
unsigned maxRankOfAllocatedMemRef) {
492 return std::make_unique<PromoteBuffersToStackPass>(maxAllocSizeInBytes,
493 maxRankOfAllocatedMemRef);
497 std::function<
bool(
Value)> isSmallAlloc) {
498 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.
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...
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.
MLIRContext is the top-level object for a collection of MLIR operations.
This class helps build Operations.
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.
A class for computing basic postdominance information.
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< Pass > createBufferHoistingPass()
Creates a pass that moves allocations upwards to reduce the number of required copies that are insert...
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...
std::unique_ptr< Pass > createBufferLoopHoistingPass()
Creates a pass that moves allocations upwards out of loops.
std::unique_ptr< Pass > createPromoteBuffersToStackPass(unsigned maxAllocSizeInBytes=1024, unsigned maxRankOfAllocatedMemRef=1)
Creates a pass that promotes heap-based allocations to stack-based ones.
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