19 #include "llvm/ADT/SetOperations.h"
23 #define GEN_PASS_DEF_CHECKUSESPASS
24 #include "mlir/Dialect/Transform/Transforms/Passes.h.inc"
38 template <
typename FnTy>
40 getReachableImpl(
Block *block, FnTy getNextNodes,
42 auto [it, inserted] = cache.try_emplace(block);
44 return it->getSecond();
48 worklist.push_back(block);
49 while (!worklist.empty()) {
50 Block *current = worklist.pop_back_val();
51 for (
Block *predecessor : getNextNodes(current)) {
54 if (reachable.insert(predecessor).second)
55 worklist.push_back(predecessor);
82 class TransformOpMemFreeAnalysis {
87 explicit TransformOpMemFreeAnalysis(
Operation *root) {
89 if (isa<transform::TransformOpInterface>(op)) {
90 collectFreedValues(op);
99 class PotentialDeleters {
102 static PotentialDeleters live() {
return PotentialDeleters({}); }
106 return PotentialDeleters(deleters);
111 explicit operator bool()
const {
return !deleters.empty(); }
115 PotentialDeleters &
operator|=(
const PotentialDeleters &other) {
116 llvm::append_range(deleters, other.deleters);
126 llvm::append_range(deleters, ops);
137 PotentialDeleters isUseLive(
OpOperand &operand) {
139 if (deleters.empty())
151 isa<OpResult>(operand.
get())
154 auto iface = cast<MemoryEffectOpInterface>(valueSource);
158 assert((isa<BlockArgument>(operand.
get()) ||
159 hasEffect<MemoryEffects::Allocate>(instances, operand.
get())) &&
160 "expected the op defining the value to have an allocation effect "
169 ancestors.push_back(ancestor);
174 std::reverse(ancestors.begin(), ancestors.end());
184 bool isOutermost = ancestor == ancestors.front();
185 bool isFromBlockPartial = isOutermost && isa<OpResult>(operand.
get());
192 if (isFromBlockPartial) {
193 bool defUseSameBlock = ancestor->
getBlock() == defBlock;
197 if (PotentialDeleters potentialDeleters = isFreedInBlockAfter(
199 defUseSameBlock ? ancestor :
nullptr))
200 return potentialDeleters;
206 if (!isFromBlockPartial || ancestor->
getBlock() != defBlock) {
207 if (PotentialDeleters potentialDeleters =
208 isFreedInBlockBefore(ancestor, operand.
get()))
209 return potentialDeleters;
223 if (PotentialDeleters potentialDeleters =
224 isMaybeFreedOnPaths(from, ancestorBlock, operand.
get(),
225 !isFromBlockPartial))
226 return potentialDeleters;
234 return PotentialDeleters::maybeFreed(deleters);
236 static PotentialDeleters live() {
return PotentialDeleters::live(); }
244 auto it = freedBy.find(value);
245 if (it == freedBy.end())
248 for (
Operation *op = getNext(first); op != last; op = getNext(op)) {
249 if (deleters.contains(op))
250 return maybeFreed(op);
259 PotentialDeleters isFreedInBlockAfter(
Operation *root,
Value value,
261 return isFreedBetween(value, root, before,
262 [](
Operation *op) {
return op->getNextNode(); });
267 PotentialDeleters isFreedInBlockBefore(
Operation *root,
Value value)
const {
268 return isFreedBetween(value, root,
nullptr,
269 [](
Operation *op) {
return op->getPrevNode(); });
280 PotentialDeleters isMaybeFreedOnPaths(
Block *from,
Block *to,
Value value,
281 bool alwaysIncludeFrom) {
286 if (!sources.contains(from))
290 llvm::set_intersect(reachable, sources);
294 if (alwaysIncludeFrom)
295 reachable.insert(from);
299 PotentialDeleters potentialDeleters = live();
300 for (
Block *block : reachable) {
302 if (freedBy[value].count(&op))
303 potentialDeleters |= maybeFreed(&op);
306 return potentialDeleters;
313 return getReachableImpl(
320 return getReachableImpl(
326 template <
typename EffectTy>
329 return llvm::any_of(instances,
331 return instance.
getValue() == value &&
338 void collectFreedValues(
Operation *root) {
343 auto iface = cast<MemoryEffectOpInterface>(child);
348 if (hasEffect<MemoryEffects::Free>(instances, operand)) {
359 freedBy[operand].insert(parent);
381 class CheckUsesPass :
public transform::impl::CheckUsesPassBase<CheckUsesPass> {
383 void runOnOperation()
override {
384 auto &analysis = getAnalysis<TransformOpMemFreeAnalysis>();
386 getOperation()->walk([&](
Operation *child) {
388 TransformOpMemFreeAnalysis::PotentialDeleters deleters =
389 analysis.isUseLive(operand);
393 InFlightDiagnostic diag = child->emitWarning()
394 <<
"operand #" << operand.getOperandNumber()
395 <<
" may be used after free";
396 diag.attachNote(operand.get().getLoc()) <<
"allocated here";
397 for (Operation *d : deleters.getOps()) {
398 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.