Files
jeanPerier 5dc8711b4e [mlir][mem2reg] Promote memory slots through transparent view operations (#196924)
This patch enables mem2reg to operate on load/store that are made
through cast/views of the memory slot (not directly using the SSA value
produced by the allocation op).

This is done by adding a new `PromotableAliaserInterface` interface that must
be implemented by operations that define view of the slot and through which
mem2reg should still happen.

This interface comes with three APIs. The first one allows the operation to
provide new MemorySlots that are aliasing an input MemorySlot. The slot pointers
of these new MemorySlot are results of the operation.

The other two APIs allows projecting the reaching values between the input and output
MemorySlot (one API for each direction).

Assisted by: Claude

RFC:
https://discourse.llvm.org/t/mlir-mem2reg-rfc-promote-memory-slots-through-transparent-view-operations/90761
2026-05-28 10:10:31 +02:00

165 lines
6.2 KiB
C++

//===-- 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;
}