xref: /llvm-project/mlir/lib/Dialect/Transform/Transforms/CheckUses.cpp (revision 938cdd60d4938e32a7f4f1620e3d9c11aabc4af5)
173c3dff1SAlex Zinenko //===- CheckUses.cpp - Expensive transform value validity checks ----------===//
273c3dff1SAlex Zinenko //
373c3dff1SAlex Zinenko // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
473c3dff1SAlex Zinenko // See https://llvm.org/LICENSE.txt for license information.
573c3dff1SAlex Zinenko // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
673c3dff1SAlex Zinenko //
773c3dff1SAlex Zinenko //===----------------------------------------------------------------------===//
873c3dff1SAlex Zinenko //
973c3dff1SAlex Zinenko // This file defines a pass that performs expensive opt-in checks for Transform
1073c3dff1SAlex Zinenko // dialect values being potentially used after they have been consumed.
1173c3dff1SAlex Zinenko //
1273c3dff1SAlex Zinenko //===----------------------------------------------------------------------===//
1373c3dff1SAlex Zinenko 
14039b969bSMichele Scuttari #include "mlir/Dialect/Transform/Transforms/Passes.h"
1567d0d7acSMichele Scuttari 
165a9bdd85SOleksandr "Alex" Zinenko #include "mlir/Dialect/Transform/Interfaces/TransformInterfaces.h"
1773c3dff1SAlex Zinenko #include "mlir/Interfaces/SideEffectInterfaces.h"
1873c3dff1SAlex Zinenko #include "mlir/Pass/Pass.h"
1973c3dff1SAlex Zinenko #include "llvm/ADT/SetOperations.h"
2073c3dff1SAlex Zinenko 
2167d0d7acSMichele Scuttari namespace mlir {
2267d0d7acSMichele Scuttari namespace transform {
23b512b1c8SMehdi Amini #define GEN_PASS_DEF_CHECKUSESPASS
2467d0d7acSMichele Scuttari #include "mlir/Dialect/Transform/Transforms/Passes.h.inc"
2567d0d7acSMichele Scuttari } // namespace transform
2667d0d7acSMichele Scuttari } // namespace mlir
2767d0d7acSMichele Scuttari 
2873c3dff1SAlex Zinenko using namespace mlir;
2973c3dff1SAlex Zinenko 
3073c3dff1SAlex Zinenko namespace {
3173c3dff1SAlex Zinenko 
3273c3dff1SAlex Zinenko /// Returns a reference to a cached set of blocks that are reachable from the
3373c3dff1SAlex Zinenko /// given block via edges computed by the `getNextNodes` function. For example,
3473c3dff1SAlex Zinenko /// if `getNextNodes` returns successors of a block, this will return the set of
3573c3dff1SAlex Zinenko /// reachable blocks; if it returns predecessors of a block, this will return
3673c3dff1SAlex Zinenko /// the set of blocks from which the given block can be reached. The block is
3773c3dff1SAlex Zinenko /// considered reachable form itself only if there is a cycle.
3873c3dff1SAlex Zinenko template <typename FnTy>
3973c3dff1SAlex Zinenko const llvm::SmallPtrSet<Block *, 4> &
4073c3dff1SAlex Zinenko getReachableImpl(Block *block, FnTy getNextNodes,
4173c3dff1SAlex Zinenko                  DenseMap<Block *, llvm::SmallPtrSet<Block *, 4>> &cache) {
427d9f9938SKazu Hirata   auto [it, inserted] = cache.try_emplace(block);
437d9f9938SKazu Hirata   if (!inserted)
4473c3dff1SAlex Zinenko     return it->getSecond();
4573c3dff1SAlex Zinenko 
467d9f9938SKazu Hirata   llvm::SmallPtrSet<Block *, 4> &reachable = it->second;
4773c3dff1SAlex Zinenko   SmallVector<Block *> worklist;
4873c3dff1SAlex Zinenko   worklist.push_back(block);
4973c3dff1SAlex Zinenko   while (!worklist.empty()) {
5073c3dff1SAlex Zinenko     Block *current = worklist.pop_back_val();
5173c3dff1SAlex Zinenko     for (Block *predecessor : getNextNodes(current)) {
5273c3dff1SAlex Zinenko       // The block is reachable from its transitive predecessors. Only add
5373c3dff1SAlex Zinenko       // them to the worklist if they weren't already visited.
5473c3dff1SAlex Zinenko       if (reachable.insert(predecessor).second)
5573c3dff1SAlex Zinenko         worklist.push_back(predecessor);
5673c3dff1SAlex Zinenko     }
5773c3dff1SAlex Zinenko   }
5873c3dff1SAlex Zinenko   return reachable;
5973c3dff1SAlex Zinenko }
6073c3dff1SAlex Zinenko 
6173c3dff1SAlex Zinenko /// An analysis that identifies whether a value allocated by a Transform op may
6273c3dff1SAlex Zinenko /// be used by another such op after it may have been freed by a third op on
6373c3dff1SAlex Zinenko /// some control flow path. This is conceptually similar to a data flow
6473c3dff1SAlex Zinenko /// analysis, but relies on side effects related to particular values that
6573c3dff1SAlex Zinenko /// currently cannot be modeled by the MLIR data flow analysis framework (also,
6673c3dff1SAlex Zinenko /// the lattice element would be rather expensive as it would need to include
6773c3dff1SAlex Zinenko /// live and/or freed values for each operation).
6873c3dff1SAlex Zinenko ///
6973c3dff1SAlex Zinenko /// This analysis is conservatively pessimisic: it will consider that a value
7073c3dff1SAlex Zinenko /// may be freed if it is freed on any possible control flow path between its
7173c3dff1SAlex Zinenko /// allocation and a relevant use, even if the control never actually flows
7273c3dff1SAlex Zinenko /// through the operation that frees the value. It also does not differentiate
7373c3dff1SAlex Zinenko /// between may- (freed on at least one control flow path) and must-free (freed
7473c3dff1SAlex Zinenko /// on all possible control flow paths) because it would require expensive graph
7573c3dff1SAlex Zinenko /// algorithms.
7673c3dff1SAlex Zinenko ///
7773c3dff1SAlex Zinenko /// It is intended as an additional non-blocking verification or debugging aid
7873c3dff1SAlex Zinenko /// for ops in the Transform dialect. It leverages the requirement for Transform
7973c3dff1SAlex Zinenko /// dialect ops to implement the MemoryEffectsOpInterface, and expects the
8073c3dff1SAlex Zinenko /// values in the Transform IR to have an allocation effect on the
8173c3dff1SAlex Zinenko /// TransformMappingResource when defined.
8273c3dff1SAlex Zinenko class TransformOpMemFreeAnalysis {
8373c3dff1SAlex Zinenko public:
8473c3dff1SAlex Zinenko   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TransformOpMemFreeAnalysis)
8573c3dff1SAlex Zinenko 
8673c3dff1SAlex Zinenko   /// Computes the analysis for Transform ops nested in the given operation.
8773c3dff1SAlex Zinenko   explicit TransformOpMemFreeAnalysis(Operation *root) {
8873c3dff1SAlex Zinenko     root->walk([&](Operation *op) {
8973c3dff1SAlex Zinenko       if (isa<transform::TransformOpInterface>(op)) {
9073c3dff1SAlex Zinenko         collectFreedValues(op);
9173c3dff1SAlex Zinenko         return WalkResult::skip();
9273c3dff1SAlex Zinenko       }
9373c3dff1SAlex Zinenko       return WalkResult::advance();
9473c3dff1SAlex Zinenko     });
9573c3dff1SAlex Zinenko   }
9673c3dff1SAlex Zinenko 
9773c3dff1SAlex Zinenko   /// A list of operations that may be deleting a value. Non-empty list
9873c3dff1SAlex Zinenko   /// contextually converts to boolean "true" value.
9973c3dff1SAlex Zinenko   class PotentialDeleters {
10073c3dff1SAlex Zinenko   public:
10173c3dff1SAlex Zinenko     /// Creates an empty list that corresponds to the value being live.
10273c3dff1SAlex Zinenko     static PotentialDeleters live() { return PotentialDeleters({}); }
10373c3dff1SAlex Zinenko 
10473c3dff1SAlex Zinenko     /// Creates a list from the operations that may be deleting the value.
10573c3dff1SAlex Zinenko     static PotentialDeleters maybeFreed(ArrayRef<Operation *> deleters) {
10673c3dff1SAlex Zinenko       return PotentialDeleters(deleters);
10773c3dff1SAlex Zinenko     }
10873c3dff1SAlex Zinenko 
10973c3dff1SAlex Zinenko     /// Converts to "true" if there are operations that may be deleting the
11073c3dff1SAlex Zinenko     /// value.
11173c3dff1SAlex Zinenko     explicit operator bool() const { return !deleters.empty(); }
11273c3dff1SAlex Zinenko 
11373c3dff1SAlex Zinenko     /// Concatenates the lists of operations that may be deleting the value. The
11473c3dff1SAlex Zinenko     /// value is known to be live if the reuslting list is still empty.
11573c3dff1SAlex Zinenko     PotentialDeleters &operator|=(const PotentialDeleters &other) {
11673c3dff1SAlex Zinenko       llvm::append_range(deleters, other.deleters);
11773c3dff1SAlex Zinenko       return *this;
11873c3dff1SAlex Zinenko     }
11973c3dff1SAlex Zinenko 
12073c3dff1SAlex Zinenko     /// Returns the list of ops that may be deleting the value.
12173c3dff1SAlex Zinenko     ArrayRef<Operation *> getOps() const { return deleters; }
12273c3dff1SAlex Zinenko 
12373c3dff1SAlex Zinenko   private:
12473c3dff1SAlex Zinenko     /// Constructs the list from the given operations.
12573c3dff1SAlex Zinenko     explicit PotentialDeleters(ArrayRef<Operation *> ops) {
12673c3dff1SAlex Zinenko       llvm::append_range(deleters, ops);
12773c3dff1SAlex Zinenko     }
12873c3dff1SAlex Zinenko 
12973c3dff1SAlex Zinenko     /// The list of operations that may be deleting the value.
13073c3dff1SAlex Zinenko     SmallVector<Operation *> deleters;
13173c3dff1SAlex Zinenko   };
13273c3dff1SAlex Zinenko 
13373c3dff1SAlex Zinenko   /// Returns the list of operations that may be deleting the operand value on
13473c3dff1SAlex Zinenko   /// any control flow path between the definition of the value and its use as
13573c3dff1SAlex Zinenko   /// the given operand. For the purposes of this analysis, the value is
13673c3dff1SAlex Zinenko   /// considered to be allocated at its definition point and never re-allocated.
13773c3dff1SAlex Zinenko   PotentialDeleters isUseLive(OpOperand &operand) {
13873c3dff1SAlex Zinenko     const llvm::SmallPtrSet<Operation *, 2> &deleters = freedBy[operand.get()];
13973c3dff1SAlex Zinenko     if (deleters.empty())
14073c3dff1SAlex Zinenko       return live();
14173c3dff1SAlex Zinenko 
14273c3dff1SAlex Zinenko #ifndef NDEBUG
143d46afeefSAlex Zinenko     // Check that the definition point actually allocates the value. If the
144d46afeefSAlex Zinenko     // definition is a block argument, it may be just forwarding the operand of
145d46afeefSAlex Zinenko     // the parent op without doing a new allocation, allow that. We currently
146d46afeefSAlex Zinenko     // don't have the capability to analyze region-based control flow here.
147d46afeefSAlex Zinenko     //
148d46afeefSAlex Zinenko     // TODO: when this ported to the dataflow analysis infra, we should have
149d46afeefSAlex Zinenko     // proper support for region-based control flow.
15073c3dff1SAlex Zinenko     Operation *valueSource =
1515550c821STres Popp         isa<OpResult>(operand.get())
15273c3dff1SAlex Zinenko             ? operand.get().getDefiningOp()
15373c3dff1SAlex Zinenko             : operand.get().getParentBlock()->getParentOp();
15473c3dff1SAlex Zinenko     auto iface = cast<MemoryEffectOpInterface>(valueSource);
15573c3dff1SAlex Zinenko     SmallVector<MemoryEffects::EffectInstance> instances;
15673c3dff1SAlex Zinenko     iface.getEffectsOnResource(transform::TransformMappingResource::get(),
15773c3dff1SAlex Zinenko                                instances);
1585550c821STres Popp     assert((isa<BlockArgument>(operand.get()) ||
159d46afeefSAlex Zinenko             hasEffect<MemoryEffects::Allocate>(instances, operand.get())) &&
16073c3dff1SAlex Zinenko            "expected the op defining the value to have an allocation effect "
16173c3dff1SAlex Zinenko            "on it");
16273c3dff1SAlex Zinenko #endif
16373c3dff1SAlex Zinenko 
16473c3dff1SAlex Zinenko     // Collect ancestors of the use operation.
16573c3dff1SAlex Zinenko     Block *defBlock = operand.get().getParentBlock();
16673c3dff1SAlex Zinenko     SmallVector<Operation *> ancestors;
16773c3dff1SAlex Zinenko     Operation *ancestor = operand.getOwner();
16873c3dff1SAlex Zinenko     do {
16973c3dff1SAlex Zinenko       ancestors.push_back(ancestor);
17073c3dff1SAlex Zinenko       if (ancestor->getParentRegion() == defBlock->getParent())
17173c3dff1SAlex Zinenko         break;
17273c3dff1SAlex Zinenko       ancestor = ancestor->getParentOp();
17373c3dff1SAlex Zinenko     } while (true);
17473c3dff1SAlex Zinenko     std::reverse(ancestors.begin(), ancestors.end());
17573c3dff1SAlex Zinenko 
17673c3dff1SAlex Zinenko     // Consider the control flow from the definition point of the value to its
17773c3dff1SAlex Zinenko     // use point. If the use is located in some nested region, consider the path
17873c3dff1SAlex Zinenko     // from the entry block of the region to the use.
17973c3dff1SAlex Zinenko     for (Operation *ancestor : ancestors) {
18073c3dff1SAlex Zinenko       // The block should be considered partially if it is the block that
18173c3dff1SAlex Zinenko       // contains the definition (allocation) of the value being used, and the
18273c3dff1SAlex Zinenko       // value is defined in the middle of the block, i.e., is not a block
18373c3dff1SAlex Zinenko       // argument.
18473c3dff1SAlex Zinenko       bool isOutermost = ancestor == ancestors.front();
1855550c821STres Popp       bool isFromBlockPartial = isOutermost && isa<OpResult>(operand.get());
18673c3dff1SAlex Zinenko 
18773c3dff1SAlex Zinenko       // Check if the value may be freed by operations between its definition
18873c3dff1SAlex Zinenko       // (allocation) point in its block and the terminator of the block or the
18973c3dff1SAlex Zinenko       // ancestor of the use if it is located in the same block. This is only
19073c3dff1SAlex Zinenko       // done for partial blocks here, full blocks will be considered below
19173c3dff1SAlex Zinenko       // similarly to other blocks.
19273c3dff1SAlex Zinenko       if (isFromBlockPartial) {
19373c3dff1SAlex Zinenko         bool defUseSameBlock = ancestor->getBlock() == defBlock;
19473c3dff1SAlex Zinenko         // Consider all ops from the def to its block terminator, except the
19573c3dff1SAlex Zinenko         // when the use is in the same block, in which case only consider the
19673c3dff1SAlex Zinenko         // ops until the user.
19773c3dff1SAlex Zinenko         if (PotentialDeleters potentialDeleters = isFreedInBlockAfter(
19873c3dff1SAlex Zinenko                 operand.get().getDefiningOp(), operand.get(),
19973c3dff1SAlex Zinenko                 defUseSameBlock ? ancestor : nullptr))
20073c3dff1SAlex Zinenko           return potentialDeleters;
20173c3dff1SAlex Zinenko       }
20273c3dff1SAlex Zinenko 
20373c3dff1SAlex Zinenko       // Check if the value may be freed by opeations preceding the ancestor in
20473c3dff1SAlex Zinenko       // its block. Skip the check for partial blocks that contain both the
20573c3dff1SAlex Zinenko       // definition and the use point, as this has been already checked above.
20673c3dff1SAlex Zinenko       if (!isFromBlockPartial || ancestor->getBlock() != defBlock) {
20773c3dff1SAlex Zinenko         if (PotentialDeleters potentialDeleters =
20873c3dff1SAlex Zinenko                 isFreedInBlockBefore(ancestor, operand.get()))
20973c3dff1SAlex Zinenko           return potentialDeleters;
21073c3dff1SAlex Zinenko       }
21173c3dff1SAlex Zinenko 
21273c3dff1SAlex Zinenko       // Check if the value may be freed by operations in any of the blocks
21373c3dff1SAlex Zinenko       // between the definition point (in the outermost region) or the entry
21473c3dff1SAlex Zinenko       // block of the region (in other regions) and the operand or its ancestor
21573c3dff1SAlex Zinenko       // in the region. This includes the entire "form" block if (1) the block
21673c3dff1SAlex Zinenko       // has not been considered as partial above and (2) the block can be
21773c3dff1SAlex Zinenko       // reached again through some control-flow loop. This includes the entire
21873c3dff1SAlex Zinenko       // "to" block if it can be reached form itself through some control-flow
21973c3dff1SAlex Zinenko       // cycle, regardless of whether it has been visited before.
22073c3dff1SAlex Zinenko       Block *ancestorBlock = ancestor->getBlock();
22173c3dff1SAlex Zinenko       Block *from =
22273c3dff1SAlex Zinenko           isOutermost ? defBlock : &ancestorBlock->getParent()->front();
22373c3dff1SAlex Zinenko       if (PotentialDeleters potentialDeleters =
22473c3dff1SAlex Zinenko               isMaybeFreedOnPaths(from, ancestorBlock, operand.get(),
22573c3dff1SAlex Zinenko                                   /*alwaysIncludeFrom=*/!isFromBlockPartial))
22673c3dff1SAlex Zinenko         return potentialDeleters;
22773c3dff1SAlex Zinenko     }
22873c3dff1SAlex Zinenko     return live();
22973c3dff1SAlex Zinenko   }
23073c3dff1SAlex Zinenko 
23173c3dff1SAlex Zinenko private:
23273c3dff1SAlex Zinenko   /// Make PotentialDeleters constructors available with shorter names.
23373c3dff1SAlex Zinenko   static PotentialDeleters maybeFreed(ArrayRef<Operation *> deleters) {
23473c3dff1SAlex Zinenko     return PotentialDeleters::maybeFreed(deleters);
23573c3dff1SAlex Zinenko   }
23673c3dff1SAlex Zinenko   static PotentialDeleters live() { return PotentialDeleters::live(); }
23773c3dff1SAlex Zinenko 
23873c3dff1SAlex Zinenko   /// Returns the list of operations that may be deleting the given value betwen
23973c3dff1SAlex Zinenko   /// the first and last operations, non-inclusive. `getNext` indicates the
24073c3dff1SAlex Zinenko   /// direction of the traversal.
24173c3dff1SAlex Zinenko   PotentialDeleters
24273c3dff1SAlex Zinenko   isFreedBetween(Value value, Operation *first, Operation *last,
24373c3dff1SAlex Zinenko                  llvm::function_ref<Operation *(Operation *)> getNext) const {
24473c3dff1SAlex Zinenko     auto it = freedBy.find(value);
24573c3dff1SAlex Zinenko     if (it == freedBy.end())
24673c3dff1SAlex Zinenko       return live();
24773c3dff1SAlex Zinenko     const llvm::SmallPtrSet<Operation *, 2> &deleters = it->getSecond();
24873c3dff1SAlex Zinenko     for (Operation *op = getNext(first); op != last; op = getNext(op)) {
24973c3dff1SAlex Zinenko       if (deleters.contains(op))
25073c3dff1SAlex Zinenko         return maybeFreed(op);
25173c3dff1SAlex Zinenko     }
25273c3dff1SAlex Zinenko     return live();
25373c3dff1SAlex Zinenko   }
25473c3dff1SAlex Zinenko 
25573c3dff1SAlex Zinenko   /// Returns the list of operations that may be deleting the given value
25673c3dff1SAlex Zinenko   /// between `root` and `before` values. `root` is expected to be in the same
25773c3dff1SAlex Zinenko   /// block as `before` and precede it. If `before` is null, consider all
25873c3dff1SAlex Zinenko   /// operations until the end of the block including the terminator.
25973c3dff1SAlex Zinenko   PotentialDeleters isFreedInBlockAfter(Operation *root, Value value,
26073c3dff1SAlex Zinenko                                         Operation *before = nullptr) const {
26173c3dff1SAlex Zinenko     return isFreedBetween(value, root, before,
26273c3dff1SAlex Zinenko                           [](Operation *op) { return op->getNextNode(); });
26373c3dff1SAlex Zinenko   }
26473c3dff1SAlex Zinenko 
26573c3dff1SAlex Zinenko   /// Returns the list of operations that may be deleting the given value
26673c3dff1SAlex Zinenko   /// between the entry of the block and the `root` operation.
26773c3dff1SAlex Zinenko   PotentialDeleters isFreedInBlockBefore(Operation *root, Value value) const {
26873c3dff1SAlex Zinenko     return isFreedBetween(value, root, nullptr,
26973c3dff1SAlex Zinenko                           [](Operation *op) { return op->getPrevNode(); });
27073c3dff1SAlex Zinenko   }
27173c3dff1SAlex Zinenko 
27273c3dff1SAlex Zinenko   /// Returns the list of operations that may be deleting the given value on
27373c3dff1SAlex Zinenko   /// any of the control flow paths between the "form" and the "to" block. The
27473c3dff1SAlex Zinenko   /// operations from any block visited on any control flow path are
27573c3dff1SAlex Zinenko   /// consdiered. The "from" block is considered if there is a control flow
27673c3dff1SAlex Zinenko   /// cycle going through it, i.e., if there is a possibility that all
27773c3dff1SAlex Zinenko   /// operations in this block are visited or if the `alwaysIncludeFrom` flag is
27873c3dff1SAlex Zinenko   /// set. The "to" block is considered only if there is a control flow cycle
27973c3dff1SAlex Zinenko   /// going through it.
28073c3dff1SAlex Zinenko   PotentialDeleters isMaybeFreedOnPaths(Block *from, Block *to, Value value,
28173c3dff1SAlex Zinenko                                         bool alwaysIncludeFrom) {
28273c3dff1SAlex Zinenko     // Find all blocks that lie on any path between "from" and "to", i.e., the
28373c3dff1SAlex Zinenko     // intersection of blocks reachable from "from" and blocks from which "to"
28473c3dff1SAlex Zinenko     // is rechable.
28573c3dff1SAlex Zinenko     const llvm::SmallPtrSet<Block *, 4> &sources = getReachableFrom(to);
28673c3dff1SAlex Zinenko     if (!sources.contains(from))
28773c3dff1SAlex Zinenko       return live();
28873c3dff1SAlex Zinenko 
28973c3dff1SAlex Zinenko     llvm::SmallPtrSet<Block *, 4> reachable(getReachable(from));
29073c3dff1SAlex Zinenko     llvm::set_intersect(reachable, sources);
29173c3dff1SAlex Zinenko 
29273c3dff1SAlex Zinenko     // If requested, include the "from" block that may not be present in the set
29373c3dff1SAlex Zinenko     // of visited blocks when there is no cycle going through it.
29473c3dff1SAlex Zinenko     if (alwaysIncludeFrom)
29573c3dff1SAlex Zinenko       reachable.insert(from);
29673c3dff1SAlex Zinenko 
29773c3dff1SAlex Zinenko     // Join potential deleters from all blocks as we don't know here which of
29873c3dff1SAlex Zinenko     // the paths through the control flow is taken.
29973c3dff1SAlex Zinenko     PotentialDeleters potentialDeleters = live();
30073c3dff1SAlex Zinenko     for (Block *block : reachable) {
30173c3dff1SAlex Zinenko       for (Operation &op : *block) {
30273c3dff1SAlex Zinenko         if (freedBy[value].count(&op))
30373c3dff1SAlex Zinenko           potentialDeleters |= maybeFreed(&op);
30473c3dff1SAlex Zinenko       }
30573c3dff1SAlex Zinenko     }
30673c3dff1SAlex Zinenko     return potentialDeleters;
30773c3dff1SAlex Zinenko   }
30873c3dff1SAlex Zinenko 
30973c3dff1SAlex Zinenko   /// Popualtes `reachable` with the set of blocks that are rechable from the
31073c3dff1SAlex Zinenko   /// given block. A block is considered reachable from itself if there is a
31173c3dff1SAlex Zinenko   /// cycle in the control-flow graph that invovles the block.
31273c3dff1SAlex Zinenko   const llvm::SmallPtrSet<Block *, 4> &getReachable(Block *block) {
31373c3dff1SAlex Zinenko     return getReachableImpl(
31473c3dff1SAlex Zinenko         block, [](Block *b) { return b->getSuccessors(); }, reachableCache);
31573c3dff1SAlex Zinenko   }
31673c3dff1SAlex Zinenko 
31773c3dff1SAlex Zinenko   /// Populates `sources` with the set of blocks from which the given block is
31873c3dff1SAlex Zinenko   /// reachable.
31973c3dff1SAlex Zinenko   const llvm::SmallPtrSet<Block *, 4> &getReachableFrom(Block *block) {
32073c3dff1SAlex Zinenko     return getReachableImpl(
32173c3dff1SAlex Zinenko         block, [](Block *b) { return b->getPredecessors(); },
32273c3dff1SAlex Zinenko         reachableFromCache);
32373c3dff1SAlex Zinenko   }
32473c3dff1SAlex Zinenko 
32573c3dff1SAlex Zinenko   /// Returns true of `instances` contains an effect of `EffectTy` on `value`.
32673c3dff1SAlex Zinenko   template <typename EffectTy>
32773c3dff1SAlex Zinenko   static bool hasEffect(ArrayRef<MemoryEffects::EffectInstance> instances,
32873c3dff1SAlex Zinenko                         Value value) {
32973c3dff1SAlex Zinenko     return llvm::any_of(instances,
33073c3dff1SAlex Zinenko                         [&](const MemoryEffects::EffectInstance &instance) {
33173c3dff1SAlex Zinenko                           return instance.getValue() == value &&
33273c3dff1SAlex Zinenko                                  isa<EffectTy>(instance.getEffect());
33373c3dff1SAlex Zinenko                         });
33473c3dff1SAlex Zinenko   }
33573c3dff1SAlex Zinenko 
33673c3dff1SAlex Zinenko   /// Records the values that are being freed by an operation or any of its
33773c3dff1SAlex Zinenko   /// children in `freedBy`.
33873c3dff1SAlex Zinenko   void collectFreedValues(Operation *root) {
33973c3dff1SAlex Zinenko     SmallVector<MemoryEffects::EffectInstance> instances;
34073c3dff1SAlex Zinenko     root->walk([&](Operation *child) {
341*938cdd60SOleksandr "Alex" Zinenko       if (isa<transform::PatternDescriptorOpInterface>(child))
342*938cdd60SOleksandr "Alex" Zinenko         return;
34373c3dff1SAlex Zinenko       // TODO: extend this to conservatively handle operations with undeclared
34473c3dff1SAlex Zinenko       // side effects as maybe freeing the operands.
34573c3dff1SAlex Zinenko       auto iface = cast<MemoryEffectOpInterface>(child);
34673c3dff1SAlex Zinenko       instances.clear();
34773c3dff1SAlex Zinenko       iface.getEffectsOnResource(transform::TransformMappingResource::get(),
34873c3dff1SAlex Zinenko                                  instances);
34973c3dff1SAlex Zinenko       for (Value operand : child->getOperands()) {
35073c3dff1SAlex Zinenko         if (hasEffect<MemoryEffects::Free>(instances, operand)) {
35173c3dff1SAlex Zinenko           // All parents of the operation that frees a value should be
35273c3dff1SAlex Zinenko           // considered as potentially freeing the value as well.
35373c3dff1SAlex Zinenko           //
35473c3dff1SAlex Zinenko           // TODO: differentiate between must-free/may-free as well as between
35573c3dff1SAlex Zinenko           // this op having the effect and children having the effect. This may
35673c3dff1SAlex Zinenko           // require some analysis of all control flow paths through the nested
35773c3dff1SAlex Zinenko           // regions as well as a mechanism to separate proper side effects from
35873c3dff1SAlex Zinenko           // those obtained by nesting.
35973c3dff1SAlex Zinenko           Operation *parent = child;
36073c3dff1SAlex Zinenko           do {
36173c3dff1SAlex Zinenko             freedBy[operand].insert(parent);
36273c3dff1SAlex Zinenko             if (parent == root)
36373c3dff1SAlex Zinenko               break;
36473c3dff1SAlex Zinenko             parent = parent->getParentOp();
36573c3dff1SAlex Zinenko           } while (true);
36673c3dff1SAlex Zinenko         }
36773c3dff1SAlex Zinenko       }
36873c3dff1SAlex Zinenko     });
36973c3dff1SAlex Zinenko   }
37073c3dff1SAlex Zinenko 
37173c3dff1SAlex Zinenko   /// The mapping from a value to operations that have a Free memory effect on
37273c3dff1SAlex Zinenko   /// the TransformMappingResource and associated with this value, or to
37373c3dff1SAlex Zinenko   /// Transform operations transitively containing such operations.
37473c3dff1SAlex Zinenko   DenseMap<Value, llvm::SmallPtrSet<Operation *, 2>> freedBy;
37573c3dff1SAlex Zinenko 
37673c3dff1SAlex Zinenko   /// Caches for sets of reachable blocks.
37773c3dff1SAlex Zinenko   DenseMap<Block *, llvm::SmallPtrSet<Block *, 4>> reachableCache;
37873c3dff1SAlex Zinenko   DenseMap<Block *, llvm::SmallPtrSet<Block *, 4>> reachableFromCache;
37973c3dff1SAlex Zinenko };
38073c3dff1SAlex Zinenko 
38173c3dff1SAlex Zinenko //// A simple pass that warns about any use of a value by a transform operation
38273c3dff1SAlex Zinenko // that may be using the value after it has been freed.
383b512b1c8SMehdi Amini class CheckUsesPass : public transform::impl::CheckUsesPassBase<CheckUsesPass> {
38473c3dff1SAlex Zinenko public:
38573c3dff1SAlex Zinenko   void runOnOperation() override {
38673c3dff1SAlex Zinenko     auto &analysis = getAnalysis<TransformOpMemFreeAnalysis>();
38773c3dff1SAlex Zinenko 
38873c3dff1SAlex Zinenko     getOperation()->walk([&](Operation *child) {
38973c3dff1SAlex Zinenko       for (OpOperand &operand : child->getOpOperands()) {
39073c3dff1SAlex Zinenko         TransformOpMemFreeAnalysis::PotentialDeleters deleters =
39173c3dff1SAlex Zinenko             analysis.isUseLive(operand);
39273c3dff1SAlex Zinenko         if (!deleters)
39373c3dff1SAlex Zinenko           continue;
39473c3dff1SAlex Zinenko 
39573c3dff1SAlex Zinenko         InFlightDiagnostic diag = child->emitWarning()
39673c3dff1SAlex Zinenko                                   << "operand #" << operand.getOperandNumber()
39773c3dff1SAlex Zinenko                                   << " may be used after free";
39873c3dff1SAlex Zinenko         diag.attachNote(operand.get().getLoc()) << "allocated here";
39973c3dff1SAlex Zinenko         for (Operation *d : deleters.getOps()) {
40073c3dff1SAlex Zinenko           diag.attachNote(d->getLoc()) << "freed here";
40173c3dff1SAlex Zinenko         }
40273c3dff1SAlex Zinenko       }
40373c3dff1SAlex Zinenko     });
40473c3dff1SAlex Zinenko   }
40573c3dff1SAlex Zinenko };
40673c3dff1SAlex Zinenko 
40773c3dff1SAlex Zinenko } // namespace
408039b969bSMichele Scuttari 
409