[Mlir-commits] [mlir] [MLIR] Refactor DCE helper to expose worklist entry-point, and use this helper in CSE (PR #195636)
Mehdi Amini
llvmlistbot at llvm.org
Mon May 4 04:33:25 PDT 2026
https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/195636
Replace CSE's private erase list with the shared trivial-DCE worklist helper, while preserving CSE's scoped known-values invariants.
CSE stores operations in an llvm::ScopedHashTable keyed by OperationEquivalence. If the new DCE helper erases a producer while the current region scope is still active, the scoped table can later try to pop an entry for an operation whose storage has already been erased. In the single-block path, run the helper only after the known-values scope has unwound so erased operations are no longer table entries.
Make the worklist helper explicit about its contract and let callers observe erasures with a pre-erase callback. The worklist entry-point now returns the number of erased operations directly, which callers can use for both change detection and statistics without an out-parameter.
CSE uses the pre-erase hook to invalidate cached dominance for regions owned by erased operations before the IR is destroyed. The pass intentionally preserves only DominanceInfo for now; a TODO documents that preserving PostDominanceInfo as well would require threading that analysis into the CSE driver and invalidating its cached regions in the same way.
Also make the helper and CSE traversal report only the changes made by the current invocation. The helper records newly enqueued defining ops in the visited set, and CSE separates duplicate eliminations from dead-code cleanup so statistics and transform fixpoint loops do not depend on stale state.
Assisted-by: Codex
>From af1dc424fb8a1ea7eb2295ab49eb92234c68643b Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Mon, 4 May 2026 03:03:24 -0700
Subject: [PATCH] [MLIR] Refactor DCE helper to expose worklist entry-point,
and use this helper in CSE
Replace CSE's private erase list with the shared trivial-DCE worklist helper, while preserving CSE's scoped known-values invariants.
CSE stores operations in an llvm::ScopedHashTable keyed by OperationEquivalence. If the new DCE helper erases a producer while the current region scope is still active, the scoped table can later try to pop an entry for an operation whose storage has already been erased. In the single-block path, run the helper only after the known-values scope has unwound so erased operations are no longer table entries.
Make the worklist helper explicit about its contract and let callers observe erasures with a pre-erase callback. The worklist entry-point now returns the number of erased operations directly, which callers can use for both change detection and statistics without an out-parameter.
CSE uses the pre-erase hook to invalidate cached dominance for regions owned by erased operations before the IR is destroyed. The pass intentionally preserves only DominanceInfo for now; a TODO documents that preserving PostDominanceInfo as well would require threading that analysis into the CSE driver and invalidating its cached regions in the same way.
Also make the helper and CSE traversal report only the changes made by the current invocation. The helper records newly enqueued defining ops in the visited set, and CSE separates duplicate eliminations from dead-code cleanup so statistics and transform fixpoint loops do not depend on stale state.
Update tests that run -cse to stop expecting now-dead producers, and add regressions for erasing a CSE duplicate after the known-values scope unwinds and for the transform apply-patterns CSE loop converging after the changed bit is reset.
Assisted-by: Codex
---
mlir/include/mlir/Transforms/RegionUtils.h | 20 +++
mlir/lib/Transforms/CSE.cpp | 9 +-
mlir/lib/Transforms/Utils/CSE.cpp | 146 +++++++++++-------
mlir/lib/Transforms/Utils/RegionUtils.cpp | 139 +++++++++--------
.../expand-then-convert-to-llvm.mlir | 7 -
mlir/test/Dialect/Tensor/bufferize.mlir | 3 -
.../Transform/test-pattern-application.mlir | 26 ++++
mlir/test/Transforms/cse.mlir | 15 ++
8 files changed, 235 insertions(+), 130 deletions(-)
diff --git a/mlir/include/mlir/Transforms/RegionUtils.h b/mlir/include/mlir/Transforms/RegionUtils.h
index ea0b9ba9614a4..a8bf8149e2e10 100644
--- a/mlir/include/mlir/Transforms/RegionUtils.h
+++ b/mlir/include/mlir/Transforms/RegionUtils.h
@@ -13,8 +13,11 @@
#include "mlir/IR/Value.h"
#include "mlir/IR/ValueRange.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SetVector.h"
+#include <cstdint>
+
namespace mlir {
class DominanceInfo;
class RewriterBase;
@@ -109,6 +112,23 @@ LogicalResult moveValueDefinitions(RewriterBase &rewriter, ValueRange values,
LogicalResult moveValueDefinitions(RewriterBase &rewriter, ValueRange values,
Operation *insertionPoint);
+/// Remove trivially dead operations starting with the provided worklist.
+///
+/// The provided \p worklist must contain only operations directly in \p region
+/// that are already known to be trivially dead, and \p visited must contain
+/// every operation in \p worklist. The helper updates \p visited as it enqueues
+/// newly dead defining ops. Operand-defining ops are re-evaluated after each
+/// erasure, so chains of dead ops are eliminated in a single pass. \p preErase
+/// is called immediately before each worklist operation is erased. Returns the
+/// number of erased worklist operations.
+int64_t eliminateTriviallyDeadOps(RewriterBase &rewriter, Region ®ion,
+ SmallVector<Operation *> &worklist,
+ DenseSet<Operation *> &visited,
+ function_ref<void(Operation *)> preErase);
+int64_t eliminateTriviallyDeadOps(RewriterBase &rewriter, Region ®ion,
+ SmallVector<Operation *> &worklist,
+ DenseSet<Operation *> &visited);
+
/// Remove trivially dead operations from \p region. An operation is trivially
/// dead when it has no users and is side-effect-free. Operand-defining ops are
/// re-evaluated after each erasure, so chains of dead ops are eliminated in a
diff --git a/mlir/lib/Transforms/CSE.cpp b/mlir/lib/Transforms/CSE.cpp
index f7afa03e2f02b..4ad5225d1ea63 100644
--- a/mlir/lib/Transforms/CSE.cpp
+++ b/mlir/lib/Transforms/CSE.cpp
@@ -51,7 +51,10 @@ void CSE::runOnOperation() {
return markAllAnalysesPreserved();
// We only delete redundant operations without moving any operation to a
- // different block, so the dominance tree structure remains unchanged and
- // DominanceInfo/PostDominanceInfo can be safely preserved.
- markAnalysesPreserved<DominanceInfo, PostDominanceInfo>();
+ // different block, so the dominance tree structure remains unchanged. The
+ // CSE driver invalidates cached dominance for regions owned by erased ops.
+ // TODO: Preserve PostDominanceInfo as well by threading the analysis into
+ // the CSE driver and invalidating cached post-dominance for regions owned by
+ // erased ops, matching the DominanceInfo handling above.
+ markAnalysesPreserved<DominanceInfo>();
}
diff --git a/mlir/lib/Transforms/Utils/CSE.cpp b/mlir/lib/Transforms/Utils/CSE.cpp
index 90444e6201891..ca3466bcf6e47 100644
--- a/mlir/lib/Transforms/Utils/CSE.cpp
+++ b/mlir/lib/Transforms/Utils/CSE.cpp
@@ -16,6 +16,7 @@
#include "mlir/IR/Dominance.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/Interfaces/SideEffectInterfaces.h"
+#include "mlir/Transforms/RegionUtils.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/Support/Allocator.h"
@@ -97,16 +98,19 @@ class CSEDriver {
/// Attempt to eliminate a redundant operation. Returns success if the
/// operation was marked for removal, failure otherwise.
- LogicalResult simplifyOperation(ScopedMapTy &knownValues, Operation *op,
- bool hasSSADominance);
- void simplifyBlock(ScopedMapTy &knownValues, Block *bb, bool hasSSADominance);
- void simplifyRegion(ScopedMapTy &knownValues, Region ®ion);
-
- /// Erase all operations queued for deletion by the simplification routines.
- void eraseDeadOps(bool *changed);
+ LogicalResult
+ simplifyOperation(ScopedMapTy &knownValues, Operation *op,
+ function_ref<void(Operation *)> addCSEToWorklist,
+ bool hasSSADominance);
+ bool simplifyBlock(ScopedMapTy &knownValues, Block *bb, bool hasSSADominance,
+ function_ref<void(Operation *)> addToWorklist,
+ function_ref<void(Operation *)> addCSEToWorklist);
+ bool simplifyRegion(ScopedMapTy &knownValues, Region ®ion);
void replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op,
- Operation *existing, bool hasSSADominance);
+ Operation *existing,
+ function_ref<void(Operation *)> addCSEToWorklist,
+ bool hasSSADominance);
/// Check if there is side-effecting operations other than the given effect
/// between the two operations.
@@ -115,8 +119,6 @@ class CSEDriver {
/// A rewriter for modifying the IR.
RewriterBase &rewriter;
- /// Operations marked as dead and to be erased.
- std::vector<Operation *> opsToErase;
DominanceInfo *domInfo = nullptr;
MemEffectsCache memEffectsCache;
@@ -126,9 +128,9 @@ class CSEDriver {
};
} // namespace
-void CSEDriver::replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op,
- Operation *existing,
- bool hasSSADominance) {
+void CSEDriver::replaceUsesAndDelete(
+ ScopedMapTy &knownValues, Operation *op, Operation *existing,
+ function_ref<void(Operation *)> addCSEToWorklist, bool hasSSADominance) {
// If we find one then replace all uses of the current operation with the
// existing one and mark it for deletion. We can only replace an operand in
// an operation if it has not been visited yet.
@@ -137,7 +139,7 @@ void CSEDriver::replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op,
// visited any use of the current operation.
// Replace all uses, but do not remove the operation yet.
rewriter.replaceAllOpUsesWith(op, existing->getResults());
- opsToErase.push_back(op);
+ addCSEToWorklist(op);
} else {
// When the region does not have SSA dominance, we need to check if we
// have visited a use before replacing any use.
@@ -157,7 +159,7 @@ void CSEDriver::replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op,
// There may be some remaining uses of the operation.
if (op->use_empty())
- opsToErase.push_back(op);
+ addCSEToWorklist(op);
}
// If the existing operation has an unknown location and the current
@@ -246,9 +248,10 @@ bool CSEDriver::hasOtherSideEffectingOpInBetween(Operation *fromOp,
}
/// Attempt to eliminate a redundant operation.
-LogicalResult CSEDriver::simplifyOperation(ScopedMapTy &knownValues,
- Operation *op,
- bool hasSSADominance) {
+LogicalResult
+CSEDriver::simplifyOperation(ScopedMapTy &knownValues, Operation *op,
+ function_ref<void(Operation *)> addCSEToWorklist,
+ bool hasSSADominance) {
// Don't simplify terminator operations.
if (op->hasTrait<OpTrait::IsTerminator>())
return failure();
@@ -275,7 +278,8 @@ LogicalResult CSEDriver::simplifyOperation(ScopedMapTy &knownValues,
// The operation that can be deleted has been reach with no
// side-effecting operations in between the existing operation and
// this one so we can remove the duplicate.
- replaceUsesAndDelete(knownValues, op, existing, hasSSADominance);
+ replaceUsesAndDelete(knownValues, op, existing, addCSEToWorklist,
+ hasSSADominance);
return success();
}
}
@@ -285,7 +289,8 @@ LogicalResult CSEDriver::simplifyOperation(ScopedMapTy &knownValues,
// Look for an existing definition for the operation.
if (auto *existing = knownValues.lookup(op)) {
- replaceUsesAndDelete(knownValues, op, existing, hasSSADominance);
+ replaceUsesAndDelete(knownValues, op, existing, addCSEToWorklist,
+ hasSSADominance);
return success();
}
@@ -294,15 +299,17 @@ LogicalResult CSEDriver::simplifyOperation(ScopedMapTy &knownValues,
return failure();
}
-void CSEDriver::simplifyBlock(ScopedMapTy &knownValues, Block *bb,
- bool hasSSADominance) {
+bool CSEDriver::simplifyBlock(
+ ScopedMapTy &knownValues, Block *bb, bool hasSSADominance,
+ function_ref<void(Operation *)> addToWorklist,
+ function_ref<void(Operation *)> addCSEToWorklist) {
+ bool changed = false;
for (auto &op : llvm::make_early_inc_range(*bb)) {
// If the operation is already trivially dead just add it to the erase list.
// This also avoids calling `simplifyRegion` on dead region ops
// unnecessarily.
if (isOpTriviallyDead(&op)) {
- opsToErase.push_back(&op);
- ++numDCE;
+ addToWorklist(&op);
continue;
}
@@ -314,41 +321,78 @@ void CSEDriver::simplifyBlock(ScopedMapTy &knownValues, Block *bb,
if (op.mightHaveTrait<OpTrait::IsIsolatedFromAbove>()) {
ScopedMapTy nestedKnownValues;
for (auto ®ion : op.getRegions())
- simplifyRegion(nestedKnownValues, region);
+ changed |= simplifyRegion(nestedKnownValues, region);
} else {
// Otherwise, process nested regions normally.
for (auto ®ion : op.getRegions())
- simplifyRegion(knownValues, region);
+ changed |= simplifyRegion(knownValues, region);
}
}
- // If the operation is simplified, we don't process any held regions.
- if (succeeded(simplifyOperation(knownValues, &op, hasSSADominance)))
+ if (succeeded(simplifyOperation(knownValues, &op, addCSEToWorklist,
+ hasSSADominance)))
continue;
}
// Clear the MemoryEffects cache since its usage is by block only.
memEffectsCache.clear();
+ return changed;
}
-void CSEDriver::simplifyRegion(ScopedMapTy &knownValues, Region ®ion) {
+bool CSEDriver::simplifyRegion(ScopedMapTy &knownValues, Region ®ion) {
// If the region is empty there is nothing to do.
if (region.empty())
- return;
+ return false;
bool hasSSADominance = domInfo->hasSSADominance(®ion);
+ bool changed = false;
+
+ SmallVector<Operation *> worklist;
+ DenseSet<Operation *> visited;
+ DenseSet<Operation *> cseErasedOps;
+ int64_t cseErasedCount = 0;
+ auto addToWorklist = [&](Operation *op) {
+ if (visited.insert(op).second)
+ worklist.push_back(op);
+ };
+ auto addCSEToWorklist = [&](Operation *op) {
+ if (visited.insert(op).second) {
+ cseErasedOps.insert(op);
+ worklist.push_back(op);
+ }
+ };
+ auto preErase = [&](Operation *op) {
+ op->walk([&](Operation *erasedOp) {
+ for (Region ®ion : erasedOp->getRegions())
+ domInfo->invalidate(®ion);
+ });
+ if (cseErasedOps.contains(op))
+ ++cseErasedCount;
+ };
+ auto eraseWorklist = [&]() {
+ int64_t erasedCount = eliminateTriviallyDeadOps(rewriter, region, worklist,
+ visited, preErase);
+ assert(erasedCount >= cseErasedCount &&
+ "CSE erasure count cannot exceed total erasures");
+ numDCE += erasedCount - cseErasedCount;
+ return erasedCount != 0;
+ };
// If the region only contains one block, then simplify it directly.
if (region.hasOneBlock()) {
- ScopedMapTy::ScopeTy scope(knownValues);
- simplifyBlock(knownValues, ®ion.front(), hasSSADominance);
- return;
+ {
+ ScopedMapTy::ScopeTy scope(knownValues);
+ changed |= simplifyBlock(knownValues, ®ion.front(), hasSSADominance,
+ addToWorklist, addCSEToWorklist);
+ }
+ changed |= eraseWorklist();
+ return changed;
}
// If the region does not have dominanceInfo, then skip it.
// TODO: Regions without SSA dominance should define a different
// traversal order which is appropriate and can be used here.
if (!hasSSADominance)
- return;
+ return false;
// Note, deque is being used here because there was significant performance
// gains over vector when the container becomes very large due to the
@@ -368,8 +412,9 @@ void CSEDriver::simplifyRegion(ScopedMapTy &knownValues, Region ®ion) {
// Check to see if we need to process this node.
if (!currentNode->processed) {
currentNode->processed = true;
- simplifyBlock(knownValues, currentNode->node->getBlock(),
- hasSSADominance);
+ changed |=
+ simplifyBlock(knownValues, currentNode->node->getBlock(),
+ hasSSADominance, addToWorklist, addCSEToWorklist);
}
// Otherwise, check to see if we need to process a child node.
@@ -383,36 +428,25 @@ void CSEDriver::simplifyRegion(ScopedMapTy &knownValues, Region ®ion) {
stack.pop_back();
}
}
-}
-
-void CSEDriver::eraseDeadOps(bool *changed) {
- // Erase any operations that were marked as dead during simplification, and
- // remove their associated dominator trees.
- for (auto *op : opsToErase) {
- for (Region ®ion : op->getRegions())
- domInfo->invalidate(®ion);
- rewriter.eraseOp(op);
- }
- if (changed)
- *changed = !opsToErase.empty();
- opsToErase.clear();
-
- // Note: CSE does currently not remove ops with regions, so DominanceInfo
- // does not have to be invalidated.
+ changed |= eraseWorklist();
+ return changed;
}
void CSEDriver::simplify(Operation *op, bool *changed) {
// Simplify all regions.
ScopedMapTy knownValues;
+ bool anyChanged = false;
for (auto ®ion : op->getRegions())
- simplifyRegion(knownValues, region);
- eraseDeadOps(changed);
+ anyChanged |= simplifyRegion(knownValues, region);
+ if (changed)
+ *changed = anyChanged;
}
void CSEDriver::simplify(Region ®ion, bool *changed) {
ScopedMapTy knownValues;
- simplifyRegion(knownValues, region);
- eraseDeadOps(changed);
+ bool anyChanged = simplifyRegion(knownValues, region);
+ if (changed)
+ *changed = anyChanged;
}
void mlir::eliminateCommonSubExpressions(RewriterBase &rewriter,
diff --git a/mlir/lib/Transforms/Utils/RegionUtils.cpp b/mlir/lib/Transforms/Utils/RegionUtils.cpp
index cee48b0b6b126..f7f3825fbdabe 100644
--- a/mlir/lib/Transforms/Utils/RegionUtils.cpp
+++ b/mlir/lib/Transforms/Utils/RegionUtils.cpp
@@ -510,6 +510,74 @@ LogicalResult mlir::runRegionDCE(RewriterBase &rewriter,
return deleteDeadness(rewriter, regions, liveMap);
}
+int64_t mlir::eliminateTriviallyDeadOps(
+ RewriterBase &rewriter, Region ®ion, SmallVector<Operation *> &worklist,
+ DenseSet<Operation *> &visited, function_ref<void(Operation *)> preErase) {
+ LDBG(2) << "Initial worklist size: " << worklist.size();
+ int64_t numErased = 0;
+ while (!worklist.empty()) {
+ Operation *op = worklist.pop_back_val();
+ LDBG(2) << "Popped operation from worklist: "
+ << OpWithFlags(op, OpPrintingFlags().skipRegions());
+ /// Erase each operand to drop its use count before checking its defining
+ /// op: by the time we call isOpTriviallyDead on defOp, the
+ /// about-to-be-erased `op` is no longer counted as a user. Only
+ /// actually-dead ops enter the worklist.
+ ///
+ /// Walk nested operations as well because erasing `op` also implicitly
+ /// erases every operation nested under it and therefore drops their operand
+ /// uses.
+ op->walk([&](Operation *erasedOp) {
+ LDBG(3) << "Processing operands of operation erased: "
+ << OpWithFlags(erasedOp, OpPrintingFlags().skipRegions());
+ for (OpOperand &opOperand : erasedOp->getOpOperands()) {
+ Operation *defOp = opOperand.get().getDefiningOp();
+ if (!defOp) {
+ LDBG(4) << "Skipping operand #" << opOperand.getOperandNumber()
+ << ": value has no defining operation";
+ continue;
+ }
+ if (defOp->getParentRegion() != ®ion) {
+ LDBG(4) << "Skipping operand #" << opOperand.getOperandNumber()
+ << ": defining operation is outside the current region";
+ continue;
+ }
+ if (visited.count(defOp)) {
+ LDBG(4) << "Skipping operand #" << opOperand.getOperandNumber()
+ << ": defining operation was already visited";
+ continue;
+ }
+ LDBG(4) << "Dropping operand #" << opOperand.getOperandNumber()
+ << " from defining operation: "
+ << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
+ opOperand.drop();
+ if (isOpTriviallyDead(defOp)) {
+ LDBG(2) << "Enqueued newly trivially dead defining operation: "
+ << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
+ visited.insert(defOp);
+ worklist.push_back(defOp);
+ } else {
+ LDBG(4) << "Defining operation is still not trivially dead: "
+ << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
+ }
+ }
+ });
+ LDBG() << "Erasing trivially dead worklist operation: "
+ << OpWithFlags(op, OpPrintingFlags().skipRegions());
+ preErase(op);
+ rewriter.eraseOp(op);
+ ++numErased;
+ }
+ return numErased;
+}
+
+int64_t mlir::eliminateTriviallyDeadOps(RewriterBase &rewriter, Region ®ion,
+ SmallVector<Operation *> &worklist,
+ DenseSet<Operation *> &visited) {
+ return eliminateTriviallyDeadOps(rewriter, region, worklist, visited,
+ [](Operation *) {});
+}
+
bool mlir::eliminateTriviallyDeadOps(RewriterBase &rewriter, Region ®ion,
bool includeNestedRegions) {
LDBG() << "Starting eliminateTriviallyDeadOps with "
@@ -520,9 +588,8 @@ bool mlir::eliminateTriviallyDeadOps(RewriterBase &rewriter, Region ®ion,
<< OpWithFlags(parentOp, OpPrintingFlags().skipRegions());
bool changed = false;
- unsigned erasedOps = 0;
- unsigned seededOps = 0;
- unsigned enqueuedDefs = 0;
+ int64_t erasedOps = 0;
+ int64_t seededOps = 0;
// Step 1: walk each op in reverse program order. If the op is already
// trivially dead, erase it outright — there's no point recursing into
@@ -579,71 +646,21 @@ bool mlir::eliminateTriviallyDeadOps(RewriterBase &rewriter, Region ®ion,
LDBG(2) << "Stage 2: Seeding trivially dead operation worklist";
for (Operation &op : region.getOps()) {
- if (isOpTriviallyDead(&op) && visited.insert(&op).second) {
+ if (isOpTriviallyDead(&op)) {
LDBG(2) << "Seeded worklist with operation: "
<< OpWithFlags(&op, OpPrintingFlags().skipRegions());
+ visited.insert(&op);
worklist.push_back(&op);
- changed = true;
++seededOps;
}
}
- LDBG(2) << "Initial worklist size: " << worklist.size();
-
- while (!worklist.empty()) {
- Operation *op = worklist.pop_back_val();
- LDBG(2) << "Popped operation from worklist: "
- << OpWithFlags(op, OpPrintingFlags().skipRegions());
- /// Erase each operand to drop its use count before checking its defining
- /// op: by the time we call isOpTriviallyDead on defOp, the
- /// about-to-be-erased `op` is no longer counted as a user. Only
- /// actually-dead ops enter the worklist.
- ///
- /// Walk nested operations as well because erasing `op` also implicitly
- /// erases every operation nested under it and therefore drops their operand
- /// uses.
- op->walk([&](Operation *erasedOp) {
- LDBG(3) << "Processing operands of operation erased: "
- << OpWithFlags(erasedOp, OpPrintingFlags().skipRegions());
- for (OpOperand &opOperand : erasedOp->getOpOperands()) {
- Operation *defOp = opOperand.get().getDefiningOp();
- if (!defOp) {
- LDBG(4) << "Skipping operand #" << opOperand.getOperandNumber()
- << ": value has no defining operation";
- continue;
- }
- if (defOp->getParentRegion() != ®ion) {
- LDBG(4) << "Skipping operand #" << opOperand.getOperandNumber()
- << ": defining operation is outside the current region";
- continue;
- }
- if (visited.count(defOp)) {
- LDBG(4) << "Skipping operand #" << opOperand.getOperandNumber()
- << ": defining operation was already visited";
- continue;
- }
- LDBG(4) << "Dropping operand #" << opOperand.getOperandNumber()
- << " from defining operation: "
- << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
- opOperand.drop();
- if (isOpTriviallyDead(defOp)) {
- LDBG(2) << "Enqueued newly trivially dead defining operation: "
- << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
- worklist.push_back(defOp);
- ++enqueuedDefs;
- } else {
- LDBG(4) << "Defining operation is still not trivially dead: "
- << OpWithFlags(defOp, OpPrintingFlags().skipRegions());
- }
- }
- });
- LDBG() << "Erasing trivially dead worklist operation: "
- << OpWithFlags(op, OpPrintingFlags().skipRegions());
- rewriter.eraseOp(op);
- ++erasedOps;
- }
+ int64_t worklistErasedOps =
+ eliminateTriviallyDeadOps(rewriter, region, worklist, visited);
+ erasedOps += worklistErasedOps;
+ changed |= worklistErasedOps != 0;
LDBG() << "Finished eliminateTriviallyDeadOps, erased " << erasedOps
- << " operations, seeded " << seededOps << " operations, enqueued "
- << enqueuedDefs << " defining operations, changed=" << changed;
+ << " operations, seeded " << seededOps
+ << " operations, changed=" << changed;
return changed;
}
diff --git a/mlir/test/Conversion/MemRefToLLVM/expand-then-convert-to-llvm.mlir b/mlir/test/Conversion/MemRefToLLVM/expand-then-convert-to-llvm.mlir
index c2c93525b6509..2a2ffbfa43dd1 100644
--- a/mlir/test/Conversion/MemRefToLLVM/expand-then-convert-to-llvm.mlir
+++ b/mlir/test/Conversion/MemRefToLLVM/expand-then-convert-to-llvm.mlir
@@ -599,9 +599,6 @@ func.func @expand_shape_dynamic(%arg0 : memref<1x?xf32>, %sz0: index) -> memref<
// CHECK: %[[UNREALIZED_CONVERSION_CAST_1:.*]] = builtin.unrealized_conversion_cast %[[ARG0]] : memref<1x?xf32> to !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
// CHECK: %[[EXTRACTVALUE_0:.*]] = llvm.extractvalue %[[UNREALIZED_CONVERSION_CAST_1]][0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
// CHECK: %[[EXTRACTVALUE_1:.*]] = llvm.extractvalue %[[UNREALIZED_CONVERSION_CAST_1]][1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
-// CHECK: %[[MLIR_0:.*]] = llvm.mlir.poison : !llvm.struct<(ptr, ptr, i64)>
-// CHECK: %[[INSERTVALUE_0:.*]] = llvm.insertvalue %[[EXTRACTVALUE_0]], %[[MLIR_0]][0] : !llvm.struct<(ptr, ptr, i64)>
-// CHECK: %[[INSERTVALUE_1:.*]] = llvm.insertvalue %[[EXTRACTVALUE_1]], %[[INSERTVALUE_0]][1] : !llvm.struct<(ptr, ptr, i64)>
// CHECK: %[[MLIR_1:.*]] = llvm.mlir.constant(0 : index) : i64
// CHECK: %[[EXTRACTVALUE_2:.*]] = llvm.extractvalue %[[UNREALIZED_CONVERSION_CAST_1]][4, 0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
// CHECK: %[[MLIR_2:.*]] = llvm.mlir.poison : !llvm.struct<(ptr, ptr, i64, array<3 x i64>, array<3 x i64>)>
@@ -637,10 +634,6 @@ func.func @expand_shape_dynamic_with_non_identity_layout(
// CHECK: %[[UNREALIZED_CONVERSION_CAST_1:.*]] = builtin.unrealized_conversion_cast %[[ARG0]] : memref<1x?xf32, strided<[?, ?], offset: ?>> to !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
// CHECK: %[[EXTRACTVALUE_0:.*]] = llvm.extractvalue %[[UNREALIZED_CONVERSION_CAST_1]][0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
// CHECK: %[[EXTRACTVALUE_1:.*]] = llvm.extractvalue %[[UNREALIZED_CONVERSION_CAST_1]][1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
-// CHECK: %[[MLIR_0:.*]] = llvm.mlir.poison : !llvm.struct<(ptr, ptr, i64)>
-// CHECK: %[[INSERTVALUE_0:.*]] = llvm.insertvalue %[[EXTRACTVALUE_0]], %[[MLIR_0]][0] : !llvm.struct<(ptr, ptr, i64)>
-// CHECK: %[[INSERTVALUE_1:.*]] = llvm.insertvalue %[[EXTRACTVALUE_1]], %[[INSERTVALUE_0]][1] : !llvm.struct<(ptr, ptr, i64)>
-// CHECK: %[[MLIR_1:.*]] = llvm.mlir.constant(0 : index) : i64
// CHECK: %[[EXTRACTVALUE_2:.*]] = llvm.extractvalue %[[UNREALIZED_CONVERSION_CAST_1]][2] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
// CHECK: %[[EXTRACTVALUE_3:.*]] = llvm.extractvalue %[[UNREALIZED_CONVERSION_CAST_1]][4, 0] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
// CHECK: %[[EXTRACTVALUE_4:.*]] = llvm.extractvalue %[[UNREALIZED_CONVERSION_CAST_1]][4, 1] : !llvm.struct<(ptr, ptr, i64, array<2 x i64>, array<2 x i64>)>
diff --git a/mlir/test/Dialect/Tensor/bufferize.mlir b/mlir/test/Dialect/Tensor/bufferize.mlir
index be8ce20d8f154..97a57811d3a88 100644
--- a/mlir/test/Dialect/Tensor/bufferize.mlir
+++ b/mlir/test/Dialect/Tensor/bufferize.mlir
@@ -568,9 +568,7 @@ func.func @tensor.pad(%t1: tensor<?x10xindex>, %l2: index, %h1: index,
%h2: index) -> tensor<?x?xindex> {
// CHECK-DAG: %[[m1:.*]] = bufferization.to_buffer %[[t1]] : tensor<?x10xindex> to memref<?x10xindex>
// CHECK-DAG: %[[c0:.*]] = arith.constant 0 : index
- // CHECK-DAG: %[[c1:.*]] = arith.constant 1 : index
// CHECK-DAG: %[[dim0:.*]] = memref.dim %[[m1]], %[[c0]]
- // CHECK-DAG: %[[dim1:.*]] = memref.dim %[[m1]], %[[c1]]
// CHECK-DAG: %[[size0:.*]] = affine.apply #[[$sum_map_1]]()[%[[dim0]], %[[h1]]]
// CHECK-DAG: %[[size1:.*]] = affine.apply #[[$sum_map_2]]()[%[[l2]], %[[h2]]]
// CHECK: %[[alloc:.*]] = memref.alloc(%[[size0]], %[[size1]]) {{.*}} : memref<?x?xindex>
@@ -786,4 +784,3 @@ func.func @parallel_insert_slice_copy_before_write(%in: tensor<4xf32>, %out: ten
}
// -----
-
diff --git a/mlir/test/Dialect/Transform/test-pattern-application.mlir b/mlir/test/Dialect/Transform/test-pattern-application.mlir
index f78b4b6f6798c..8c277be2d7315 100644
--- a/mlir/test/Dialect/Transform/test-pattern-application.mlir
+++ b/mlir/test/Dialect/Transform/test-pattern-application.mlir
@@ -278,6 +278,32 @@ module attributes {transform.with_named_sequence} {
// -----
+// CHECK-LABEL: func @non_isolated_apply_patterns_cse_changed_resets()
+// CHECK: "test.container"
+// CHECK: %[[C0:.*]] = arith.constant 0 : index
+// CHECK-NEXT: "test.use"(%[[C0]], %[[C0]]) : (index, index) -> ()
+func.func @non_isolated_apply_patterns_cse_changed_resets() {
+ "test.container"() ({
+ %c0 = arith.constant 0 : index
+ %c0_dup = arith.constant 0 : index
+ "test.use"(%c0, %c0_dup) : (index, index) -> ()
+ }) : () -> ()
+ return
+}
+
+module attributes {transform.with_named_sequence} {
+ transform.named_sequence @__transform_main(%arg1: !transform.any_op) {
+ %0 = transform.structured.match ops{["test.container"]} in %arg1
+ : (!transform.any_op) -> !transform.any_op
+ transform.apply_patterns to %0 {
+ transform.apply_patterns.canonicalization
+ } {apply_cse} : !transform.any_op
+ transform.yield
+ }
+}
+
+// -----
+
// CHECK-LABEL: func @full_dialect_conversion
// CHECK-NEXT: %[[m:.*]] = "test.new_op"() : () -> memref<5xf32>
// CHECK-NEXT: %[[cast:.*]] = builtin.unrealized_conversion_cast %0 : memref<5xf32> to tensor<5xf32>
diff --git a/mlir/test/Transforms/cse.mlir b/mlir/test/Transforms/cse.mlir
index 4b2907287d89e..8e7827ab2e68d 100644
--- a/mlir/test/Transforms/cse.mlir
+++ b/mlir/test/Transforms/cse.mlir
@@ -12,6 +12,21 @@ func.func @simple_constant() -> (i32, i32) {
// -----
+// CHECK-LABEL: @cse_erases_duplicate_producer_after_scope
+func.func @cse_erases_duplicate_producer_after_scope(%arg0: index)
+ -> (index, index) {
+ // CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index
+ %c1 = arith.constant 1 : index
+ %duplicate_c1 = arith.constant 1 : index
+ // CHECK-NEXT: %[[SUM:.*]] = arith.addi %arg0, %[[C1]] : index
+ %sum0 = arith.addi %arg0, %c1 : index
+ %sum1 = arith.addi %arg0, %duplicate_c1 : index
+ // CHECK-NEXT: return %[[SUM]], %[[SUM]] : index, index
+ return %sum0, %sum1 : index, index
+}
+
+// -----
+
// CHECK: #[[$MAP:.*]] = affine_map<(d0) -> (d0 mod 2)>
#map0 = affine_map<(d0) -> (d0 mod 2)>
More information about the Mlir-commits
mailing list