MLIR 23.0.0git
Mem2Reg.cpp
Go to the documentation of this file.
1//===- Mem2Reg.cpp - Promotes memory slots into values ----------*- C++ -*-===//
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
13#include "mlir/IR/Builders.h"
14#include "mlir/IR/Dominance.h"
17#include "mlir/IR/Value.h"
21#include "llvm/ADT/STLExtras.h"
22#include "llvm/ADT/SetVector.h"
23#include "llvm/Support/DebugLog.h"
24#include "llvm/Support/GenericIteratedDominanceFrontier.h"
25
26namespace mlir {
27#define GEN_PASS_DEF_MEM2REG
28#include "mlir/Transforms/Passes.h.inc"
29} // namespace mlir
30
31#define DEBUG_TYPE "mem2reg"
32
33using namespace mlir;
34
35/// mem2reg
36///
37/// This pass turns unnecessary uses of automatically allocated memory slots
38/// into direct Value-based operations. For example, it will simplify storing a
39/// constant in a memory slot to immediately load it to a direct use of that
40/// constant. In other words, given a memory slot addressed by a "pointer" Value
41/// (which may be exposed through aliases), mem2reg removes all the uses of
42/// that pointer and its aliases.
43///
44/// Within a block, this is done by following the chain of stores and loads of
45/// the slot (or its aliases) and replacing the results of loads with the values
46/// previously stored. If a load happens before any other store, a poison value
47/// is used instead.
48///
49/// Control flow can create situations where a load could be replaced by
50/// multiple possible stores depending on the control flow path taken. As a
51/// result, this pass must introduce new block arguments in some blocks to
52/// accommodate for the multiple possible definitions. Each predecessor will
53/// populate the block argument with the definition reached at its end. With
54/// this, the value stored can be well defined at block boundaries, allowing
55/// the propagation of replacement through blocks.
56///
57/// The way regions are handled in the transformation is by offering an
58/// interface to express the behavior of the allocation value at the edges of
59/// the regions: from a particular definition reaching the region operation, the
60/// operation will specify what the reaching definition at the entry of its
61/// regions are (potentially mutating itself, for example to add region
62/// arguments). Likewise, provided a reaching definition at the end of the
63/// blocks in the regions, the region operation will provide the reaching
64/// definition right after itself.
65///
66/// This pass computes this transformation in two main phases: an analysis
67/// phase that does not mutate IR, and a transformation phase where mutation
68/// happens. Each phase is handled by the `MemorySlotPromotionAnalyzer` and
69/// `MemorySlotPromoter` classes respectively.
70///
71/// The two steps of the analysis phase are the following:
72/// - A first step computes the list of operations that transitively use the
73/// memory slot we would like to promote. The purpose of this phase is to
74/// identify which uses must be removed to promote the slot, either by rewiring
75/// the user or deleting it. Naturally, direct uses of the slot and its aliases
76/// must be removed. Sometimes additional uses must also be removed: this is
77/// notably the case when a direct user of the slot cannot rewire its use and
78/// must delete itself, and thus must make its users no longer use it. If the
79/// allocation is used in nested regions, it is also ensured the region
80/// operations provide the right interface to analyze the values of the
81/// allocation at the edges of its regions. If any of those constraints cannot
82/// be satisfied, promotion cannot continue: this is decided at this step.
83/// - A second step computes the list of blocks where a block argument will be
84/// needed ("merge points") without mutating the IR. These blocks are the blocks
85/// leading to a definition clash between two predecessors. Such blocks happen
86/// to be the Iterated Dominance Frontier (IDF) of the set of blocks containing
87/// a store, as they represent the points where a clear defining dominator stops
88/// existing. Computing this information in advance allows making sure the
89/// terminators that will forward values are capable of doing so (inability to
90/// do so aborts promotion at this step).
91///
92/// At this point, promotion is guaranteed to happen, and the transformation
93/// phase can begin. The transformation is a three-step process.
94/// - The first step computes the reaching definition of the memory slot at
95/// each blocking user. This is the core of the mem2reg algorithm, also known
96/// as load-store forwarding. This analyses loads and stores and propagates
97/// which value must be stored in the slot at each blocking user. This is
98/// achieved by doing a depth-first walk of the dominator tree of the function.
99/// This is sufficient because the reaching definition at the beginning of a
100/// block is either its new block argument if it is a merge block, or the
101/// definition reaching the end of its immediate dominator (parent in the
102/// dominator tree). We can therefore propagate this information down the
103/// dominator tree to proceed with renaming within blocks. If at any point a
104/// region operation that contains a use of the allocation is encountered, the
105/// reaching definition computation is recursively triggered on the child
106/// regions of the encountered operation, to obtain the reaching definition at
107/// its end and carry on with the value forwarding.
108/// - The second step visits the values that will replace the memory slot for
109/// operations that requested it. This must happen before the removal of
110/// blocking uses, so that operations can safely inspect reaching definitions
111/// before they may be removed or modified.
112/// - The third step uses the reaching definition to remove blocking uses. Some
113/// reaching definitions may be values that will be removed or modified during
114/// the blocking use removal step (typically, in the case of a store that stores
115/// the result of a load). To properly handle such values, this step traverses
116/// the regions in post-order, and the operations to modify in reverse
117/// topological order. This way, if a value that will disappear is used in place
118/// of a reaching definition, the logic to make it disappear will be executed
119/// after the value has been used to replace an operation. For regions within a
120/// PromotableRegionOpInterface, this ensures that the finalization logic (run
121/// during the first step) and visitReplacedValues (run during the second step)
122/// happen before the blocking use removal step, so that any use of a value that
123/// will be removed gets properly replaced.
124///
125/// For further reading, chapter three of SSA-based Compiler Design [1]
126/// showcases SSA construction for control-flow graphs, where mem2reg is an
127/// adaptation of the same process.
128///
129/// [1]: Rastello F. & Bouchez Tichadou F., SSA-based Compiler Design (2022),
130/// Springer.
131
132namespace {
133
134using BlockingUsesMap =
135 llvm::MapVector<Operation *, SmallPtrSet<OpOperand *, 4>>;
136using RegionBlockingUsesMap =
137 llvm::SmallMapVector<Region *, BlockingUsesMap, 2>;
138
139using RegionSet = SmallPtrSet<Region *, 32>;
140
141/// Information about regions that will be traversed for promotion, computed
142/// during promotion analysis.
143struct RegionPromotionInfo {
144 /// True if an operation storing to the slot is present in the region.
145 bool hasValueStores;
146};
147
148/// Information computed during promotion analysis used to perform actual
149/// promotion.
150struct MemorySlotPromotionInfo {
151 /// Blocks for which at least two definitions of the slot values clash.
152 SmallPtrSet<Block *, 8> mergePoints;
153 /// Contains, for each each region, the blocking uses for its operations. The
154 /// blocking uses are the uses that must be eliminated by promotion. For each
155 /// region, this is a DAG structure because if an operation must eliminate
156 /// some of its uses, it is because the defining ops of the blocking uses
157 /// requested it. The defining ops therefore must also have blocking uses or
158 /// be the starting point of the blocking uses.
159 RegionBlockingUsesMap userToBlockingUses;
160 /// Regions of which the edges must be analyzed for promotion. All regions
161 /// are guaranteed to be held by a PromotableRegionOpInterface, and to be
162 /// nested within the parent region of the slot pointer.
164 /// Transitive aliases of `slot.ptr` via `PromotableAliaserInterface`,
165 /// mapping alias values to their exposed slot and aliased operand.
166 PromotableAliasMap aliasMap;
167 /// True if at least one blocking use requires visiting the replaced values.
168 bool needsAnyReplacedValuesVisit = false;
169};
170
171/// Computes information for basic slot promotion. This will check that direct
172/// slot promotion can be performed, and provide the information to execute the
173/// promotion. This does not mutate IR.
174class MemorySlotPromotionAnalyzer {
175public:
176 MemorySlotPromotionAnalyzer(MemorySlot slot, DominanceInfo &dominance,
177 const DataLayout &dataLayout)
178 : slot(slot), dominance(dominance), dataLayout(dataLayout) {}
179
180 /// Computes the information for slot promotion if promotion is possible,
181 /// returns nothing otherwise.
182 std::optional<MemorySlotPromotionInfo> computeInfo();
183
184private:
185 /// Computes the transitive uses of the slot that block promotion. This finds
186 /// uses that would block the promotion, checks that the operation has a
187 /// solution to remove the blocking use, and potentially forwards the analysis
188 /// if the operation needs further blocking uses resolved to resolve its own
189 /// uses (typically, removing its users because it will delete itself to
190 /// resolve its own blocking uses). This will fail if one of the transitive
191 /// users cannot remove a requested use, and should prevent promotion.
192 /// Resulting blocking uses are grouped by region.
193 /// This also ensures all the uses are within promotable regions, adding
194 /// information about regions to be promoted to the `regionsToPromote` map.
195 LogicalResult
196 computeBlockingUses(RegionBlockingUsesMap &userToBlockingUses,
198 PromotableAliasMap &aliasMap,
199 bool &needsAnyReplacedValuesVisit);
200
201 /// Computes the points in the provided region where multiple re-definitions
202 /// of the slot's value (stores) may conflict.
203 /// `definingBlocks` is the set of blocks containing a store to the slot,
204 /// either directly or inherited from a nested region.
205 void computeMergePoints(Region *region,
206 SmallPtrSetImpl<Block *> &definingBlocks,
207 SmallPtrSetImpl<Block *> &mergePoints);
208
209 /// Ensures predecessors of merge points can properly provide their current
210 /// definition of the value stored in the slot to the merge point. This can
211 /// notably be an issue if the terminator used does not have the ability to
212 /// forward values through block operands.
213 bool areMergePointsUsable(SmallPtrSetImpl<Block *> &mergePoints);
214
215 MemorySlot slot;
216
217 DominanceInfo &dominance;
218 const DataLayout &dataLayout;
219};
220
221/// Maps a region to a map of blocks to their index in the region.
222/// The region is identified by its entry block pointer instead of its region
223/// pointer to not need to invalidate the cache when region content is moved to
224/// a new region. This only supports moves of all the blocks of a region to
225/// an empty region.
226using BlockIndexCache = DenseMap<Block *, DenseMap<Block *, size_t>>;
227
228/// The MemorySlotPromoter handles the state of promoting a memory slot. It
229/// wraps a slot and its associated allocator. This will perform the mutation of
230/// IR.
231class MemorySlotPromoter {
232public:
233 MemorySlotPromoter(MemorySlot slot, PromotableAllocationOpInterface allocator,
234 OpBuilder &builder, DominanceInfo &dominance,
235 const DataLayout &dataLayout, MemorySlotPromotionInfo info,
236 const Mem2RegStatistics &statistics,
237 BlockIndexCache &blockIndexCache);
238
239 /// Actually promotes the slot by mutating IR. Promoting a slot DOES
240 /// invalidate the MemorySlotPromotionInfo of other slots. Preparation of
241 /// promotion info should NOT be performed in batches.
242 /// Returns a promotable allocation op if a new allocator was created, nullopt
243 /// otherwise.
244 std::optional<PromotableAllocationOpInterface> promoteSlot();
245
246private:
247 /// Computes the reaching definition for all the operations that require
248 /// promotion, including within nested regions needing promotion.
249 /// `reachingDef` is the value the slot contains at the beginning of the
250 /// block. This member function returns the reached definition at the end of
251 /// the block. Blocking uses are not removed and `visitReplacedValues` is
252 /// not called here; both are deferred to `promoteSlot`.
253 ///
254 /// The `reachingDef` may be a null value. In that case, a lazily-created
255 /// default value will be used.
256 ///
257 /// This member function must only be called at most once per block.
258 Value promoteInBlock(Block *block, Value reachingDef);
259
260 /// Computes the reaching definition for all the operations that require
261 /// promotion, including within nested regions needing promotion. Records
262 /// `region` in `regionsInPostOrder` after the DFS completes.
263 /// `reachingDef` is the value the slot contains at the beginning of the
264 /// region. Blocking uses are not removed here; that is deferred to
265 /// `promoteSlot`.
266 ///
267 /// The `reachingDef` may be a null value. In that case, a lazily-created
268 /// default value will be used.
269 ///
270 /// This member function must only be called at most once per region.
271 void promoteInRegion(Region *region, Value reachingDef);
272
273 /// Calls `visitReplacedValues` on ops in `region` that requested it.
274 /// Must run before `removeBlockingUses` so that replacedValues are still
275 /// valid (`removeBlockingUses` can later replace them).
276 void visitReplacedValuesForRegion(
277 Region *region, ArrayRef<std::pair<Operation *, Value>> replacedValues);
278
279 /// Removes the blocking uses of the slot within the given region, in
280 /// reverse topological order. If the content of the region was moved out
281 /// to a different region, the new region will be processed instead.
282 void removeBlockingUses(Region *region);
283
284 /// Removes operations and merge point block arguments that ended up not being
285 /// necessary.
286 void removeUnusedItems();
287
288 /// Lazily-constructed default value representing the content of the slot when
289 /// no store has been executed. This function may mutate IR.
290 Value getOrCreateDefaultValue();
291
292 MemorySlot slot;
293 PromotableAllocationOpInterface allocator;
294 OpBuilder &builder;
295 /// Potentially non-initialized default value. Use `getOrCreateDefaultValue`
296 /// to initialize it on demand.
297 Value defaultValue;
298 /// Contains the reaching definition at this operation. Reaching definitions
299 /// are only computed for promotable memory operations with blocking uses.
302
303 /// Contains the reaching definition at the end of the blocks visited so far.
304 DenseMap<Block *, Value> reachingAtBlockEnd;
305
306 /// Regions visited by `promoteInRegion`, recorded in DFS post-order.
307 /// Inner regions appear before their enclosing regions.
308 llvm::SmallVector<Region *> regionsInPostOrder;
309
310 /// Operations to be erased at the end of the promotion.
311 llvm::SmallSetVector<Operation *, 8> toErase;
312
313 DominanceInfo &dominance;
314 const DataLayout &dataLayout;
315 MemorySlotPromotionInfo info;
316 const Mem2RegStatistics &statistics;
317
318 /// Shared cache of block indices of specific regions.
319 /// Cache entries must be invalidated before any addition, removal or
320 /// reordering of blocks in the corresponding region.
321 /// Cache entries are *NOT* invalidated if all the blocks of the corresponding
322 /// region are moved to an empty region.
323 BlockIndexCache &blockIndexCache;
324};
325
326} // namespace
327
328MemorySlotPromoter::MemorySlotPromoter(
329 MemorySlot slot, PromotableAllocationOpInterface allocator,
330 OpBuilder &builder, DominanceInfo &dominance, const DataLayout &dataLayout,
331 MemorySlotPromotionInfo info, const Mem2RegStatistics &statistics,
332 BlockIndexCache &blockIndexCache)
333 : slot(slot), allocator(allocator), builder(builder), dominance(dominance),
334 dataLayout(dataLayout), info(std::move(info)), statistics(statistics),
335 blockIndexCache(blockIndexCache) {
336#ifndef NDEBUG
337 auto isResultOrNewBlockArgument = [&]() {
338 if (BlockArgument arg = dyn_cast<BlockArgument>(slot.ptr))
339 return arg.getOwner()->getParentOp() == allocator;
340 return slot.ptr.getDefiningOp() == allocator;
341 };
342
343 assert(isResultOrNewBlockArgument() &&
344 "a slot must be a result of the allocator or an argument of the child "
345 "regions of the allocator");
346#endif // NDEBUG
347}
348
349Value MemorySlotPromoter::getOrCreateDefaultValue() {
350 if (defaultValue)
351 return defaultValue;
352
353 OpBuilder::InsertionGuard guard(builder);
355 return defaultValue = allocator.getDefaultValue(slot, builder);
356}
357
358LogicalResult MemorySlotPromotionAnalyzer::computeBlockingUses(
359 RegionBlockingUsesMap &userToBlockingUses,
361 PromotableAliasMap &aliasMap, bool &needsAnyReplacedValuesVisit) {
362 // The promotion of an operation may require the promotion of further
363 // operations (typically, removing operations that use an operation that must
364 // delete itself). We thus need to start from the use of the slot pointer and
365 // propagate further requests through the forward slice.
366
367 // Graph regions are not supported.
368 Region *slotPtrRegion = slot.ptr.getParentRegion();
369 auto slotPtrRegionOp =
370 dyn_cast<RegionKindInterface>(slotPtrRegion->getParentOp());
371 if (slotPtrRegionOp &&
372 slotPtrRegionOp.getRegionKind(slotPtrRegion->getRegionNumber()) ==
373 RegionKind::Graph)
374 return failure();
375
376 // First insert that all immediate users of the slot pointer must no longer
377 // use it.
378 for (OpOperand &use : slot.ptr.getUses()) {
379 SmallPtrSet<OpOperand *, 4> &blockingUses =
380 userToBlockingUses[use.getOwner()->getParentRegion()][use.getOwner()];
381 blockingUses.insert(&use);
382 }
383
384 // Regions that immediately contain a slot memory use that is not a store.
385 RegionSet regionsWithDirectUse;
386 // Regions that immediately contain a slot memory use that is a store.
387 RegionSet regionsWithDirectStore;
388
389 // Then, propagate the requirements for the removal of uses. The
390 // topologically-sorted forward slice allows for all blocking uses of an
391 // operation to have been computed before it is reached. Operations are
392 // traversed in topological order of their uses, starting from the slot
393 // pointer.
394 SetVector<Operation *> forwardSlice;
395 mlir::getForwardSlice(slot.ptr, &forwardSlice);
396 for (Operation *user : forwardSlice) {
397 // If the next operation has no blocking uses, everything is fine.
398 auto *blockingUsesMapIt = userToBlockingUses.find(user->getParentRegion());
399 if (blockingUsesMapIt == userToBlockingUses.end())
400 continue;
401 BlockingUsesMap &blockingUsesMap = blockingUsesMapIt->second;
402 auto *it = blockingUsesMap.find(user);
403 if (it == blockingUsesMap.end())
404 continue;
405
406 // Populate the alias map for alias-exposing ops.
407 if (auto aliaser = dyn_cast<PromotableAliaserInterface>(user))
408 populatePromotableAliasMap(aliaser, slot, aliasMap);
409
410 SmallPtrSet<OpOperand *, 4> &blockingUses = it->second;
411
412 SmallVector<OpOperand *> newBlockingUses;
413 // If the operation decides it cannot deal with removing the blocking uses,
414 // promotion must fail.
415 if (auto promotable = dyn_cast<PromotableOpInterface>(user)) {
416 if (!promotable.canUsesBeRemoved(blockingUses, newBlockingUses,
417 dataLayout))
418 return failure();
419 regionsWithDirectUse.insert(user->getParentRegion());
420 if (promotable.requiresReplacedValues())
421 needsAnyReplacedValuesVisit = true;
422 } else if (auto promotable = dyn_cast<PromotableMemOpInterface>(user)) {
423 // If the memop reaches the root slot through multiple distinct alias
424 // operands, promotion fails. `PromotableMemOpInterface` expects a
425 // single slot per call. Supporting multiple aliases would require
426 // extending the interface.
427 if (!referencesAtMostOneAliasOfSlot(user, slot, aliasMap))
428 return failure();
429 MemorySlot aliasSlot =
430 getOpAliasSlot(user, slot, aliasMap).value_or(slot);
431 if (!promotable.canUsesBeRemoved(aliasSlot, blockingUses, newBlockingUses,
432 dataLayout))
433 return failure();
434
435 // Operations that interact with the slot's memory will be promoted using
436 // a reaching definition. Therefore, the operation must be within a region
437 // where the reaching definition can be computed.
438 if (promotable.storesTo(aliasSlot))
439 regionsWithDirectStore.insert(user->getParentRegion());
440 else
441 regionsWithDirectUse.insert(user->getParentRegion());
442 } else {
443 // An operation that has blocking uses must be promoted. If it is not
444 // promotable, promotion must fail.
445 return failure();
446 }
447
448 // Then, register any new blocking uses for coming operations.
449 for (OpOperand *blockingUse : newBlockingUses) {
450 assert(llvm::is_contained(user->getResults(), blockingUse->get()));
451
452 Operation *useOwner = blockingUse->getOwner();
453 SmallPtrSetImpl<OpOperand *> &newUserBlockingUseSet =
454 userToBlockingUses[useOwner->getParentRegion()][useOwner];
455 newUserBlockingUseSet.insert(blockingUse);
456 }
457 }
458
459 // Finally, check that all the regions needed are promotable, and propagate
460 // the constraint to their parent regions.
461 auto visitRegions = [&](SmallVector<Region *> &regionsToPropagateFrom,
462 bool hasValueStores) {
463 while (!regionsToPropagateFrom.empty()) {
464 Region *region = regionsToPropagateFrom.pop_back_val();
465
466 if (region == slot.ptr.getParentRegion() ||
467 regionsToPromote.contains(region))
468 continue;
469
470 RegionPromotionInfo &regionInfo = regionsToPromote[region];
471 regionInfo.hasValueStores = hasValueStores;
472
473 auto promotableParentOp =
474 dyn_cast<PromotableRegionOpInterface>(region->getParentOp());
475 if (!promotableParentOp)
476 return failure();
477
478 if (!promotableParentOp.isRegionPromotable(slot, region, hasValueStores))
479 return failure();
480
481 regionsToPropagateFrom.push_back(region->getParentRegion());
482 }
483
484 return success();
485 };
486
487 // Start with the regions that directly contain a store to give priority
488 // to stores in the propagation of `hasValueStores` information.
489 SmallVector<Region *> regionsToPropagateFrom(regionsWithDirectStore.begin(),
490 regionsWithDirectStore.end());
491 if (failed(visitRegions(regionsToPropagateFrom, true)))
492 return failure();
493
494 // Then, propagate from the regions that directly contain non-store uses.
495 regionsToPropagateFrom.clear();
496 regionsToPropagateFrom.append(regionsWithDirectUse.begin(),
497 regionsWithDirectUse.end());
498 if (failed(visitRegions(regionsToPropagateFrom, false)))
499 return failure();
500
501 return success();
502}
503
504using IDFCalculator = llvm::IDFCalculatorBase<Block, false>;
505void MemorySlotPromotionAnalyzer::computeMergePoints(
506 Region *region, SmallPtrSetImpl<Block *> &definingBlocks,
507 SmallPtrSetImpl<Block *> &mergePoints) {
508 if (region->hasOneBlock())
509 return;
510
511 IDFCalculator idfCalculator(dominance.getDomTree(region));
512 idfCalculator.setDefiningBlocks(definingBlocks);
513
514 SmallVector<Block *> mergePointsVec;
515 idfCalculator.calculate(mergePointsVec);
516
517 mergePoints.insert_range(mergePointsVec);
518}
519
520bool MemorySlotPromotionAnalyzer::areMergePointsUsable(
521 SmallPtrSetImpl<Block *> &mergePoints) {
522 for (Block *mergePoint : mergePoints)
523 for (Block *pred : mergePoint->getPredecessors())
524 if (!isa<BranchOpInterface>(pred->getTerminator()))
525 return false;
526
527 return true;
528}
529
530std::optional<MemorySlotPromotionInfo>
531MemorySlotPromotionAnalyzer::computeInfo() {
532 MemorySlotPromotionInfo info;
533
534 // First, find the set of operations that will need to be changed for the
535 // promotion to happen. These operations need to resolve some of their uses,
536 // either by rewiring them or simply deleting themselves. If any of them
537 // cannot find a way to resolve their blocking uses, we abort the promotion.
538 // We also compute at this stage the regions that will be analyzed for
539 // reaching definition information.
540 if (failed(computeBlockingUses(info.userToBlockingUses, info.regionsToPromote,
541 info.aliasMap,
542 info.needsAnyReplacedValuesVisit)))
543 return {};
544
545 // Compute the blocks containing a store for each region, either directly or
546 // inherited from a nested region. As a side effect, `definingBlocks` contains
547 // all regions with at least one store.
548 //
549 // Iterate over direct users of the slot pointer and all alias pointers in
550 // `info.aliasMap`. This assumes `PromotableMemOpInterface` operations storing
551 // to the slot use the slot pointer or its aliases directly. Dialects must
552 // implement `PromotableAliaserInterface` for views/aliasing.
554 auto collectStoringBlocks = [&](Value ptr, const MemorySlot &ptrSlot) {
555 for (OpOperand &use : ptr.getUses()) {
556 Operation *user = use.getOwner();
557 if (auto storeOp = dyn_cast<PromotableMemOpInterface>(user))
558 if (storeOp.storesTo(ptrSlot))
559 definingBlocks[user->getParentRegion()].insert(user->getBlock());
560 }
561 };
562 collectStoringBlocks(slot.ptr, slot);
563 for (auto &[aliasPtr, aliasInfo] : info.aliasMap)
564 collectStoringBlocks(aliasPtr, aliasInfo.slot);
565 for (auto &[region, regionInfo] : info.regionsToPromote)
566 if (regionInfo.hasValueStores)
567 definingBlocks[region->getParentRegion()].insert(
568 region->getParentOp()->getBlock());
569
570 // Then, compute blocks in which two or more definitions of the allocated
571 // variable may conflict. These blocks will need a new block argument to
572 // accommodate this.
573 for (auto &[region, defBlocks] : definingBlocks)
574 computeMergePoints(region, defBlocks, info.mergePoints);
575
576 // The slot can be promoted if the block arguments to be created can
577 // actually be populated with values, which may not be possible depending
578 // on their predecessors.
579 if (!areMergePointsUsable(info.mergePoints))
580 return {};
581
582 return info;
583}
584
585Value MemorySlotPromoter::promoteInBlock(Block *block, Value reachingDef) {
586 SmallVector<Operation *> blockOps;
587 for (Operation &op : block->getOperations())
588 blockOps.push_back(&op);
589 for (Operation *op : blockOps) {
590 // Promote operations that interact with the slot's memory.
591 if (auto memOp = dyn_cast<PromotableMemOpInterface>(op)) {
592 if (info.userToBlockingUses[memOp->getParentRegion()].contains(memOp))
593 reachingDefs.insert({memOp, reachingDef});
594
595 MemorySlot aliasSlot =
596 getOpAliasSlot(memOp, slot, info.aliasMap).value_or(slot);
597 if (memOp.storesTo(aliasSlot)) {
598 // Ensures helper IR introduced by `getStored` is created before the
599 // storing op. This ensures the provided stored value dominates the
600 // store, allowing `visitReplacedValues` to safely create new uses
601 // after the store.
602 builder.setInsertionPoint(memOp);
603 // To not expose default value creation to the interfaces, if we have
604 // no reaching definition by now, we set it to the default value.
605 // This is slightly too eager as `getStored` may not need it.
606 if (!reachingDef)
607 reachingDef = getOrCreateDefaultValue();
608 Value reachingDefAtStore = reachingDef;
609 if (slot.ptr != aliasSlot.ptr) {
610 // The store sees the slot at `aliasSlot.elemType`; project the
611 // reaching definition (at root elem type) before handing it to
612 // `getStored`.
613 reachingDefAtStore = convertSlotValueToAliasValue(
614 reachingDef, aliasSlot, slot, info.aliasMap, builder);
615 assert(reachingDefAtStore &&
616 "projectSlotValueToAliasValue contract violation");
617 }
618 Value stored =
619 memOp.getStored(aliasSlot, builder, reachingDefAtStore, dataLayout);
620 assert(stored && "a memory operation storing to a slot must provide a "
621 "new definition of the slot");
622 // `replacedValuesMap` keeps `stored` at `aliasSlot.elemType` for
623 // `visitReplacedValues`; the new reaching definition is tracked at
624 // the root slot's elem type, so project `stored` back.
625 replacedValuesMap[memOp] = stored;
626 if (aliasSlot.ptr != slot.ptr) {
627 stored = convertAliasValueToSlotValue(stored, aliasSlot, reachingDef,
628 slot, info.aliasMap, builder);
629 assert(stored && "projectAliasValueToSlotValue contract violation");
630 }
631 reachingDef = stored;
632 }
633 }
634
635 // Promote regions that contain operations that interact with the slot's
636 // memory.
637 if (auto promotableRegionOp = dyn_cast<PromotableRegionOpInterface>(op)) {
638 bool needsPromotion = false;
639 bool hasValueStores = false;
640 for (Region &region : op->getRegions()) {
641 auto regionInfoIt = info.regionsToPromote.find(&region);
642 if (regionInfoIt == info.regionsToPromote.end())
643 continue;
644 needsPromotion = true;
645 if (!regionInfoIt->second.hasValueStores)
646 continue;
647
648 hasValueStores = true;
649 break;
650 }
651
652 if (needsPromotion) {
653 llvm::SmallMapVector<Region *, Value, 2> regionsToProcess;
654
655 // To not expose default value creation to the interfaces, if we have
656 // no reaching definition by now, we set it to the default value.
657 // This is slightly too eager as `setupPromotion` may not need it.
658 if (!reachingDef)
659 reachingDef = getOrCreateDefaultValue();
660
661 promotableRegionOp.setupPromotion(slot, reachingDef, hasValueStores,
662 regionsToProcess);
663
664#ifndef NDEBUG
665 for (Region &region : op->getRegions())
666 if (info.regionsToPromote.contains(&region))
667 assert(
668 regionsToProcess.contains(&region) &&
669 "reaching definition must be provided for a required region");
670#endif // NDEBUG
671
672 for (auto &[region, reachingDef] : regionsToProcess) {
673 assert(region->getParentOp() == op &&
674 "region must be part of the operation");
675 if (!info.regionsToPromote.contains(region))
676 continue;
677 promoteInRegion(region, reachingDef);
678 }
679
680 // TODO: Currently we have to invalidate the dominance information of
681 // the regions of the operation because finalizePromotion may move their
682 // content. We might want to support moving dominance information
683 // accross regions as this can be detected.
684 for (Region &region : op->getRegions())
685 dominance.invalidate(&region);
686
687 builder.setInsertionPointAfter(op);
688 reachingDef = promotableRegionOp.finalizePromotion(
689 slot, reachingDef, hasValueStores, reachingAtBlockEnd, builder);
690 }
691 }
692 }
693
694 reachingAtBlockEnd[block] = reachingDef;
695 return reachingDef;
696}
697
698void MemorySlotPromoter::promoteInRegion(Region *region, Value reachingDef) {
699 if (region->hasOneBlock()) {
700 promoteInBlock(&region->front(), reachingDef);
701 regionsInPostOrder.push_back(region);
702 return;
703 }
704
705 struct DfsJob {
706 llvm::DomTreeNodeBase<Block> *block;
707 Value reachingDef;
708 };
709
710 SmallVector<DfsJob> dfsStack;
711
712 auto &domTree = dominance.getDomTree(region);
713
714 dfsStack.emplace_back<DfsJob>(
715 {domTree.getNode(&region->front()), reachingDef});
716
717 while (!dfsStack.empty()) {
718 DfsJob job = dfsStack.pop_back_val();
719 Block *block = job.block->getBlock();
720
721 if (info.mergePoints.contains(block)) {
722 BlockArgument blockArgument =
723 block->addArgument(slot.elemType, slot.ptr.getLoc());
724 job.reachingDef = blockArgument;
725 }
726
727 job.reachingDef = promoteInBlock(block, job.reachingDef);
728
729 if (auto terminator = dyn_cast<BranchOpInterface>(block->getTerminator())) {
730 for (BlockOperand &blockOperand : terminator->getBlockOperands()) {
731 if (info.mergePoints.contains(blockOperand.get())) {
732 if (!job.reachingDef)
733 job.reachingDef = getOrCreateDefaultValue();
734
735 terminator.getSuccessorOperands(blockOperand.getOperandNumber())
736 .append(job.reachingDef);
737 }
738 }
739 }
740
741 for (auto *child : job.block->children())
742 dfsStack.emplace_back<DfsJob>({child, job.reachingDef});
743 }
744
745 // Record this region after its (and any nested) promotion has completed,
746 // so that `regionsInPostOrder` lists inner regions before their enclosing
747 // regions.
748 regionsInPostOrder.push_back(region);
749}
750
751/// Gets or creates a block index mapping for the region of which the entry
752/// block is `regionEntryBlock`.
753static const DenseMap<Block *, size_t> &
754getOrCreateBlockIndices(BlockIndexCache &blockIndexCache,
755 Block *regionEntryBlock) {
756 auto [it, inserted] = blockIndexCache.try_emplace(regionEntryBlock);
757 if (!inserted)
758 return it->second;
759
760 DenseMap<Block *, size_t> &blockIndices = it->second;
761 SetVector<Block *> topologicalOrder =
762 getBlocksSortedByDominance(*regionEntryBlock->getParent());
763 for (auto [index, block] : llvm::enumerate(topologicalOrder))
764 blockIndices[block] = index;
765 return blockIndices;
766}
767
768/// Sorts `ops` according to dominance. Relies on the topological order of basic
769/// blocks to get a deterministic ordering. Uses `blockIndexCache` to avoid the
770/// potentially expensive recomputation of a block index map.
771/// This function assumes no blocks are ever deleted or entry block changed
772/// during the lifetime of the block index cache.
774 BlockIndexCache &blockIndexCache) {
775 if (region.empty())
776 return;
777
778 // Produce a topological block order and construct a map to lookup the indices
779 // of blocks.
780 const DenseMap<Block *, size_t> &topoBlockIndices =
781 getOrCreateBlockIndices(blockIndexCache, &region.front());
782
783 // Combining the topological order of the basic blocks together with block
784 // internal operation order guarantees a deterministic, dominance respecting
785 // order.
786 llvm::sort(ops, [&](Operation *lhs, Operation *rhs) {
787 size_t lhsBlockIndex = topoBlockIndices.at(lhs->getBlock());
788 size_t rhsBlockIndex = topoBlockIndices.at(rhs->getBlock());
789 if (lhsBlockIndex == rhsBlockIndex)
790 return lhs->isBeforeInBlock(rhs);
791 return lhsBlockIndex < rhsBlockIndex;
792 });
793}
794
795void MemorySlotPromoter::removeBlockingUses(Region *region) {
796 auto *blockingUsesMapIt = info.userToBlockingUses.find(region);
797 if (blockingUsesMapIt == info.userToBlockingUses.end())
798 return;
799 BlockingUsesMap &blockingUsesMap = blockingUsesMapIt->second;
800 if (blockingUsesMap.empty())
801 return;
802
803 // Operations may have been moved to a different region at this point.
804 // To cover this, we process the current region of an operation to remove
805 // instead of the provided region.
806 region = blockingUsesMap.front().first->getParentRegion();
807#ifndef NDEBUG
808 for (auto &[op, blockingUses] : blockingUsesMap)
809 assert(op->getParentRegion() == region &&
810 "all operations must still be in the same region");
811#endif // NDEBUG
812
813 llvm::SmallVector<Operation *> usersToRemoveUses(
814 llvm::make_first_range(blockingUsesMap));
815
816 // Sort according to dominance.
817 dominanceSort(usersToRemoveUses, *region, blockIndexCache);
818
819 // Iterate over the operations to rewrite in reverse dominance order.
820 for (Operation *toPromote : llvm::reverse(usersToRemoveUses)) {
821 if (auto toPromoteMemOp = dyn_cast<PromotableMemOpInterface>(toPromote)) {
822 Value reachingDef = reachingDefs.lookup(toPromoteMemOp);
823 // If no reaching definition is known, this use is outside the reach of
824 // the slot. The default value should thus be used.
825 // FIXME: This is too eager, and will generate default values even for
826 // pure stores. This cannot be removed easily as partial stores may
827 // still require a default value to complete.
828 if (!reachingDef)
829 reachingDef = getOrCreateDefaultValue();
830
831 builder.setInsertionPointAfter(toPromote);
832 MemorySlot aliasSlot =
833 getOpAliasSlot(toPromote, slot, info.aliasMap).value_or(slot);
834 Value reachingDefAtBlockingUse = reachingDef;
835 if (aliasSlot.ptr != slot.ptr) {
836 // Project the reaching definition to `aliasSlot.elemType` to match
837 // what `toPromoteMemOp` sees.
838 reachingDefAtBlockingUse = convertSlotValueToAliasValue(
839 reachingDef, aliasSlot, slot, info.aliasMap, builder);
840 assert(reachingDefAtBlockingUse &&
841 "projectSlotValueToAliasValue contract violation");
842 }
843 if (toPromoteMemOp.removeBlockingUses(
844 aliasSlot, blockingUsesMap[toPromote], builder,
845 reachingDefAtBlockingUse, dataLayout) == DeletionKind::Delete)
846 toErase.insert(toPromote);
847 continue;
848 }
849
850 auto toPromoteBasic = cast<PromotableOpInterface>(toPromote);
851 builder.setInsertionPointAfter(toPromote);
852 if (toPromoteBasic.removeBlockingUses(blockingUsesMap[toPromote],
853 builder) == DeletionKind::Delete)
854 toErase.insert(toPromote);
855 }
856}
857
858void MemorySlotPromoter::visitReplacedValuesForRegion(
859 Region *region, ArrayRef<std::pair<Operation *, Value>> replacedValues) {
860 auto *blockingUsesMapIt = info.userToBlockingUses.find(region);
861 if (blockingUsesMapIt == info.userToBlockingUses.end())
862 return;
863 BlockingUsesMap &blockingUsesMap = blockingUsesMapIt->second;
864 if (blockingUsesMap.empty())
865 return;
866
867 for (auto &[op, _] : blockingUsesMap) {
868 auto toVisit = dyn_cast<PromotableOpInterface>(op);
869 if (!toVisit || !toVisit.requiresReplacedValues())
870 continue;
871 builder.setInsertionPointAfter(op);
872 toVisit.visitReplacedValues(replacedValues, builder);
873 }
874}
875
876void MemorySlotPromoter::removeUnusedItems() {
877 // We want to eliminate unused block arguments. Because block arguments can be
878 // used to populate other block arguments, there might be cycles of arguments
879 // that are only used to populate each-other. We therefore need a small
880 // dataflow analysis to identify which block arguments are truly used.
881
882 SmallPtrSet<BlockArgument, 8> mergePointArgsUnused;
883 SmallVector<BlockArgument> usedMergePointArgsToProcess;
884
885 // First, separate the block arguments that are not used or only used for the
886 // purpose of populating a merge point block argument from the others. These
887 // block arguments are potentially unused. Meanwhile, arguments that are
888 // definitely used will be the starting point of the propagation of the
889 // analysis.
890 auto isDefinitelyUsed = [&](BlockArgument arg) {
891 for (auto &use : arg.getUses()) {
892 if (llvm::is_contained(toErase, use.getOwner()))
893 continue;
894
895 // We now want to detect whether the use is to populate a merge point
896 // block argument. If it is not, the argument is definitely used.
897
898 auto branchOp = dyn_cast<BranchOpInterface>(use.getOwner());
899 if (!branchOp)
900 return true;
901
902 std::optional<BlockArgument> successorArgument =
903 branchOp.getSuccessorBlockArgument(use.getOperandNumber());
904 if (!successorArgument)
905 return true;
906
907 if (!info.mergePoints.contains(successorArgument->getOwner()))
908 return true;
909
910 // The last block argument of a merge point is its reaching definition
911 // argument. If the argument being populated is not the last one, it is a
912 // genuine use of the value.
913 bool isLastBlockArgument =
914 successorArgument->getArgNumber() ==
915 successorArgument->getOwner()->getNumArguments() - 1;
916 if (!isLastBlockArgument)
917 return true;
918 }
919 return false;
920 };
921
922 for (Block *mergePoint : info.mergePoints) {
923 BlockArgument arg = mergePoint->getArguments().back();
924 if (isDefinitelyUsed(arg))
925 usedMergePointArgsToProcess.push_back(arg);
926 else
927 mergePointArgsUnused.insert(arg);
928 }
929
930 // We now refine mergePointArgsUnused from the information of which block
931 // arguments are definitely used.
932 while (!usedMergePointArgsToProcess.empty()) {
933 BlockArgument arg = usedMergePointArgsToProcess.pop_back_val();
934 Block *mergePoint = arg.getOwner();
935
936 assert(arg.getArgNumber() == mergePoint->getNumArguments() - 1 &&
937 "merge point argument must be the last argument of the merge point");
938
939 for (BlockOperand &use : mergePoint->getUses()) {
940 // If a value used to populate this used merge point argument is another
941 // merge point block argument that is currently considered unused, it must
942 // now be considered used and processed as such later.
943
944 auto branch = cast<BranchOpInterface>(use.getOwner());
945 SuccessorOperands succOperands =
946 branch.getSuccessorOperands(use.getOperandNumber());
947
948 // The successor operand is either the last one or is not present if the
949 // user block is dead.
950 assert(succOperands.size() == mergePoint->getNumArguments() ||
951 succOperands.size() + 1 == mergePoint->getNumArguments());
952
953 // If the user block is dead, the default value acts as a placeholder
954 // dummy value.
955 if (succOperands.size() + 1 == mergePoint->getNumArguments())
956 succOperands.append(getOrCreateDefaultValue());
957
958 Value populatedValue = succOperands[arg.getArgNumber()];
959 auto populatedValueAsArg = dyn_cast<BlockArgument>(populatedValue);
960 if (populatedValueAsArg &&
961 mergePointArgsUnused.erase(populatedValueAsArg))
962 usedMergePointArgsToProcess.push_back(populatedValueAsArg);
963 }
964
965 builder.setInsertionPointToStart(mergePoint);
966 allocator.handleBlockArgument(slot, arg, builder);
967 if (statistics.newBlockArgumentAmount)
968 (*statistics.newBlockArgumentAmount)++;
969 }
971 for (Operation *toEraseOp : toErase)
972 toEraseOp->erase();
974 // First, erase all successor operands that feed into unused merge point
975 // block arguments. This must be done before erasing the block arguments
976 // themselves because an unused merge point argument may be used to
977 // populate another unused merge point argument via a branch operation.
978 for (BlockArgument arg : mergePointArgsUnused) {
979 Block *mergePoint = arg.getOwner();
980 for (BlockOperand &use : mergePoint->getUses()) {
981 auto branch = cast<BranchOpInterface>(use.getOwner());
982 SuccessorOperands succOperands =
983 branch.getSuccessorOperands(use.getOperandNumber());
984 succOperands.erase(arg.getArgNumber());
986 }
987
988 // Now that all successor operands feeding unused args have been removed,
989 // erase the block arguments themselves.
990 for (BlockArgument arg : mergePointArgsUnused) {
991 Block *mergePoint = arg.getOwner();
992 mergePoint->eraseArgument(mergePoint->getNumArguments() - 1);
993 }
995
996std::optional<PromotableAllocationOpInterface>
997MemorySlotPromoter::promoteSlot() {
998 // Step 1: perform the promotion recursively through nested regions. The
999 // reaching definition starts with a null value that will be replaced by a
1000 // lazily-created default value if the value must be passed to a promotion
1001 // interface while no store has been encountered yet.
1002 // Blocking uses are not removed yet.
1003 promoteInRegion(slot.ptr.getParentRegion(), nullptr);
1005 // Step 2: call `visitReplacedValues` on operations that requested it.
1006 if (info.needsAnyReplacedValuesVisit) {
1008 replacedValues.reserve(replacedValuesMap.size());
1009 for (const auto &[memOp, value] : replacedValuesMap)
1010 if (value)
1011 replacedValues.push_back({memOp, value});
1012 for (Region *region : regionsInPostOrder)
1013 visitReplacedValuesForRegion(region, replacedValues);
1014 }
1015
1016 // Step 3: remove the slot's blocking uses across all regions. Iterating
1017 // in through the regions in same order as promoteInRegion.
1018 for (Region *region : regionsInPostOrder)
1019 removeBlockingUses(region);
1020
1021 // Finally, remove unused operations and merge point block arguments.
1022 removeUnusedItems();
1023
1024 assert(slot.ptr.use_empty() &&
1025 "after promotion, the slot pointer should not be used anymore");
1027 LDBG() << "Promoted memory slot: " << slot.ptr;
1028
1029 if (statistics.promotedAmount)
1030 (*statistics.promotedAmount)++;
1031
1032 return allocator.handlePromotionComplete(slot, defaultValue, builder);
1033}
1034
1037 const DataLayout &dataLayout, DominanceInfo &dominance,
1038 Mem2RegStatistics statistics) {
1039 bool promotedAny = false;
1040
1041 // A cache that stores deterministic block indices which are used to determine
1042 // a valid operation modification order. The block index maps are computed
1043 // lazily and cached to avoid expensive recomputation.
1044 BlockIndexCache blockIndexCache;
1045
1047
1049 newWorkList.reserve(workList.size());
1050 while (true) {
1051 bool changesInThisRound = false;
1052 for (PromotableAllocationOpInterface allocator : workList) {
1053 bool changedAllocator = false;
1054 for (MemorySlot slot : allocator.getPromotableSlots()) {
1055 if (slot.ptr.use_empty())
1056 continue;
1057
1058 MemorySlotPromotionAnalyzer analyzer(slot, dominance, dataLayout);
1059 std::optional<MemorySlotPromotionInfo> info = analyzer.computeInfo();
1060 if (info) {
1061 std::optional<PromotableAllocationOpInterface> newAllocator =
1062 MemorySlotPromoter(slot, allocator, builder, dominance,
1063 dataLayout, std::move(*info), statistics,
1064 blockIndexCache)
1065 .promoteSlot();
1066 changedAllocator = true;
1067 // Add newly created allocators to the worklist for further
1068 // processing.
1069 if (newAllocator)
1070 newWorkList.push_back(*newAllocator);
1071
1072 // A break is required, since promoting a slot may invalidate the
1073 // remaining slots of an allocator.
1074 break;
1075 }
1076 }
1077 if (!changedAllocator)
1078 newWorkList.push_back(allocator);
1079 changesInThisRound |= changedAllocator;
1080 }
1081 if (!changesInThisRound)
1082 break;
1083 promotedAny = true;
1084
1085 // Swap the vector's backing memory and clear the entries in newWorkList
1086 // afterwards. This ensures that additional heap allocations can be avoided.
1087 workList.swap(newWorkList);
1088 newWorkList.clear();
1089 }
1090
1091 return success(promotedAny);
1092}
1093
1094namespace {
1095
1096struct Mem2Reg : impl::Mem2RegBase<Mem2Reg> {
1097 using impl::Mem2RegBase<Mem2Reg>::Mem2RegBase;
1098
1099 void runOnOperation() override {
1100 Operation *scopeOp = getOperation();
1101
1102 Mem2RegStatistics statistics{&promotedAmount, &newBlockArgumentAmount};
1103
1104 bool changed = false;
1105
1106 auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
1107 const DataLayout &dataLayout = dataLayoutAnalysis.getAtOrAbove(scopeOp);
1108 auto &dominance = getAnalysis<DominanceInfo>();
1109
1110 for (Region &region : scopeOp->getRegions()) {
1111 if (region.getBlocks().empty())
1112 continue;
1113
1114 OpBuilder builder(&region.front(), region.front().begin());
1115
1116 SmallVector<PromotableAllocationOpInterface> allocators;
1117 // Build a list of allocators to attempt to promote the slots of.
1118 region.walk([&](PromotableAllocationOpInterface allocator) {
1119 allocators.emplace_back(allocator);
1120 });
1121
1122 // Attempt promoting as many of the slots as possible.
1123 if (succeeded(tryToPromoteMemorySlots(allocators, builder, dataLayout,
1124 dominance, statistics)))
1125 changed = true;
1126 }
1127 if (!changed)
1128 markAllAnalysesPreserved();
1129 }
1130};
1131
1132} // namespace
return success()
lhs
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static void dominanceSort(SmallVector< Operation * > &ops, Region &region, BlockIndexCache &blockIndexCache)
Sorts ops according to dominance.
Definition Mem2Reg.cpp:773
static const DenseMap< Block *, size_t > & getOrCreateBlockIndices(BlockIndexCache &blockIndexCache, Block *regionEntryBlock)
Gets or creates a block index mapping for the region of which the entry block is regionEntryBlock.
Definition Mem2Reg.cpp:754
llvm::IDFCalculatorBase< Block, false > IDFCalculator
Definition Mem2Reg.cpp:504
This class represents an argument of a Block.
Definition Value.h:306
unsigned getArgNumber() const
Returns the number of this argument.
Definition Value.h:318
Block * getOwner() const
Returns the block that owns this argument.
Definition Value.h:315
A block operand represents an operand that holds a reference to a Block, e.g.
Block represents an ordered list of Operations.
Definition Block.h:33
unsigned getNumArguments()
Definition Block.h:138
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
Definition Block.cpp:27
OpListType & getOperations()
Definition Block.h:147
Operation * getTerminator()
Get the terminator operation of this block.
Definition Block.cpp:249
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
BlockArgListType getArguments()
Definition Block.h:97
iterator begin()
Definition Block.h:153
void eraseArgument(unsigned index)
Erase the argument at 'index' and remove it from the argument list.
Definition Block.cpp:198
The main mechanism for performing data layout queries.
A class for computing basic dominance information.
Definition Dominance.h:143
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
This class helps build Operations.
Definition Builders.h:209
void setInsertionPointToStart(Block *block)
Sets the insertion point to the start of the specified block.
Definition Builders.h:433
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:400
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:414
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Block * getBlock()
Returns the operation block that contains this operation.
Definition Operation.h:230
MutableArrayRef< Region > getRegions()
Returns the regions held by this operation.
Definition Operation.h:702
Region * getParentRegion()
Returns the region to which the instruction belongs.
Definition Operation.h:247
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
Block & front()
Definition Region.h:65
Region * getParentRegion()
Return the region containing this region or nullptr if the region is attached to a top-level operatio...
Definition Region.cpp:45
unsigned getRegionNumber()
Return the number of this region in the parent operation.
Definition Region.cpp:62
bool empty()
Definition Region.h:60
Operation * getParentOp()
Return the parent operation this region is attached to.
Definition Region.h:200
BlockListType & getBlocks()
Definition Region.h:45
bool hasOneBlock()
Return true if this region has exactly one block.
Definition Region.h:68
RetT walk(FnT &&callback)
Walk all nested operations, blocks or regions (including this region), depending on the type of callb...
Definition Region.h:296
This class models how operands are forwarded to block arguments in control flow.
void erase(unsigned subStart, unsigned subLen=1)
Erase operands forwarded to the successor.
void append(ValueRange valueRange)
Add new operands that are forwarded to the successor.
unsigned size() const
Returns the amount of operands passed to the successor.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
bool use_empty() const
Returns true if this value has no uses.
Definition Value.h:208
use_range getUses() const
Returns a range of all uses, which is useful for iterating over all uses.
Definition Value.h:188
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
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
DomTree & getDomTree(Region *region) const
Definition Dominance.h:104
void invalidate()
Invalidate dominance info.
Definition Dominance.cpp:37
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
SetVector< Block * > getBlocksSortedByDominance(Region &region)
Gets a list of blocks that is sorted according to dominance.
bool referencesAtMostOneAliasOfSlot(Operation *op, const MemorySlot &rootSlot, const PromotableAliasMap &aliasMap)
Returns true if op's operands reach rootSlot through at most one distinct alias pointer (the root its...
Value convertSlotValueToAliasValue(Value slotValue, const MemorySlot &aliasSlot, const MemorySlot &rootSlot, const PromotableAliasMap &aliasMap, OpBuilder &builder)
Walks the alias chain from rootSlot down to aliasSlot.
void populatePromotableAliasMap(PromotableAliaserInterface aliaser, const MemorySlot &rootSlot, PromotableAliasMap &aliasMap)
Populates aliasMap with alias entries produced by aliaser for operands that already alias rootSlot.
llvm::SetVector< T, Vector, Set, N > SetVector
Definition LLVM.h:125
Value convertAliasValueToSlotValue(Value aliasValue, const MemorySlot &aliasSlot, Value rootReachingDef, const MemorySlot &rootSlot, const PromotableAliasMap &aliasMap, OpBuilder &builder)
Walks the alias chain from aliasSlot back up to rootSlot.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:120
llvm::SmallDenseMap< Value, PromotableSlotAliasInfo, 4 > PromotableAliasMap
Maps an alias slot pointer (a result of a PromotableAliaserInterface op) reachable from a root slot t...
std::optional< MemorySlot > getOpAliasSlot(Operation *op, const MemorySlot &rootSlot, const PromotableAliasMap &aliasMap)
Returns a MemorySlot for the operand of op that aliases rootSlot.ptr (either the root itself or a kno...
LogicalResult tryToPromoteMemorySlots(ArrayRef< PromotableAllocationOpInterface > allocators, OpBuilder &builder, const DataLayout &dataLayout, DominanceInfo &dominance, Mem2RegStatistics statistics={})
Attempts to promote the memory slots of the provided allocators.
Definition Mem2Reg.cpp:1035
void getForwardSlice(Operation *op, SetVector< Operation * > *forwardSlice, const ForwardSliceOptions &options={})
Fills forwardSlice with the computed forward slice (i.e.
Statistics collected while applying mem2reg.
Definition Mem2Reg.h:18
llvm::Statistic * newBlockArgumentAmount
Total amount of new block arguments inserted in blocks.
Definition Mem2Reg.h:22
Represents a slot in memory.
Value ptr
Pointer to the memory slot, used by operations to refer to it.
Type elemType
Type of the value contained in the slot.