18 #include "llvm/ADT/SetOperations.h"
22 #define GEN_PASS_DEF_CHECKUSESPASS
23 #include "mlir/Dialect/Transform/Transforms/Passes.h.inc"
37 template <
typename FnTy>
39 getReachableImpl(
Block *block, FnTy getNextNodes,
41 auto [it, inserted] = cache.try_emplace(block);
43 return it->getSecond();
47 worklist.push_back(block);
48 while (!worklist.empty()) {
49 Block *current = worklist.pop_back_val();
50 for (
Block *predecessor : getNextNodes(current)) {
53 if (reachable.insert(predecessor).second)
54 worklist.push_back(predecessor);
81 class TransformOpMemFreeAnalysis {
86 explicit TransformOpMemFreeAnalysis(
Operation *root) {
88 if (isa<transform::TransformOpInterface>(op)) {
89 collectFreedValues(op);
98 class PotentialDeleters {
101 static PotentialDeleters live() {
return PotentialDeleters({}); }
105 return PotentialDeleters(deleters);
110 explicit operator bool()
const {
return !deleters.empty(); }
114 PotentialDeleters &
operator|=(
const PotentialDeleters &other) {
115 llvm::append_range(deleters, other.deleters);
125 llvm::append_range(deleters, ops);
136 PotentialDeleters isUseLive(
OpOperand &operand) {
138 if (deleters.empty())
150 isa<OpResult>(operand.
get())
153 auto iface = cast<MemoryEffectOpInterface>(valueSource);
157 assert((isa<BlockArgument>(operand.
get()) ||
158 hasEffect<MemoryEffects::Allocate>(instances, operand.
get())) &&
159 "expected the op defining the value to have an allocation effect "
168 ancestors.push_back(ancestor);
173 std::reverse(ancestors.begin(), ancestors.end());
183 bool isOutermost = ancestor == ancestors.front();
184 bool isFromBlockPartial = isOutermost && isa<OpResult>(operand.
get());
191 if (isFromBlockPartial) {
192 bool defUseSameBlock = ancestor->
getBlock() == defBlock;
196 if (PotentialDeleters potentialDeleters = isFreedInBlockAfter(
198 defUseSameBlock ? ancestor :
nullptr))
199 return potentialDeleters;
205 if (!isFromBlockPartial || ancestor->
getBlock() != defBlock) {
206 if (PotentialDeleters potentialDeleters =
207 isFreedInBlockBefore(ancestor, operand.
get()))
208 return potentialDeleters;
222 if (PotentialDeleters potentialDeleters =
223 isMaybeFreedOnPaths(from, ancestorBlock, operand.
get(),
224 !isFromBlockPartial))
225 return potentialDeleters;
233 return PotentialDeleters::maybeFreed(deleters);
235 static PotentialDeleters live() {
return PotentialDeleters::live(); }
243 auto it = freedBy.find(value);
244 if (it == freedBy.end())
247 for (
Operation *op = getNext(first); op != last; op = getNext(op)) {
248 if (deleters.contains(op))
249 return maybeFreed(op);
258 PotentialDeleters isFreedInBlockAfter(
Operation *root,
Value value,
260 return isFreedBetween(value, root, before,
261 [](
Operation *op) {
return op->getNextNode(); });
266 PotentialDeleters isFreedInBlockBefore(
Operation *root,
Value value)
const {
267 return isFreedBetween(value, root,
nullptr,
268 [](
Operation *op) {
return op->getPrevNode(); });
279 PotentialDeleters isMaybeFreedOnPaths(
Block *from,
Block *to,
Value value,
280 bool alwaysIncludeFrom) {
285 if (!sources.contains(from))
289 llvm::set_intersect(reachable, sources);
293 if (alwaysIncludeFrom)
294 reachable.insert(from);
298 PotentialDeleters potentialDeleters = live();
299 for (
Block *block : reachable) {
301 if (freedBy[value].count(&op))
302 potentialDeleters |= maybeFreed(&op);
305 return potentialDeleters;
312 return getReachableImpl(
319 return getReachableImpl(
325 template <
typename EffectTy>
328 return llvm::any_of(instances,
330 return instance.
getValue() == value &&
337 void collectFreedValues(
Operation *root) {
340 if (isa<transform::PatternDescriptorOpInterface>(child))
344 auto iface = cast<MemoryEffectOpInterface>(child);
349 if (hasEffect<MemoryEffects::Free>(instances, operand)) {
360 freedBy[operand].insert(parent);
382 class CheckUsesPass :
public transform::impl::CheckUsesPassBase<CheckUsesPass> {
384 void runOnOperation()
override {
385 auto &
analysis = getAnalysis<TransformOpMemFreeAnalysis>();
387 getOperation()->walk([&](
Operation *child) {
389 TransformOpMemFreeAnalysis::PotentialDeleters deleters =
390 analysis.isUseLive(operand);
394 InFlightDiagnostic diag = child->emitWarning()
395 <<
"operand #" << operand.getOperandNumber()
396 <<
" may be used after free";
397 diag.attachNote(operand.get().getLoc()) <<
"allocated here";
398 for (Operation *d : deleters.getOps()) {
399 diag.attachNote(d->getLoc()) <<
"freed here";
#define MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(CLASS_NAME)
Block represents an ordered list of Operations.
Region * getParent() const
Provide a 'getParent' method for ilist_node_with_parent methods.
SuccessorRange getSuccessors()
iterator_range< pred_iterator > getPredecessors()
Operation * getParentOp()
Returns the closest surrounding operation that contains this block.
IRValueT get() const
Return the current value being used by this operand.
This class represents an operand of an operation.
Operation is the basic unit of execution within MLIR.
std::enable_if_t< llvm::function_traits< std::decay_t< FnT > >::num_args==1, RetT > walk(FnT &&callback)
Walk the operation by calling the callback for each nested operation (including this one),...
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Block * getBlock()
Returns the operation block that contains this operation.
MutableArrayRef< OpOperand > getOpOperands()
operand_range getOperands()
Returns an iterator on the underlying Value's.
Region * getParentRegion()
Returns the region to which the instruction belongs.
This class represents a specific instance of an effect.
EffectT * getEffect() const
Return the effect being applied.
Value getValue() const
Return the value the effect is applied on, or nullptr if there isn't a known value being affected.
static TransformMappingResource * get()
Returns a unique instance for the given effect class.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Block * getParentBlock()
Return the Block in which this Value is defined.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
static WalkResult advance()
Operation * getOwner() const
Return the owner of this operand.
Include the generated interface declarations.
ChangeResult & operator|=(ChangeResult &lhs, ChangeResult rhs)
bool hasEffect(Operation *op)
Returns "true" if op has an effect of type EffectTy.