[Mlir-commits] [mlir] [mlir] Fix remove-dead-values crash on non-call symbol users (PR #181155)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Feb 12 06:44:57 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir-core

Author: Jueon Park (JueonPark)

<details>
<summary>Changes</summary>

Fixes #<!-- -->180416.

processFuncOp in the remove-dead-values pass previously assumed that all symbol users of a function are CallOpInterface ops. This is not always true — for example, spirv.EntryPoint references a function symbol but is not a call-like op. The assumption caused a crash when iterating over symbol users and unconditionally casting them to CallOpInterface.

Instead of bailing out entirely when a non-call symbol user is present, this patch filters the symbol uses to collect only call-like users into a SmallVector<Operation *>. The downstream loops then iterate over this filtered list. This allows the pass to still optimize functions that have both call-like and non-call-like symbol users, in addition to cleaning all assertions.

---
Full diff: https://github.com/llvm/llvm-project/pull/181155.diff


2 Files Affected:

- (modified) mlir/lib/Transforms/RemoveDeadValues.cpp (+13-10) 
- (modified) mlir/test/Transforms/remove-dead-values.mlir (+16) 


``````````diff
diff --git a/mlir/lib/Transforms/RemoveDeadValues.cpp b/mlir/lib/Transforms/RemoveDeadValues.cpp
index 66f369e8a5f65..9d1dd36ad4ca2 100644
--- a/mlir/lib/Transforms/RemoveDeadValues.cpp
+++ b/mlir/lib/Transforms/RemoveDeadValues.cpp
@@ -286,6 +286,16 @@ static void processFuncOp(FunctionOpInterface funcOp, Operation *module,
     return;
   }
 
+  // Collect only call-like symbol users. Non-call users (e.g. spirv.EntryPoint)
+  // are skipped because we cannot safely modify the function signature without
+  // knowing how such users depend on it.
+  SmallVector<Operation *> callUses;
+  auto symbolUses = funcOp.getSymbolUses(module);
+  for (SymbolTable::SymbolUse use : *symbolUses) {
+    if (isa<CallOpInterface>(use.getUser()))
+      callUses.push_back(use.getUser());
+  }
+
   // Get the list of unnecessary (non-live) arguments in `nonLiveArgs`.
   SmallVector<Value> arguments(funcOp.getArguments());
   BitVector nonLiveArgs = markLives(arguments, nonLiveSet, la);
@@ -299,10 +309,7 @@ static void processFuncOp(FunctionOpInterface funcOp, Operation *module,
   // Do (2). (Skip creating generic operand cleanup entries for call ops.
   // Call arguments will be removed in the call-site specific segment-aware
   // cleanup, avoiding generic eraseOperands bitvector mechanics.)
-  SymbolTable::UseRange uses = *funcOp.getSymbolUses(module);
-  for (SymbolTable::SymbolUse use : uses) {
-    Operation *callOp = use.getUser();
-    assert(isa<CallOpInterface>(callOp) && "expected a call-like user");
+  for (Operation *callOp : callUses) {
     // Push an empty operand cleanup entry so that call-site specific logic in
     // cleanUpDeadVals runs (it keys off CallOpInterface). The BitVector is
     // intentionally all false to avoid generic erasure.
@@ -336,9 +343,7 @@ static void processFuncOp(FunctionOpInterface funcOp, Operation *module,
   // since it forwards only to non-live value(s) (%1#1).
   size_t numReturns = funcOp.getNumResults();
   BitVector nonLiveRets(numReturns, true);
-  for (SymbolTable::SymbolUse use : uses) {
-    Operation *callOp = use.getUser();
-    assert(isa<CallOpInterface>(callOp) && "expected a call-like user");
+  for (Operation *callOp : callUses) {
     BitVector liveCallRets = markLives(callOp->getResults(), nonLiveSet, la);
     nonLiveRets &= liveCallRets.flip();
   }
@@ -360,9 +365,7 @@ static void processFuncOp(FunctionOpInterface funcOp, Operation *module,
   // Do (5) and (6).
   if (numReturns == 0)
     return;
-  for (SymbolTable::SymbolUse use : uses) {
-    Operation *callOp = use.getUser();
-    assert(isa<CallOpInterface>(callOp) && "expected a call-like user");
+  for (Operation *callOp : callUses) {
     cl.results.push_back({callOp, nonLiveRets});
     collectNonLiveValues(nonLiveSet, callOp->getResults(), nonLiveRets);
   }
diff --git a/mlir/test/Transforms/remove-dead-values.mlir b/mlir/test/Transforms/remove-dead-values.mlir
index ae83eac0c376f..e6d34881ebd14 100644
--- a/mlir/test/Transforms/remove-dead-values.mlir
+++ b/mlir/test/Transforms/remove-dead-values.mlir
@@ -796,3 +796,19 @@ func.func @scf_while_dead_iter_args() -> i32 {
   }
   return %result#0 : i32
 }
+
+// -----
+
+// Verify that `remove-dead-values` does not crash when a function has a
+// non-call symbol user (e.g. spirv.EntryPoint). The function should be
+// preserved as-is since its signature cannot be safely modified.
+
+// CHECK-LABEL: module
+// CHECK: spirv.EntryPoint "GLCompute" @main_func
+// CHECK: llvm.func @main_func()
+module {
+  spirv.EntryPoint "GLCompute" @main_func
+  llvm.func @main_func() attributes {sym_visibility = "private"} {
+    llvm.return
+  }
+}

``````````

</details>


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


More information about the Mlir-commits mailing list