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