| //===-- MemorySlotInterfaces.cpp - MemorySlot interfaces --------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "mlir/Interfaces/MemorySlotInterfaces.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "mlir/Interfaces/MemorySlotOpInterfaces.cpp.inc" | |
| #include "mlir/Interfaces/MemorySlotTypeInterfaces.cpp.inc" | |
| using namespace mlir; | |
| /// Returns the slot describing `aliasPtr`: `rootSlot` if it is the root, | |
| /// the entry in `aliasMap` if it's a known alias, or `nullopt` otherwise. | |
| static std::optional<MemorySlot> | |
| getParentSlot(Value aliasPtr, const MemorySlot &rootSlot, | |
| const PromotableAliasMap &aliasMap) { | |
| if (aliasPtr == rootSlot.ptr) | |
| return rootSlot; | |
| auto it = aliasMap.find(aliasPtr); | |
| if (it == aliasMap.end()) | |
| return std::nullopt; | |
| return it->second.slot; | |
| } | |
| void mlir::populatePromotableAliasMap(PromotableAliaserInterface aliaser, | |
| const MemorySlot &rootSlot, | |
| PromotableAliasMap &aliasMap) { | |
| for (OpOperand &operand : aliaser->getOpOperands()) { | |
| std::optional<MemorySlot> parentSlot = | |
| getParentSlot(operand.get(), rootSlot, aliasMap); | |
| if (!parentSlot) | |
| continue; | |
| SmallVector<MemorySlot, 2> newSlots; | |
| aliaser.getPromotableSlotAliases(operand, *parentSlot, newSlots); | |
| for (const MemorySlot &alias : newSlots) | |
| aliasMap.try_emplace(alias.ptr, PromotableSlotAliasInfo{alias, &operand}); | |
| } | |
| } | |
| std::optional<MemorySlot> | |
| mlir::getOpAliasSlot(Operation *op, const MemorySlot &rootSlot, | |
| const PromotableAliasMap &aliasMap) { | |
| for (Value operand : op->getOperands()) | |
| if (std::optional<MemorySlot> slot = | |
| getParentSlot(operand, rootSlot, aliasMap)) | |
| return slot; | |
| return std::nullopt; | |
| } | |
| bool mlir::referencesAtMostOneAliasOfSlot(Operation *op, | |
| const MemorySlot &rootSlot, | |
| const PromotableAliasMap &aliasMap) { | |
| Value uniqueAliasPtr; | |
| for (Value operand : op->getOperands()) { | |
| std::optional<MemorySlot> slot = getParentSlot(operand, rootSlot, aliasMap); | |
| if (!slot) | |
| continue; | |
| if (uniqueAliasPtr && uniqueAliasPtr != slot->ptr) | |
| return false; | |
| uniqueAliasPtr = slot->ptr; | |
| } | |
| return true; | |
| } | |
| namespace { | |
| /// A step in an alias chain, from leaf to root. `parentSlot` is one step | |
| /// closer to the root; `aliasSlot` is the slot exposed at this step. | |
| struct ChainStep { | |
| PromotableAliaserInterface aliaser; | |
| OpOperand *aliasedSlotPointerOperand; | |
| MemorySlot parentSlot; | |
| MemorySlot aliasSlot; | |
| }; | |
| } // namespace | |
| /// Walks from `aliasSlot` back to `rootSlot` via `aliasMap`. Returns the | |
| /// leaf-to-root chain, or `nullopt` if `aliasSlot` is not a known alias. | |
| static std::optional<SmallVector<ChainStep>> | |
| buildAliasChain(const MemorySlot &aliasSlot, const MemorySlot &rootSlot, | |
| const PromotableAliasMap &aliasMap) { | |
| SmallVector<ChainStep> chain; | |
| Value current = aliasSlot.ptr; | |
| while (current != rootSlot.ptr) { | |
| auto it = aliasMap.find(current); | |
| if (it == aliasMap.end()) | |
| return std::nullopt; | |
| OpOperand *operand = it->second.aliasedSlotPointerOperand; | |
| auto aliaser = cast<PromotableAliaserInterface>(operand->getOwner()); | |
| std::optional<MemorySlot> parent = | |
| getParentSlot(operand->get(), rootSlot, aliasMap); | |
| if (!parent) | |
| return std::nullopt; | |
| chain.push_back(ChainStep{aliaser, operand, *parent, it->second.slot}); | |
| current = operand->get(); | |
| } | |
| return chain; | |
| } | |
| Value mlir::convertSlotValueToAliasValue(Value slotValue, | |
| const MemorySlot &aliasSlot, | |
| const MemorySlot &rootSlot, | |
| const PromotableAliasMap &aliasMap, | |
| OpBuilder &builder) { | |
| std::optional<SmallVector<ChainStep>> chain = | |
| buildAliasChain(aliasSlot, rootSlot, aliasMap); | |
| if (!chain) | |
| return {}; | |
| Value current = slotValue; | |
| // Root-to-leaf walk: reverse the leaf-first chain. | |
| for (ChainStep &step : llvm::reverse(*chain)) { | |
| current = step.aliaser.projectSlotValueToAliasValue( | |
| *step.aliasedSlotPointerOperand, step.parentSlot, step.aliasSlot, | |
| current, builder); | |
| if (!current) | |
| return {}; | |
| } | |
| return current; | |
| } | |
| Value mlir::convertAliasValueToSlotValue(Value aliasValue, | |
| const MemorySlot &aliasSlot, | |
| Value rootReachingDef, | |
| const MemorySlot &rootSlot, | |
| const PromotableAliasMap &aliasMap, | |
| OpBuilder &builder) { | |
| std::optional<SmallVector<ChainStep>> chainOpt = | |
| buildAliasChain(aliasSlot, rootSlot, aliasMap); | |
| if (!chainOpt) | |
| return {}; | |
| SmallVector<ChainStep> &chain = *chainOpt; | |
| // Project `rootReachingDef` down to each step's parent level so the | |
| // per-step projector can use it (needed for partial sub-aliases; full | |
| // aliases ignore it). The chain is leaf-first, so `chain.back()` is the | |
| // root-most step (parent = rootSlot) and `chain.front()` is the leaf. | |
| SmallVector<Value> perStepReachingDef(chain.size()); | |
| Value current = rootReachingDef; | |
| for (int i = static_cast<int>(chain.size()) - 1; i >= 0; --i) { | |
| perStepReachingDef[i] = current; | |
| current = chain[i].aliaser.projectSlotValueToAliasValue( | |
| *chain[i].aliasedSlotPointerOperand, chain[i].parentSlot, | |
| chain[i].aliasSlot, current, builder); | |
| if (!current) | |
| return {}; | |
| } | |
| // Walk leaf-to-root, combining `aliasValue` with the projected reaching | |
| // definition at each step. | |
| current = aliasValue; | |
| for (size_t i = 0; i < chain.size(); ++i) { | |
| current = chain[i].aliaser.projectAliasValueToSlotValue( | |
| *chain[i].aliasedSlotPointerOperand, chain[i].parentSlot, | |
| chain[i].aliasSlot, current, perStepReachingDef[i], builder); | |
| if (!current) | |
| return {}; | |
| } | |
| return current; | |
| } |