[Mlir-commits] [flang] [mlir] [mlir][CSE] Add pruneDeadOps to CSE pass (PR #193778)

lonely eagle llvmlistbot at llvm.org
Sat May 2 20:36:37 PDT 2026


================
@@ -294,15 +297,38 @@ LogicalResult CSEDriver::simplifyOperation(ScopedMapTy &knownValues,
   return failure();
 }
 
+void CSEDriver::pruneDeadOps(Operation *root, ScopedMapTy &knownValues) {
+  // We use `SetVector` to prevent already inserted ops from being added to the
+  // `worklist` repeatedly, avoiding secondary access to erased operations.
+  llvm::SetVector<Operation *> worklist;
----------------
linuxlonelyeagle wrote:

> Why do you think it makes sense?

Consistent with the PR description and the provided test cases, this implementation further optimization for CSE. There's no need to run trivial-dce or canonicalize beforehand. In my view, we only need to visit the ops once during CSE and can handle the DCE work along the way.

 > Do you have a case not covered by applying a trivial DCE before CSE? That is one example for my previous question: is there a case where a DCE opportunity only presents itself after CSE merged some operations?

Technically speaking, these examples are indeed present, and they have existed since the initial implementation. After running CSE, `%c1_dup = arith.constant 1 : i32` becomes a dead op. However, it doesn't have any operands. Even if it did, there's no need to recursively delete the def-ops of those operands, because they are shared by the identical existingOp.

```
func.func @example() -> i32 {
  %c1 = arith.constant 1 : i32
  %c2 = arith.addi %c1, %c1 : i32
  %c1_dup = arith.constant 1 : i32
  %result = arith.addi %c1_dup, %c2 : i32
  return %result : i32
}
```

https://github.com/llvm/llvm-project/pull/193778


More information about the Mlir-commits mailing list