MLIR 24.0.0git
BufferOptimizations.cpp
Go to the documentation of this file.
1//===- BufferOptimizations.cpp - pre-pass optimizations for bufferization -===//
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// This file implements logic for three optimization passes. The first two
10// passes try to move alloc nodes out of blocks to reduce the number of
11// allocations and copies during buffer deallocation. The third pass tries to
12// convert heap-based allocations to stack-based allocations, if possible.
13
15
22#include "mlir/IR/Operation.h"
26#include "mlir/Pass/Pass.h"
27
28namespace mlir {
29namespace bufferization {
30#define GEN_PASS_DEF_BUFFERHOISTINGPASS
31#define GEN_PASS_DEF_BUFFERLOOPHOISTINGPASS
32#define GEN_PASS_DEF_PROMOTEBUFFERSTOSTACKPASS
33#include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
34} // namespace bufferization
35} // namespace mlir
36
37using namespace mlir;
38using namespace mlir::bufferization;
39
40/// Returns true if the given operation implements a known high-level region-
41/// based control-flow interface.
43 return isa<LoopLikeOpInterface, RegionBranchOpInterface>(op);
44}
45
46/// Returns true if the given operation represents a loop by testing whether it
47/// implements the `LoopLikeOpInterface` or the `RegionBranchOpInterface`. In
48/// the case of a `RegionBranchOpInterface`, it checks all region-based control-
49/// flow edges for cycles.
50static bool isLoop(Operation *op) {
51 // If the operation implements the `LoopLikeOpInterface` it can be considered
52 // a loop.
53 if (isa<LoopLikeOpInterface>(op))
54 return true;
55
56 // If the operation does not implement the `RegionBranchOpInterface`, it is
57 // (currently) not possible to detect a loop.
58 auto regionInterface = dyn_cast<RegionBranchOpInterface>(op);
59 if (!regionInterface)
60 return false;
61
62 return regionInterface.hasLoop();
63}
64
65/// Return whether the given operation is a loop with sequential execution
66/// semantics.
67static bool isSequentialLoop(Operation *op) {
68 return !op->hasTrait<OpTrait::HasParallelRegion>() && isLoop(op);
69}
70
71/// Returns true if an allocation can be reused across iterations when its
72/// aliases have a common dominator outside the while loop.
73/// `aliases` must contain the complete forward alias set of the allocation.
74static bool
75canHoistFromWhile(scf::WhileOp loop,
76 const BufferViewFlowAnalysis::ValueSetT &aliases) {
77 // Require a parent that stops further loop hoisting. Otherwise, the
78 // dominator-based path could move the allocation across an enclosing loop
79 // with unmodeled parallel execution.
80 if (isSequentialLoop(loop->getParentOp()))
81 return false;
82
83 // Check all aliases, including loop results: memory effects alone do not rule
84 // out capture or uses that depend on allocation identity or lifetime.
85 for (Value alias : aliases) {
86 if (!isa<BaseMemRefType>(alias.getType()))
87 return false;
88 for (OpOperand &use : alias.getUses()) {
89 Operation *user = use.getOwner();
90 if (user->getParentOp() != loop && user->getBlock() != loop->getBlock())
91 return false;
92 if (auto load = dyn_cast<memref::LoadOp>(user)) {
93 // Reusing the allocation across iterations can invalidate invariant
94 // loads if different iterations store different values to the same
95 // location.
96 if (load.getInvariant())
97 return false;
98 continue;
99 }
100 if (auto store = dyn_cast<memref::StoreOp>(user)) {
101 if (store.getMemref() != alias)
102 return false;
103 continue;
104 }
105 // ViewLikeOpInterface describes aliases but does not guarantee that
106 // changing allocation identity preserves the view's access semantics.
107 if (isa<memref::CastOp, memref::SubViewOp>(user))
108 continue;
109 // scf.condition only forwards memrefs to after or to the loop results.
110 // Reject scf.yield to keep aliases out of the next iteration.
111 if (user != loop.getConditionOp().getOperation())
112 return false;
114 }
115 return true;
116}
118/// Returns true if the given operation implements the AllocationOpInterface
119/// and it supports the dominate block hoisting.
121 auto allocOp = dyn_cast<AllocationOpInterface>(op);
122 return allocOp &&
123 static_cast<uint8_t>(allocOp.getHoistingKind() & HoistingKind::Block);
124}
126/// Returns true if the given operation implements the AllocationOpInterface
127/// and it supports the loop hoisting.
129 auto allocOp = dyn_cast<AllocationOpInterface>(op);
130 return allocOp &&
131 static_cast<uint8_t>(allocOp.getHoistingKind() & HoistingKind::Loop);
132}
134/// Check if the size of the allocation is less than the given size. The
135/// transformation is only applied to small buffers since large buffers could
136/// exceed the stack space.
137static bool defaultIsSmallAlloc(Value alloc, unsigned maximumSizeInBytes,
138 unsigned maxRankOfAllocatedMemRef) {
139 auto type = dyn_cast<ShapedType>(alloc.getType());
140 if (!type || !alloc.getDefiningOp<memref::AllocOp>())
141 return false;
142 if (!type.hasStaticShape()) {
143 // Check if the dynamic shape dimension of the alloc is produced by
144 // `memref.rank`. If this is the case, it is likely to be small.
145 // Furthermore, the dimension is limited to the maximum rank of the
146 // allocated memref to avoid large values by multiplying several small
147 // values.
148 if (type.getRank() <= maxRankOfAllocatedMemRef) {
149 return llvm::all_of(alloc.getDefiningOp()->getOperands(),
150 [&](Value operand) {
151 return operand.getDefiningOp<memref::RankOp>();
152 });
153 }
154 return false;
155 }
156 // Skip if the element type has no data layout support (e.g., nested memrefs).
157 Type elemType = type.getElementType();
158 if (!elemType.isIntOrFloat() &&
159 !isa<ComplexType, IndexType, VectorType>(elemType) &&
160 !isa<DataLayoutTypeInterface>(elemType))
161 return false;
162 unsigned bitwidth = mlir::DataLayout::closest(alloc.getDefiningOp())
163 .getTypeSizeInBits(elemType);
164 // Use tryGetNumElements to avoid an assertion on integer overflow (e.g. for
165 // very large statically-shaped memrefs). If the element count overflows
166 // int64_t the allocation is certainly not "small", so return false.
167 std::optional<int64_t> numElements = type.tryGetNumElements();
168 if (!numElements)
169 return false;
170 // Guard against overflow in the size computation as well.
171 if (bitwidth != 0 &&
172 *numElements > static_cast<int64_t>(maximumSizeInBytes * 8ULL / bitwidth))
173 return false;
174 return *numElements * bitwidth <=
175 static_cast<int64_t>(maximumSizeInBytes * 8ULL);
176}
177
178/// Checks whether the given aliases leave the allocation scope.
179static bool
181 const BufferViewFlowAnalysis::ValueSetT &aliases) {
182 for (Value alias : aliases) {
183 for (auto *use : alias.getUsers()) {
184 // If there is at least one alias that leaves the parent region, we know
185 // that this alias escapes the whole region and hence the associated
186 // allocation leaves allocation scope.
187 if (isa<RegionBranchTerminatorOpInterface>(use) &&
188 use->getParentRegion() == parentRegion)
189 return true;
191 }
192 return false;
195/// Checks, if an automated allocation scope for a given alloc value exists.
196static bool hasAllocationScope(Value alloc,
197 const BufferViewFlowAnalysis &aliasAnalysis) {
198 Region *region = alloc.getParentRegion();
199 do {
200 if (Operation *parentOp = region->getParentOp()) {
201 // Check if the operation is an automatic allocation scope and whether an
202 // alias leaves the scope. This means, an allocation yields out of
203 // this scope and can not be transformed in a stack-based allocation.
204 if (parentOp->hasTrait<OpTrait::AutomaticAllocationScope>() &&
205 !leavesAllocationScope(region, aliasAnalysis.resolve(alloc)))
206 return true;
207 // Check if the operation is a known control flow interface and break the
208 // loop to avoid transformation in loops. Furthermore skip transformation
209 // if the operation does not implement a RegionBeanchOpInterface.
210 if (isLoop(parentOp) || !isKnownControlFlowInterface(parentOp))
211 break;
212 }
213 } while ((region = region->getParentRegion()));
214 return false;
215}
216
217namespace {
218
219//===----------------------------------------------------------------------===//
220// BufferAllocationHoisting
221//===----------------------------------------------------------------------===//
222
223/// A base implementation compatible with the `BufferAllocationHoisting` class.
224struct BufferAllocationHoistingStateBase {
225 /// A pointer to the current dominance info.
226 DominanceInfo *dominators;
227
228 /// The current placement block (if any).
229 Block *placementBlock;
230
231 /// Initializes the state base.
232 BufferAllocationHoistingStateBase(DominanceInfo *dominators,
233 Block *placementBlock)
234 : dominators(dominators), placementBlock(placementBlock) {}
235};
237/// Implements the actual hoisting logic for allocation nodes.
238template <typename StateT>
239class BufferAllocationHoisting : public BufferPlacementTransformationBase {
240public:
241 BufferAllocationHoisting(Operation *op)
243 postDominators(op), scopeOp(op) {}
244
245 /// Moves allocations upwards.
246 void hoist() {
247 SmallVector<Value> allocsAndAllocas;
248 for (BufferPlacementAllocs::AllocEntry &entry : allocs)
249 allocsAndAllocas.push_back(std::get<0>(entry));
250 scopeOp->walk([&](memref::AllocaOp op) {
251 allocsAndAllocas.push_back(op.getMemref());
252 });
253
254 for (auto allocValue : allocsAndAllocas) {
255 if (!StateT::shouldHoistOpType(allocValue.getDefiningOp()))
256 continue;
257 Operation *definingOp = allocValue.getDefiningOp();
258 assert(definingOp && "No defining op");
259 // Skip allocations in blocks that are not reachable from the function
260 // entry. Such blocks are dead code and the dominator tree analysis may
261 // not have nodes for them, which would cause crashes below.
262 if (!dominators.isReachableFromEntry(allocValue.getParentBlock()))
263 continue;
264 auto operands = definingOp->getOperands();
265 auto resultAliases = aliases.resolve(allocValue);
266 // Determine the common dominator block of all aliases.
267 Block *dominatorBlock =
268 findCommonDominator(allocValue, resultAliases, dominators);
269 // Init the initial hoisting state.
270 StateT state(&dominators, allocValue.getParentBlock(), resultAliases);
271 // Check for additional allocation dependencies to compute an upper bound
272 // for hoisting.
273 Block *dependencyBlock = nullptr;
274 // If this node has dependencies, check all dependent nodes. This ensures
275 // that all dependency values have been computed before allocating the
276 // buffer.
277 for (Value depValue : operands) {
278 Block *depBlock = depValue.getParentBlock();
279 if (!dependencyBlock || dominators.dominates(dependencyBlock, depBlock))
280 dependencyBlock = depBlock;
281 }
282
283 // Find the actual placement block and determine the start operation using
284 // an upper placement-block boundary. The idea is that placement block
285 // cannot be moved any further upwards than the given upper bound.
286 Block *placementBlock = findPlacementBlock(
287 state, state.computeUpperBound(dominatorBlock, dependencyBlock));
288 Operation *startOperation = BufferPlacementAllocs::getStartOperation(
289 allocValue, placementBlock, liveness);
290
291 // Move the alloc in front of the start operation.
292 Operation *allocOperation = allocValue.getDefiningOp();
293 allocOperation->moveBefore(startOperation);
294 }
295 }
296
297private:
298 /// Finds a valid placement block by walking upwards in the CFG until we
299 /// either cannot continue our walk due to constraints (given by the StateT
300 /// implementation) or we have reached the upper-most dominator block.
301 Block *findPlacementBlock(StateT &state, Block *upperBound) {
302 Block *currentBlock = state.placementBlock;
303 // Walk from the innermost regions/loops to the outermost regions/loops and
304 // find an appropriate placement block that satisfies the constraint of the
305 // current StateT implementation. Walk until we reach the upperBound block
306 // (if any).
307
308 // If we are not able to find a valid parent operation or an associated
309 // parent block, break the walk loop.
310 Operation *parentOp;
311 Block *parentBlock;
312 while ((parentOp = currentBlock->getParentOp()) &&
313 (parentBlock = parentOp->getBlock()) &&
314 (!upperBound ||
315 dominators.properlyDominates(upperBound, currentBlock))) {
316 // A reachable nested region may have an unreachable enclosing block,
317 // which has no node in the dominator tree.
318 if (!dominators.isReachableFromEntry(currentBlock))
319 break;
320
321 // Try to find an immediate dominator and check whether the parent block
322 // is above the immediate dominator (if any).
323 DominanceInfoNode *idom = nullptr;
324
325 // DominanceInfo doesn't support getNode queries for single-block regions.
326 if (!currentBlock->isEntryBlock())
327 idom = dominators.getNode(currentBlock)->getIDom();
328
329 if (idom && dominators.properlyDominates(parentBlock, idom->getBlock())) {
330 // If the current immediate dominator is below the placement block, move
331 // to the immediate dominator block.
332 currentBlock = idom->getBlock();
333 state.recordMoveToDominator(currentBlock);
334 } else {
335 // The target's parent block is outside the liveness analysis. Keep
336 // allocations within the target and preserve isolation boundaries.
337 if (parentOp == scopeOp ||
338 parentOp->hasTrait<OpTrait::IsIsolatedFromAbove>() ||
339 !isKnownControlFlowInterface(parentOp) ||
340 !state.isLegalPlacement(parentOp))
341 break;
342 // Move to our parent block by notifying the current StateT
343 // implementation.
344 currentBlock = parentBlock;
345 state.recordMoveToParent(currentBlock);
346 }
347 }
348 // Return the finally determined placement block.
349 return state.placementBlock;
350 }
351
352 /// The dominator info to find the appropriate start operation to move the
353 /// allocs.
354 DominanceInfo dominators;
355
356 /// The post dominator info to move the dependent allocs in the right
357 /// position.
358 PostDominanceInfo postDominators;
359
360 /// The map storing the final placement blocks of a given alloc value.
361 llvm::DenseMap<Value, Block *> placementBlocks;
362
363 /// The operation that this transformation is working on. It is used to also
364 /// gather allocas.
365 Operation *scopeOp;
366};
367
368/// A state implementation compatible with the `BufferAllocationHoisting` class
369/// that hoists allocations into dominator blocks while keeping them inside of
370/// loops.
371struct BufferAllocationHoistingState : BufferAllocationHoistingStateBase {
372 BufferAllocationHoistingState(DominanceInfo *dominators,
373 Block *placementBlock,
375 : BufferAllocationHoistingStateBase(dominators, placementBlock) {}
376
377 /// Computes the upper bound for the placement block search.
378 Block *computeUpperBound(Block *dominatorBlock, Block *dependencyBlock) {
379 // If we do not have a dependency block, the upper bound is given by the
380 // dominator block.
381 if (!dependencyBlock)
382 return dominatorBlock;
383
384 // Find the "lower" block of the dominator and the dependency block to
385 // ensure that we do not move allocations above this block.
386 return dominators->properlyDominates(dominatorBlock, dependencyBlock)
387 ? dependencyBlock
388 : dominatorBlock;
389 }
390
391 /// Returns true if the given operation does not represent a loop.
392 bool isLegalPlacement(Operation *op) { return !isLoop(op); }
393
394 /// Returns true if the given operation should be considered for hoisting.
395 static bool shouldHoistOpType(Operation *op) {
397 }
398
399 /// Sets the current placement block to the given block.
400 void recordMoveToDominator(Block *block) { placementBlock = block; }
401
402 /// Sets the current placement block to the given block.
403 void recordMoveToParent(Block *block) { recordMoveToDominator(block); }
404};
405
406/// A state implementation compatible with the `BufferAllocationHoisting` class
407/// that hoists allocations out of loops.
408struct BufferAllocationLoopHoistingState : BufferAllocationHoistingStateBase {
409 /// The forward alias closure of the current allocation.
411
412 /// Remembers the dominator block of all aliases.
413 Block *aliasDominatorBlock = nullptr;
414
415 BufferAllocationLoopHoistingState(
416 DominanceInfo *dominators, Block *placementBlock,
418 : BufferAllocationHoistingStateBase(dominators, placementBlock),
419 aliases(aliases) {}
420
421 /// Computes the upper bound for the placement block search.
422 Block *computeUpperBound(Block *dominatorBlock, Block *dependencyBlock) {
423 aliasDominatorBlock = dominatorBlock;
424 // If there is a dependency block, we have to use this block as an upper
425 // bound to satisfy all allocation value dependencies.
426 return dependencyBlock ? dependencyBlock : nullptr;
427 }
428
429 /// Returns true if the allocation can be moved across the given loop.
430 bool isLegalPlacement(Operation *op) {
431 if (!isSequentialLoop(op))
432 return false;
433 if (!dominators->dominates(aliasDominatorBlock, op->getBlock()))
434 return true;
435 auto loop = dyn_cast<scf::WhileOp>(op);
436 return loop && canHoistFromWhile(loop, aliases);
437 }
438
439 /// Returns true if the given operation should be considered for hoisting.
440 static bool shouldHoistOpType(Operation *op) {
441 return allowAllocLoopHoisting(op);
442 }
443
444 /// Does not change the internal placement block, as we want to move
445 /// operations out of loops only.
446 void recordMoveToDominator(Block *block) {}
447
448 /// Sets the current placement block to the given block.
449 void recordMoveToParent(Block *block) { placementBlock = block; }
450};
451
452//===----------------------------------------------------------------------===//
453// BufferPlacementPromotion
454//===----------------------------------------------------------------------===//
455
456/// Promotes heap-based allocations to stack-based allocations (if possible).
457class BufferPlacementPromotion : BufferPlacementTransformationBase {
458public:
459 BufferPlacementPromotion(Operation *op)
460 : BufferPlacementTransformationBase(op) {}
461
462 /// Promote buffers to stack-based allocations.
463 void promote(function_ref<bool(Value)> isSmallAlloc) {
464 for (BufferPlacementAllocs::AllocEntry &entry : allocs) {
465 Value alloc = std::get<0>(entry);
466 Operation *dealloc = std::get<1>(entry);
467 // Checking several requirements to transform an AllocOp into an AllocaOp.
468 // The transformation is done if the allocation is limited to a given
469 // size. Furthermore, a deallocation must not be defined for this
470 // allocation entry and a parent allocation scope must exist.
471 if (!isSmallAlloc(alloc) || dealloc ||
472 !hasAllocationScope(alloc, aliases))
473 continue;
474
475 Operation *startOperation = BufferPlacementAllocs::getStartOperation(
476 alloc, alloc.getParentBlock(), liveness);
477 // Build a new alloca that is associated with its parent
478 // `AutomaticAllocationScope` determined during the initialization phase.
479 OpBuilder builder(startOperation);
480 Operation *allocOp = alloc.getDefiningOp();
481 if (auto allocInterface = dyn_cast<AllocationOpInterface>(allocOp)) {
482 std::optional<Operation *> alloca =
483 allocInterface.buildPromotedAlloc(builder, alloc);
484 if (!alloca)
485 continue;
486 // Replace the original alloc by a newly created alloca.
487 allocOp->replaceAllUsesWith(alloca.value());
488 allocOp->erase();
489 }
490 }
491 }
492};
493
494//===----------------------------------------------------------------------===//
495// BufferOptimizationPasses
496//===----------------------------------------------------------------------===//
497
498/// The buffer hoisting pass that hoists allocation nodes into dominating
499/// blocks.
500struct BufferHoistingPass
501 : public bufferization::impl::BufferHoistingPassBase<BufferHoistingPass> {
502
503 void runOnOperation() override {
504 // Hoist all allocations into dominator blocks.
505 BufferAllocationHoisting<BufferAllocationHoistingState> optimizer(
506 getOperation());
507 optimizer.hoist();
508 }
509};
510
511/// The buffer loop hoisting pass that hoists allocation nodes out of loops.
512struct BufferLoopHoistingPass
514 BufferLoopHoistingPass> {
515
516 void runOnOperation() override {
517 // Hoist all allocations out of loops.
518 hoistBuffersFromLoops(getOperation());
519 }
520};
521
522/// The promote buffer to stack pass that tries to convert alloc nodes into
523/// alloca nodes.
524class PromoteBuffersToStackPass
526 PromoteBuffersToStackPass> {
527 using Base::Base;
528
529public:
530 explicit PromoteBuffersToStackPass(std::function<bool(Value)> isSmallAlloc)
531 : isSmallAlloc(std::move(isSmallAlloc)) {}
532
533 LogicalResult initialize(MLIRContext *context) override {
534 if (isSmallAlloc == nullptr) {
535 isSmallAlloc = [=](Value alloc) {
536 return defaultIsSmallAlloc(alloc, maxAllocSizeInBytes,
537 maxRankOfAllocatedMemRef);
538 };
539 }
540 return success();
541 }
542
543 void runOnOperation() override {
544 // Move all allocation nodes and convert candidates into allocas.
545 BufferPlacementPromotion optimizer(getOperation());
546 optimizer.promote(isSmallAlloc);
547 }
548
549private:
550 std::function<bool(Value)> isSmallAlloc;
551};
552
553} // namespace
554
556 BufferAllocationHoisting<BufferAllocationLoopHoistingState> optimizer(op);
557 optimizer.hoist();
558}
559
561 std::function<bool(Value)> isSmallAlloc) {
562 return std::make_unique<PromoteBuffersToStackPass>(std::move(isSmallAlloc));
563}
return success()
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 canHoistFromWhile(scf::WhileOp loop, const BufferViewFlowAnalysis::ValueSetT &aliases)
Returns true if an allocation can be reused across iterations when its aliases have a common dominato...
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)
auto load
Block represents an ordered list of Operations.
Definition Block.h:34
bool isEntryBlock()
Return if this block is the entry block in the parent region.
Definition Block.cpp:36
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
Definition Block.cpp:31
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.
Definition Dominance.h:143
This class represents an operand of an operation.
Definition Value.h:254
A trait of region holding operations that define a new scope for automatic allocations,...
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
bool hasTrait()
Returns true if the operation was registered with a particular trait, e.g.
Definition Operation.h:801
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:251
operand_range getOperands()
Returns an iterator on the underlying Value's.
Definition Operation.h:403
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'.
Definition Operation.h:297
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.
Definition Region.h:26
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Definition Region.cpp:45
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:198
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
Block * getParentBlock()
Return the Block in which this Value is defined.
Definition Value.cpp:46
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
Region * getParentRegion()
Return the Region in which this Value is defined.
Definition Value.cpp:39
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.
Definition BufferUtils.h:38
The base class for all BufferPlacement transformations.
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...
Definition BufferUtils.h:82
LogicalResult promote(RewriterBase &rewriter, scf::ForallOp forallOp)
Promotes the loop body of a scf::ForallOp to its containing block.
Definition SCF.cpp:752
Include the generated interface declarations.
llvm::DomTreeNodeBase< Block > DominanceInfoNode
Definition Dominance.h:30
llvm::function_ref< Fn > function_ref
Definition LLVM.h:155