[llvm] [CallGraphUpdater] Replace dead function in metadata with null instead of poison (PR #191729)

Robert Imschweiler via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 12 09:41:57 PDT 2026


https://github.com/ro-i created https://github.com/llvm/llvm-project/pull/191729

None

>From 38e96505eeaa08e2f9a429a02b0eb73f6d990bd1 Mon Sep 17 00:00:00 2001
From: Robert Imschweiler <robert.imschweiler at amd.com>
Date: Sun, 12 Apr 2026 10:39:08 -0500
Subject: [PATCH] [CallGraphUpdater] Replace dead function in metadata with
 null instead of poison

---
 .../lib/Transforms/Utils/CallGraphUpdater.cpp |  7 ++++-
 .../Inline/inline-history-dead-function.ll    | 29 +++++++++++++++++++
 2 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/Transforms/Inline/inline-history-dead-function.ll

diff --git a/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp b/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp
index 3b6fce578ffcc..46823c387be63 100644
--- a/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp
+++ b/llvm/lib/Transforms/Utils/CallGraphUpdater.cpp
@@ -29,7 +29,12 @@ bool CallGraphUpdater::finalize() {
   // no call graph was provided.
   for (Function *DeadFn : DeadFunctions) {
     DeadFn->removeDeadConstantUsers();
-    DeadFn->replaceAllUsesWith(PoisonValue::get(DeadFn->getType()));
+    // If the function is used by metadata, we don't want it to be replaced with
+    // poison in the metadata, so we replace it with nullptr in the metadata
+    // before RAUW'ing the non-metadata uses below.
+    if (DeadFn->isUsedByMetadata())
+      ValueAsMetadata::handleDeletion(DeadFn);
+    DeadFn->replaceNonMetadataUsesWith(PoisonValue::get(DeadFn->getType()));
 
     if (LCG && !ReplacedFunctions.count(DeadFn)) {
       // Taken mostly from the inliner:
diff --git a/llvm/test/Transforms/Inline/inline-history-dead-function.ll b/llvm/test/Transforms/Inline/inline-history-dead-function.ll
new file mode 100644
index 0000000000000..367b0f70d07ac
--- /dev/null
+++ b/llvm/test/Transforms/Inline/inline-history-dead-function.ll
@@ -0,0 +1,29 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=attributor -S < %s 2>&1 | FileCheck %s
+
+; When a function referenced in !inline_history metadata is deleted by a pass
+; that uses CallGraphUpdater (e.g. Attributor), the function must not simply
+; get RAUW'd with poison before being erased. Reason: this causes the
+; !inline_history metadata to contain "ptr poison" instead of null, which would
+; be the correct choice.
+; This test verifies that the metadata contains null instead of poison.
+
+define internal void @dead_fn() {
+  unreachable
+}
+
+define void @live_fn() {
+; CHECK-LABEL: define void @live_fn() {
+; CHECK-NEXT:    call void @ext(), !inline_history [[META0:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+  call void @ext(), !inline_history !0
+  ret void
+}
+
+declare void @ext()
+
+!0 = !{ptr @dead_fn}
+;.
+; CHECK: [[META0]] = distinct !{null}
+;.



More information about the llvm-commits mailing list