[Mlir-commits] [mlir] [mlir][CSE] fix use-after-free (PR #203849)

Stefan Schuermans llvmlistbot at llvm.org
Mon Jun 15 04:10:42 PDT 2026


https://github.com/schuermans-roofline updated https://github.com/llvm/llvm-project/pull/203849

>From e3193b4867c822e2490b405e2ba26873f716a7b2 Mon Sep 17 00:00:00 2001
From: Stefan Schuermans <schuermans at roofline.ai>
Date: Mon, 15 Jun 2026 10:45:51 +0200
Subject: [PATCH] [mlir][CSE] fix use-after-free

There was a use-after free introduced accidentally by
e79e056ee982 [mlir][CSE] Remove the opsToErase container and immediately delete dead ops. (#203702)
This change fixes it by moving the copy of the location (the "use") to before the deletion of the op (the "free").

Locating the use-after-free has been assisted by Claude Code. The implementation of the fix has been done without it.

Signed-off-by: Stefan Schuermans <schuermans at roofline.ai>
---
 mlir/lib/Transforms/Utils/CSE.cpp | 58 +++++++++++++++----------------
 1 file changed, 29 insertions(+), 29 deletions(-)

diff --git a/mlir/lib/Transforms/Utils/CSE.cpp b/mlir/lib/Transforms/Utils/CSE.cpp
index eed77f20dd2da..5af76bc99e956 100644
--- a/mlir/lib/Transforms/Utils/CSE.cpp
+++ b/mlir/lib/Transforms/Utils/CSE.cpp
@@ -125,44 +125,44 @@ class CSEDriver {
 void CSEDriver::replaceUsesAndDelete(ScopedMapTy &knownValues, Operation *op,
                                      Operation *existing,
                                      bool hasSSADominance) {
+  // If the existing operation has an unknown location and the current
+  // operation doesn't, then set the existing op's location to that of the
+  // current op.
+  if (isa<UnknownLoc>(existing->getLoc()) && !isa<UnknownLoc>(op->getLoc()))
+    existing->setLoc(op->getLoc());
+
+  ++numCSE;
+
   // 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.
+  // existing one and delete it.
   if (hasSSADominance) {
     // If the region has SSA dominance, then we are guaranteed to have not
     // visited any use of the current operation.
     // Replace all uses, but do not remove the operation yet.
     rewriter.replaceAllOpUsesWith(op, existing->getResults());
     eraseDeadOp(op);
-  } else {
-    // When the region does not have SSA dominance, we need to check if we
-    // have visited a use before replacing any use.
-    auto wasVisited = [&](OpOperand &operand) {
-      return !knownValues.count(operand.getOwner());
-    };
-    if (auto *rewriteListener =
-            dyn_cast_if_present<RewriterBase::Listener>(rewriter.getListener()))
-      for (Value v : op->getResults())
-        if (all_of(v.getUses(), wasVisited))
-          rewriteListener->notifyOperationReplaced(op, existing);
-
-    // Replace all uses, but do not remove the operation yet. This does not
-    // notify the listener because the original op is not erased.
-    rewriter.replaceUsesWithIf(op->getResults(), existing->getResults(),
-                               wasVisited);
-
-    // There may be some remaining uses of the operation.
-    if (op->use_empty())
-      eraseDeadOp(op);
+    return;
   }
 
-  // If the existing operation has an unknown location and the current
-  // operation doesn't, then set the existing op's location to that of the
-  // current op.
-  if (isa<UnknownLoc>(existing->getLoc()) && !isa<UnknownLoc>(op->getLoc()))
-    existing->setLoc(op->getLoc());
-
-  ++numCSE;
+  // When the region does not have SSA dominance, we need to check if we
+  // have visited a use before replacing any use.
+  auto wasVisited = [&](OpOperand &operand) {
+    return !knownValues.count(operand.getOwner());
+  };
+  if (auto *rewriteListener =
+          dyn_cast_if_present<RewriterBase::Listener>(rewriter.getListener()))
+    for (Value v : op->getResults())
+      if (all_of(v.getUses(), wasVisited))
+        rewriteListener->notifyOperationReplaced(op, existing);
+
+  // Replace all uses, but do not remove the operation yet. This does not
+  // notify the listener because the original op is not erased.
+  rewriter.replaceUsesWithIf(op->getResults(), existing->getResults(),
+                             wasVisited);
+
+  // There may be some remaining uses of the operation.
+  if (op->use_empty())
+    eraseDeadOp(op);
 }
 
 bool CSEDriver::hasOtherSideEffectingOpInBetween(Operation *fromOp,



More information about the Mlir-commits mailing list