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 return allocator->emitOpError()
61 <<
"selected for discardable-allocation cleanup but does not have one result";
64 Value allocation = allocator->getResult(0);
65 if (allocation.use_empty()) {
69 for (OpOperand &
use : allocation.getUses()) {
70 Operation *user =
use.getOwner();
71 if (user->mightHaveTrait<OpTrait::IsTerminator>()) {
75 auto accessor = llvm::dyn_cast<DiscardableAllocationAccessorOpInterface>(user);
76 if (!accessor || accessor.loadsFromDiscardableAllocation(allocation) ||
77 !accessor.storesToDiscardableAllocation(allocation) ||
78 !accessor.canEraseAsDeadStoreTo(allocation, dataLayout)) {
81 usersToErase.push_back(user);
88static void collectOperandDefiningOps(
89 llvm::ArrayRef<Operation *> opsToErase,
90 const llvm::SmallPtrSetImpl<Operation *> &opsBeingErased,
91 SmallVectorImpl<Operation *> &maybeDeadDefs
93 llvm::SmallPtrSet<Operation *, 16> seenDefs;
94 for (Operation *op : opsToErase) {
95 for (Value operand : op->getOperands()) {
96 Operation *definingOp = operand.getDefiningOp();
97 if (definingOp && !opsBeingErased.contains(definingOp) &&
98 seenDefs.insert(definingOp).second) {
99 maybeDeadDefs.push_back(definingOp);
106static bool eraseTriviallyDeadDefs(SmallVectorImpl<Operation *> &maybeDeadDefs) {
107 bool changed =
false;
108 bool changedThisIteration =
false;
109 llvm::SmallPtrSet<Operation *, 16> seen(maybeDeadDefs.begin(), maybeDeadDefs.end());
110 llvm::SmallPtrSet<Operation *, 16> erased;
113 changedThisIteration =
false;
114 for (
size_t i = 0; i < maybeDeadDefs.size(); ++i) {
115 Operation *op = maybeDeadDefs[i];
116 if (erased.contains(op) || !isOpTriviallyDead(op)) {
119 for (Value operand : op->getOperands()) {
120 if (Operation *definingOp = operand.getDefiningOp()) {
121 if (seen.insert(definingOp).second) {
122 maybeDeadDefs.push_back(definingOp);
128 changedThisIteration =
true;
130 changed |= changedThisIteration;
131 }
while (changedThisIteration);
139static FailureOr<bool> removeUnusedDiscardableAllocations(
140 ModuleOp module, StringRef allocatorOpName,
const DataLayout &dataLayout
142 bool changed =
false;
143 bool changedThisIteration;
145 SmallVector<Operation *> opsToErase;
146 SmallVector<Operation *> maybeDeadDefs;
147 llvm::SmallPtrSet<Operation *, 16> seen;
149 auto walkResult =
module->walk([&](Operation *allocator) -> WalkResult {
150 if (allocator->getName().getStringRef() != allocatorOpName) {
151 return WalkResult::advance();
153 if (!hasDiscardableAllocationEffect(allocator)) {
154 return allocator->emitOpError(
155 "selected for discardable-allocation cleanup but is not "
156 "marked with MemAlloc<DiscardableAllocationResource>"
160 SmallVector<Operation *> usersToErase;
161 FailureOr<bool> canRemove = collectRemovableUsers(allocator, usersToErase, dataLayout);
162 if (failed(canRemove)) {
163 return WalkResult::interrupt();
166 return WalkResult::advance();
168 for (Operation *user : usersToErase) {
169 if (seen.insert(user).second) {
170 opsToErase.push_back(user);
173 if (seen.insert(allocator).second) {
174 opsToErase.push_back(allocator);
176 return WalkResult::advance();
178 if (walkResult.wasInterrupted()) {
182 collectOperandDefiningOps(opsToErase, seen, maybeDeadDefs);
183 for (Operation *op : opsToErase) {
187 changedThisIteration = !opsToErase.empty();
188 changedThisIteration |= eraseTriviallyDeadDefs(maybeDeadDefs);
189 changed |= changedThisIteration;
190 }
while (changedThisIteration);
196 using Base = RemoveUnusedDiscardableAllocationsPassBase<PassImpl>;
199 void runOnOperation()
override {
200 ModuleOp module = getOperation();
201 if (allocatorOpName.empty()) {
202 module.emitError("requires non-empty 'allocator-op' option");
207 auto &dataLayoutAnalysis = getAnalysis<DataLayoutAnalysis>();
208 const DataLayout &dataLayout = dataLayoutAnalysis.getAtOrAbove(module);
210 FailureOr<bool> changed =
211 removeUnusedDiscardableAllocations(module, allocatorOpName, dataLayout);
212 if (failed(changed)) {
218 markAllAnalysesPreserved();
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use