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;
146 SmallVector<Operation *> opsToErase;
147 SmallVector<Operation *> maybeDeadDefs;
148 llvm::SmallPtrSet<Operation *, 16> seen;
150 WalkResult walkResult =
module->walk([&](Operation *allocator) {
151 if (allocator->getName().getStringRef() != allocatorOpName) {
152 return WalkResult::advance();
154 if (!hasDiscardableAllocationEffect(allocator)) {
155 allocator->emitOpError()
156 <<
"selected for discardable-allocation cleanup but is not marked with "
157 "MemAlloc<DiscardableAllocationResource>";
158 return WalkResult::interrupt();
161 SmallVector<Operation *> usersToErase;
162 FailureOr<bool> canRemove = collectRemovableUsers(allocator, usersToErase, dataLayout);
163 if (failed(canRemove)) {
164 return WalkResult::interrupt();
167 return WalkResult::advance();
169 for (Operation *user : usersToErase) {
170 if (seen.insert(user).second) {
171 opsToErase.push_back(user);
174 if (seen.insert(allocator).second) {
175 opsToErase.push_back(allocator);
177 return WalkResult::advance();
179 if (walkResult.wasInterrupted()) {
183 collectOperandDefiningOps(opsToErase, seen, maybeDeadDefs);
184 for (Operation *op : opsToErase) {
188 changedThisIteration = !opsToErase.empty();
189 changedThisIteration |= eraseTriviallyDeadDefs(maybeDeadDefs);
190 changed |= changedThisIteration;
191 }
while (changedThisIteration);
197 using Base = RemoveUnusedDiscardableAllocationsPassBase<PassImpl>;
200 void runOnOperation()
override {
201 ModuleOp module = getOperation();
202 if (allocatorOpName.empty()) {
203 module.emitError() << "requires non-empty 'allocator-op' option";
208 auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
209 const DataLayout &dataLayout = dataLayoutAnalysis.getAtOrAbove(module);
211 FailureOr<bool> changed =
212 removeUnusedDiscardableAllocations(module, allocatorOpName, dataLayout);
213 if (failed(changed)) {
219 markAllAnalysesPreserved();
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use