18#include <mlir/Analysis/DataLayoutAnalysis.h>
19#include <mlir/IR/BuiltinOps.h>
20#include <mlir/IR/Operation.h>
21#include <mlir/Interfaces/SideEffectInterfaces.h>
23#include <llvm/ADT/ArrayRef.h>
24#include <llvm/ADT/STLExtras.h>
25#include <llvm/ADT/SmallPtrSet.h>
26#include <llvm/ADT/SmallVector.h>
27#include <llvm/Support/Casting.h>
31#define GEN_PASS_DEF_REMOVEUNUSEDDISCARDABLEALLOCATIONSPASS
41static bool hasDiscardableAllocationEffect(Operation *allocator) {
42 auto effectInterface = llvm::dyn_cast<MemoryEffectOpInterface>(allocator);
43 if (!effectInterface) {
47 llvm::SmallVector<SideEffects::EffectInstance<MemoryEffects::Effect>, 4> effects;
48 effectInterface.getEffects(effects);
49 return llvm::any_of(effects, [](
const auto &effect) {
50 return llvm::isa<MemoryEffects::Allocate>(effect.getEffect()) &&
51 llvm::isa<DiscardableAllocationResource>(effect.getResource());
56static FailureOr<bool> collectRemovableUsers(
57 Operation *allocator, SmallVectorImpl<Operation *> &usersToErase,
const DataLayout &dataLayout
59 if (allocator->getNumResults() != 1) {
60 allocator->emitOpError()
61 <<
"selected for discardable-allocation cleanup but does not have one result";
65 Value allocation = allocator->getResult(0);
66 if (allocation.use_empty()) {
70 for (OpOperand &
use : allocation.getUses()) {
71 Operation *user =
use.getOwner();
72 if (user->mightHaveTrait<OpTrait::IsTerminator>()) {
76 auto accessor = llvm::dyn_cast<DiscardableAllocationAccessorOpInterface>(user);
77 if (!accessor || accessor.loadsFromDiscardableAllocation(allocation) ||
78 !accessor.storesToDiscardableAllocation(allocation) ||
79 !accessor.canEraseAsDeadStoreTo(allocation, dataLayout)) {
82 usersToErase.push_back(user);
89static void collectOperandDefiningOps(
90 llvm::ArrayRef<Operation *> opsToErase,
91 const llvm::SmallPtrSetImpl<Operation *> &opsBeingErased,
92 SmallVectorImpl<Operation *> &maybeDeadDefs
94 llvm::SmallPtrSet<Operation *, 16> seenDefs;
95 for (Operation *op : opsToErase) {
96 for (Value operand : op->getOperands()) {
97 Operation *definingOp = operand.getDefiningOp();
98 if (definingOp && !opsBeingErased.contains(definingOp) &&
99 seenDefs.insert(definingOp).second) {
100 maybeDeadDefs.push_back(definingOp);
107static bool eraseTriviallyDeadDefs(SmallVectorImpl<Operation *> &maybeDeadDefs) {
108 bool changed =
false;
109 bool changedThisIteration =
false;
110 llvm::SmallPtrSet<Operation *, 16> seen(maybeDeadDefs.begin(), maybeDeadDefs.end());
111 llvm::SmallPtrSet<Operation *, 16> erased;
114 changedThisIteration =
false;
115 for (
size_t i = 0; i < maybeDeadDefs.size(); ++i) {
116 Operation *op = maybeDeadDefs[i];
117 if (erased.contains(op) || !isOpTriviallyDead(op)) {
120 for (Value operand : op->getOperands()) {
121 if (Operation *definingOp = operand.getDefiningOp()) {
122 if (seen.insert(definingOp).second) {
123 maybeDeadDefs.push_back(definingOp);
129 changedThisIteration =
true;
131 changed |= changedThisIteration;
132 }
while (changedThisIteration);
140static FailureOr<bool> removeUnusedDiscardableAllocations(
141 ModuleOp module, StringRef allocatorOpName,
const DataLayout &dataLayout
143 bool changed =
false;
144 bool changedThisIteration =
false;
146 changedThisIteration =
false;
147 SmallVector<Operation *> opsToErase;
148 SmallVector<Operation *> maybeDeadDefs;
149 llvm::SmallPtrSet<Operation *, 16> seen;
151 WalkResult walkResult =
module->walk([&](Operation *allocator) {
152 if (allocator->getName().getStringRef() != allocatorOpName) {
153 return WalkResult::advance();
155 if (!hasDiscardableAllocationEffect(allocator)) {
156 allocator->emitOpError()
157 <<
"selected for discardable-allocation cleanup but is not marked with "
158 "MemAlloc<DiscardableAllocationResource>";
159 return WalkResult::interrupt();
162 SmallVector<Operation *> usersToErase;
163 FailureOr<bool> canRemove = collectRemovableUsers(allocator, usersToErase, dataLayout);
164 if (failed(canRemove)) {
165 return WalkResult::interrupt();
168 return WalkResult::advance();
170 for (Operation *user : usersToErase) {
171 if (seen.insert(user).second) {
172 opsToErase.push_back(user);
175 if (seen.insert(allocator).second) {
176 opsToErase.push_back(allocator);
178 return WalkResult::advance();
180 if (walkResult.wasInterrupted()) {
184 collectOperandDefiningOps(opsToErase, seen, maybeDeadDefs);
185 for (Operation *op : opsToErase) {
189 changedThisIteration = !opsToErase.empty();
190 changedThisIteration |= eraseTriviallyDeadDefs(maybeDeadDefs);
191 changed |= changedThisIteration;
192 }
while (changedThisIteration);
198 using Base = RemoveUnusedDiscardableAllocationsPassBase<PassImpl>;
201 void runOnOperation()
override {
202 ModuleOp module = getOperation();
203 if (allocatorOpName.empty()) {
204 module.emitError() << "requires non-empty 'allocator-op' option";
209 auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
210 const DataLayout &dataLayout = dataLayoutAnalysis.getAtOrAbove(module);
212 FailureOr<bool> changed =
213 removeUnusedDiscardableAllocations(module, allocatorOpName, dataLayout);
214 if (failed(changed)) {
220 markAllAnalysesPreserved();
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use