blob: 57c00aa031d372ab4f6fd6abb2b235dfb1d63ba2 [file] [edit]
//===-- 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;
}