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.