diff --git a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td --- a/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td +++ b/mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td @@ -356,7 +356,6 @@ let assemblyFormat = "$input attr-dict `:` type($input) `to` type($output)"; - let hasFolder = 1; let verifier = ?; let hasCanonicalizer = 1; } diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp --- a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp +++ b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp @@ -498,32 +498,45 @@ Value source = cloneOp.input(); - // Removes the clone operation and the corresponding dealloc and alloc - // operation (if any). - auto tryRemoveClone = [&](Operation *sourceOp, Operation *dealloc, - Operation *alloc) { - if (!sourceOp || !dealloc || !alloc || - alloc->getBlock() != dealloc->getBlock()) - return false; - rewriter.replaceOp(cloneOp, source); - rewriter.eraseOp(dealloc); - return true; - }; - - // Removes unnecessary clones that are derived from the result of the clone - // op. - Operation *deallocOp = findDealloc(cloneOp.output()); - Operation *sourceOp = source.getDefiningOp(); - if (tryRemoveClone(sourceOp, deallocOp, sourceOp)) - return success(); + // This only finds dealloc operations for the immediate value. It should + // also consider aliases. That would also make the safety check below + // redundant. + Operation *cloneDeallocOp = findDealloc(cloneOp.output()); + Operation *sourceDeallocOp = findDealloc(source); + + // If both are deallocated in the same block, their in-block lifetimes + // might not fully overlap, so we cannot decide which one to drop. + if (cloneDeallocOp && sourceDeallocOp && + cloneDeallocOp->getBlock() == sourceDeallocOp->getBlock()) + return failure(); - // Removes unnecessary clones that are derived from the source of the clone - // op. - deallocOp = findDealloc(source); - if (tryRemoveClone(sourceOp, deallocOp, cloneOp)) - return success(); + Block *currentBlock = cloneOp->getBlock(); + Operation *redundantDealloc = nullptr; + if (cloneDeallocOp && cloneDeallocOp->getBlock() == currentBlock) { + redundantDealloc = cloneDeallocOp; + } else if (sourceDeallocOp && sourceDeallocOp->getBlock() == currentBlock) { + redundantDealloc = sourceDeallocOp; + } - return failure(); + if (!redundantDealloc) + return failure(); + + // Safety check that there are no other deallocations inbetween + // cloneOp and redundantDealloc, as otherwise we might deallocate an alias + // of source before the uses of the clone. With alias informaiton, we could + // restrict this to only fail of the dealloc is on an alias of source. + for (Operation *pos = cloneOp->getNextNode(); pos != redundantDealloc; + pos = pos->getNextNode()) { + auto effectInterface = dyn_cast(pos); + if (!effectInterface) + continue; + if (effectInterface.hasEffect()) + return failure(); + } + + rewriter.replaceOp(cloneOp, source); + rewriter.eraseOp(redundantDealloc); + return success(); } }; @@ -534,10 +547,6 @@ results.insert(context); } -OpFoldResult CloneOp::fold(ArrayRef operands) { - return succeeded(foldMemRefCast(*this)) ? getResult() : Value(); -} - //===----------------------------------------------------------------------===// // DeallocOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Transforms/BufferDeallocation.cpp b/mlir/lib/Transforms/BufferDeallocation.cpp --- a/mlir/lib/Transforms/BufferDeallocation.cpp +++ b/mlir/lib/Transforms/BufferDeallocation.cpp @@ -151,8 +151,10 @@ } // Recurse into all distinct regions and check for explicit control-flow // loops. - for (Region ®ion : op->getRegions()) - recurse(region.front(), current); + for (Region ®ion : op->getRegions()) { + if (!region.empty()) + recurse(region.front(), current); + } } /// Recurses into explicit control-flow structures that are given by