[llvm] [RFC][IR] InstructionDeletionListener: per-function instruction removal notifications (PR #193198)

Pankaj Dwivedi via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 23 04:12:10 PDT 2026


https://github.com/PankajDwivedi-25 updated https://github.com/llvm/llvm-project/pull/193198

>From 5d24beb632dc4691ee16b2d50e2bf6658353a592 Mon Sep 17 00:00:00 2001
From: padivedi <padivedi at amd.com>
Date: Tue, 21 Apr 2026 16:55:36 +0530
Subject: [PATCH 1/5] [IR] Add ValueDeletionListener for context-level Value
 deletion notifications

---
 llvm/include/llvm/IR/LLVMContext.h            |  10 ++
 llvm/include/llvm/IR/ValueDeletionListener.h  |  58 ++++++++
 llvm/lib/IR/LLVMContext.cpp                   |  18 +++
 llvm/lib/IR/LLVMContextImpl.h                 |   6 +
 llvm/lib/IR/Value.cpp                         |   6 +
 llvm/unittests/IR/CMakeLists.txt              |   1 +
 .../IR/ValueDeletionListenerTest.cpp          | 132 ++++++++++++++++++
 7 files changed, 231 insertions(+)
 create mode 100644 llvm/include/llvm/IR/ValueDeletionListener.h
 create mode 100644 llvm/unittests/IR/ValueDeletionListenerTest.cpp

diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h
index 646a04673dbd0..1452822dec430 100644
--- a/llvm/include/llvm/IR/LLVMContext.h
+++ b/llvm/include/llvm/IR/LLVMContext.h
@@ -32,6 +32,7 @@ class Instruction;
 class LLVMContextImpl;
 class Module;
 class OptPassGate;
+class ValueDeletionListener;
 template <typename T> class SmallVectorImpl;
 template <typename T> class StringMapEntry;
 class StringRef;
@@ -309,6 +310,15 @@ class LLVMContext {
   /// any global mutex or cannot block the execution in another LLVM context.
   LLVM_ABI void yield();
 
+  /// Register a listener that will be notified whenever a Value in this
+  /// context is deleted. This is typically called from the
+  /// ValueDeletionListener constructor.
+  LLVM_ABI void addValueDeletionListener(ValueDeletionListener *L);
+
+  /// Remove a previously registered listener. This is typically called from
+  /// the ValueDeletionListener destructor.
+  LLVM_ABI void removeValueDeletionListener(ValueDeletionListener *L);
+
   /// emitError - Emit an error message to the currently installed error handler
   /// with optional location information.  This function returns, so code should
   /// be prepared to drop the erroneous construct on the floor and "not crash".
diff --git a/llvm/include/llvm/IR/ValueDeletionListener.h b/llvm/include/llvm/IR/ValueDeletionListener.h
new file mode 100644
index 0000000000000..340b081325453
--- /dev/null
+++ b/llvm/include/llvm/IR/ValueDeletionListener.h
@@ -0,0 +1,58 @@
+//===- llvm/IR/ValueDeletionListener.h - Callback on Value deletion -------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares ValueDeletionListener, an interface that allows analyses
+// to be notified when any Value in a given LLVMContext is deleted. Think of
+// an analysis that uses this as "a value handle to many values" — a single
+// registration replaces one handle per tracked value.
+//
+// Registration and unregistration happen automatically via RAII: the
+// constructor registers with an LLVMContext, and the destructor unregisters.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_VALUEDELETIONLISTENER_H
+#define LLVM_IR_VALUEDELETIONLISTENER_H
+
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+class LLVMContext;
+class Value;
+
+/// A listener that is notified whenever a Value is deleted in its LLVMContext.
+///
+/// Subclasses implement the callback by passing a static function to the
+/// constructor, which static_casts the listener back to the derived type.
+/// This avoids virtual dispatch overhead while preserving type safety at
+/// the point of registration.
+///
+/// Lifetime is managed via RAII: the constructor registers with the
+/// LLVMContext, and the destructor unregisters.
+class ValueDeletionListener {
+public:
+  using CallbackT = void (*)(ValueDeletionListener *, const Value *);
+
+private:
+  LLVMContext &Ctx;
+  CallbackT Callback;
+
+public:
+  LLVM_ABI ValueDeletionListener(LLVMContext &C, CallbackT CB);
+  LLVM_ABI ~ValueDeletionListener();
+
+  ValueDeletionListener(const ValueDeletionListener &) = delete;
+  ValueDeletionListener &operator=(const ValueDeletionListener &) = delete;
+
+  void valueDeleted(const Value *V) { Callback(this, V); }
+};
+
+} // namespace llvm
+
+#endif // LLVM_IR_VALUEDELETIONLISTENER_H
diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp
index 10aba759185a7..c756d9ef3e12c 100644
--- a/llvm/lib/IR/LLVMContext.cpp
+++ b/llvm/lib/IR/LLVMContext.cpp
@@ -20,6 +20,7 @@
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/IR/LLVMRemarkStreamer.h"
+#include "llvm/IR/ValueDeletionListener.h"
 #include "llvm/Remarks/RemarkStreamer.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -207,6 +208,23 @@ void LLVMContext::yield() {
     pImpl->YieldCallback(this, pImpl->YieldOpaqueHandle);
 }
 
+ValueDeletionListener::ValueDeletionListener(LLVMContext &C, CallbackT CB)
+    : Ctx(C), Callback(CB) {
+  Ctx.addValueDeletionListener(this);
+}
+
+ValueDeletionListener::~ValueDeletionListener() {
+  Ctx.removeValueDeletionListener(this);
+}
+
+void LLVMContext::addValueDeletionListener(ValueDeletionListener *L) {
+  pImpl->ValueDeletionListeners.insert(L);
+}
+
+void LLVMContext::removeValueDeletionListener(ValueDeletionListener *L) {
+  pImpl->ValueDeletionListeners.erase(L);
+}
+
 void LLVMContext::emitError(const Twine &ErrorStr) {
   diagnose(DiagnosticInfoGeneric(ErrorStr));
 }
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 11245b4ea7803..09d31a0da58af 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -73,6 +73,7 @@ class RemarkStreamer;
 template <typename T> class StringMapEntry;
 class StringRef;
 class TypedPointerType;
+class ValueDeletionListener;
 class ValueHandleBase;
 
 template <> struct DenseMapInfo<APFloat> {
@@ -1747,6 +1748,11 @@ class LLVMContextImpl {
   using ValueHandlesTy = DenseMap<Value *, ValueHandleBase *>;
   ValueHandlesTy ValueHandles;
 
+  /// Context-level listeners notified when any Value in this context is
+  /// deleted. Used by analyses that track Value pointers (e.g. UniformValues)
+  /// to remove stale entries without per-value handle overhead.
+  SmallPtrSet<ValueDeletionListener *, 2> ValueDeletionListeners;
+
   /// CustomMDKindNames - Map to hold the metadata string to ID mapping.
   StringMap<unsigned> CustomMDKindNames;
 
diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index 360bf0f8fc47f..69a85f423679e 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -27,6 +27,7 @@
 #include "llvm/IR/Module.h"
 #include "llvm/IR/Operator.h"
 #include "llvm/IR/TypedPointerType.h"
+#include "llvm/IR/ValueDeletionListener.h"
 #include "llvm/IR/ValueHandle.h"
 #include "llvm/IR/ValueSymbolTable.h"
 #include "llvm/Support/CommandLine.h"
@@ -77,6 +78,11 @@ Value::~Value() {
   // Notify all ValueHandles (if present) that this value is going away.
   if (HasValueHandle)
     ValueHandleBase::ValueIsDeleted(this);
+
+  // Notify context-level deletion listeners (e.g. analyses tracking Value*).
+  for (ValueDeletionListener *L : getContext().pImpl->ValueDeletionListeners)
+    L->valueDeleted(this);
+
   if (isUsedByMetadata())
     ValueAsMetadata::handleDeletion(this);
 
diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt
index d62ce66ef9d34..3684f3b699594 100644
--- a/llvm/unittests/IR/CMakeLists.txt
+++ b/llvm/unittests/IR/CMakeLists.txt
@@ -49,6 +49,7 @@ add_llvm_unittest(IRTests
   TypesTest.cpp
   UseTest.cpp
   UserTest.cpp
+  ValueDeletionListenerTest.cpp
   ValueHandleTest.cpp
   ValueMapTest.cpp
   ValueTest.cpp
diff --git a/llvm/unittests/IR/ValueDeletionListenerTest.cpp b/llvm/unittests/IR/ValueDeletionListenerTest.cpp
new file mode 100644
index 0000000000000..0332b95550a82
--- /dev/null
+++ b/llvm/unittests/IR/ValueDeletionListenerTest.cpp
@@ -0,0 +1,132 @@
+//===- ValueDeletionListenerTest.cpp - Tests for ValueDeletionListener ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/ValueDeletionListener.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/LLVMContext.h"
+#include "gtest/gtest.h"
+#include <memory>
+
+using namespace llvm;
+
+namespace {
+
+class TrackingListener : public ValueDeletionListener {
+  DenseSet<const Value *> &Tracked;
+
+  static void callback(ValueDeletionListener *Self, const Value *V) {
+    static_cast<TrackingListener *>(Self)->Tracked.erase(V);
+  }
+
+public:
+  TrackingListener(LLVMContext &C, DenseSet<const Value *> &S)
+      : ValueDeletionListener(C, &callback), Tracked(S) {}
+};
+
+TEST(ValueDeletionListenerTest, BasicDeletion) {
+  LLVMContext C;
+  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
+
+  DenseSet<const Value *> Tracked;
+  TrackingListener Listener(C, Tracked);
+
+  std::unique_ptr<BitCastInst> V(
+      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
+  Tracked.insert(V.get());
+  EXPECT_TRUE(Tracked.contains(V.get()));
+
+  // Deleting the value triggers the listener, removing it from the set.
+  V.reset();
+  EXPECT_TRUE(Tracked.empty());
+}
+
+TEST(ValueDeletionListenerTest, AddressReuse) {
+  LLVMContext C;
+  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
+
+  DenseSet<const Value *> Tracked;
+  TrackingListener Listener(C, Tracked);
+
+  // Create, track, and destroy a value.
+  const Value *OldAddr;
+  {
+    std::unique_ptr<BitCastInst> V1(
+        new BitCastInst(ConstantV, Type::getInt32Ty(C)));
+    OldAddr = V1.get();
+    Tracked.insert(OldAddr);
+    EXPECT_TRUE(Tracked.contains(OldAddr));
+  } // V1 destroyed here — listener removes it from Tracked.
+  EXPECT_FALSE(Tracked.contains(OldAddr));
+
+  // Allocate a new instruction. Even if the allocator reuses the same
+  // address, the set must not contain it.
+  std::unique_ptr<BitCastInst> V2(
+      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
+  EXPECT_FALSE(Tracked.contains(V2.get()));
+}
+
+TEST(ValueDeletionListenerTest, ListenerScopeRAII) {
+  LLVMContext C;
+  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
+
+  DenseSet<const Value *> Tracked;
+
+  std::unique_ptr<BitCastInst> V(
+      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
+  Tracked.insert(V.get());
+
+  {
+    TrackingListener Listener(C, Tracked);
+    EXPECT_TRUE(Tracked.contains(V.get()));
+  }
+  // Listener is destroyed (unregistered). Deleting the value now must not
+  // crash, but the set won't be updated since no listener is active.
+  const Value *Addr = V.get();
+  V.reset();
+  EXPECT_TRUE(Tracked.contains(Addr));
+}
+
+TEST(ValueDeletionListenerTest, MultipleListeners) {
+  LLVMContext C;
+  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
+
+  DenseSet<const Value *> Set1, Set2;
+  TrackingListener L1(C, Set1);
+  TrackingListener L2(C, Set2);
+
+  std::unique_ptr<BitCastInst> V(
+      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
+  Set1.insert(V.get());
+  Set2.insert(V.get());
+
+  V.reset();
+  EXPECT_TRUE(Set1.empty());
+  EXPECT_TRUE(Set2.empty());
+}
+
+TEST(ValueDeletionListenerTest, UnrelatedValueNotAffected) {
+  LLVMContext C;
+  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
+
+  DenseSet<const Value *> Tracked;
+  TrackingListener Listener(C, Tracked);
+
+  std::unique_ptr<BitCastInst> V1(
+      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
+  std::unique_ptr<BitCastInst> V2(
+      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
+  Tracked.insert(V1.get());
+
+  // Deleting V2 (not tracked) should not affect V1 in the set.
+  V2.reset();
+  EXPECT_TRUE(Tracked.contains(V1.get()));
+}
+
+} // namespace

>From df53d34b020c642fa294fc3b1a28a3531bf58340 Mon Sep 17 00:00:00 2001
From: padivedi <padivedi at amd.com>
Date: Wed, 22 Apr 2026 10:53:33 +0530
Subject: [PATCH 2/5] review: address suggestion

---
 llvm/include/llvm/IR/LLVMContext.h | 5 +++++
 llvm/lib/IR/LLVMContext.cpp        | 5 +++++
 llvm/lib/IR/Value.cpp              | 4 +---
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h
index 1452822dec430..0acdd8cbc2c7e 100644
--- a/llvm/include/llvm/IR/LLVMContext.h
+++ b/llvm/include/llvm/IR/LLVMContext.h
@@ -37,6 +37,7 @@ template <typename T> class SmallVectorImpl;
 template <typename T> class StringMapEntry;
 class StringRef;
 class Twine;
+class Value;
 class LLVMRemarkStreamer;
 
 namespace remarks {
@@ -319,6 +320,10 @@ class LLVMContext {
   /// the ValueDeletionListener destructor.
   LLVM_ABI void removeValueDeletionListener(ValueDeletionListener *L);
 
+  /// Notify all registered ValueDeletionListeners that \p V is being deleted.
+  /// Called from ~Value().
+  LLVM_ABI void notifyValueDeleted(Value *V);
+
   /// emitError - Emit an error message to the currently installed error handler
   /// with optional location information.  This function returns, so code should
   /// be prepared to drop the erroneous construct on the floor and "not crash".
diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp
index c756d9ef3e12c..165c82272f691 100644
--- a/llvm/lib/IR/LLVMContext.cpp
+++ b/llvm/lib/IR/LLVMContext.cpp
@@ -225,6 +225,11 @@ void LLVMContext::removeValueDeletionListener(ValueDeletionListener *L) {
   pImpl->ValueDeletionListeners.erase(L);
 }
 
+void LLVMContext::notifyValueDeleted(Value *V) {
+  for (ValueDeletionListener *L : pImpl->ValueDeletionListeners)
+    L->valueDeleted(V);
+}
+
 void LLVMContext::emitError(const Twine &ErrorStr) {
   diagnose(DiagnosticInfoGeneric(ErrorStr));
 }
diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index 69a85f423679e..32e355f20566e 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -27,7 +27,6 @@
 #include "llvm/IR/Module.h"
 #include "llvm/IR/Operator.h"
 #include "llvm/IR/TypedPointerType.h"
-#include "llvm/IR/ValueDeletionListener.h"
 #include "llvm/IR/ValueHandle.h"
 #include "llvm/IR/ValueSymbolTable.h"
 #include "llvm/Support/CommandLine.h"
@@ -80,8 +79,7 @@ Value::~Value() {
     ValueHandleBase::ValueIsDeleted(this);
 
   // Notify context-level deletion listeners (e.g. analyses tracking Value*).
-  for (ValueDeletionListener *L : getContext().pImpl->ValueDeletionListeners)
-    L->valueDeleted(this);
+  getContext().notifyValueDeleted(this);
 
   if (isUsedByMetadata())
     ValueAsMetadata::handleDeletion(this);

>From 2af998bd9f35f4696a05e65bd5d846ae4e4f58e1 Mon Sep 17 00:00:00 2001
From: padivedi <padivedi at amd.com>
Date: Wed, 22 Apr 2026 12:27:58 +0530
Subject: [PATCH 3/5] review: make listener api's private

---
 llvm/include/llvm/IR/LLVMContext.h           | 22 ++++++++------------
 llvm/include/llvm/IR/ValueDeletionListener.h |  6 +++---
 2 files changed, 12 insertions(+), 16 deletions(-)

diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h
index 0acdd8cbc2c7e..c3be962c14cb8 100644
--- a/llvm/include/llvm/IR/LLVMContext.h
+++ b/llvm/include/llvm/IR/LLVMContext.h
@@ -311,19 +311,6 @@ class LLVMContext {
   /// any global mutex or cannot block the execution in another LLVM context.
   LLVM_ABI void yield();
 
-  /// Register a listener that will be notified whenever a Value in this
-  /// context is deleted. This is typically called from the
-  /// ValueDeletionListener constructor.
-  LLVM_ABI void addValueDeletionListener(ValueDeletionListener *L);
-
-  /// Remove a previously registered listener. This is typically called from
-  /// the ValueDeletionListener destructor.
-  LLVM_ABI void removeValueDeletionListener(ValueDeletionListener *L);
-
-  /// Notify all registered ValueDeletionListeners that \p V is being deleted.
-  /// Called from ~Value().
-  LLVM_ABI void notifyValueDeleted(Value *V);
-
   /// emitError - Emit an error message to the currently installed error handler
   /// with optional location information.  This function returns, so code should
   /// be prepared to drop the erroneous construct on the floor and "not crash".
@@ -377,6 +364,15 @@ class LLVMContext {
 
   /// removeModule - Unregister a module from this context.
   void removeModule(Module *);
+
+  // ValueDeletionListener registers/deregisters via its constructor/destructor.
+  friend class ValueDeletionListener;
+  // Value calls notifyValueDeleted from ~Value().
+  friend class Value;
+
+  LLVM_ABI void addValueDeletionListener(ValueDeletionListener *L);
+  LLVM_ABI void removeValueDeletionListener(ValueDeletionListener *L);
+  LLVM_ABI void notifyValueDeleted(Value *V);
 };
 
 // Create wrappers for C Binding types (see CBindingWrapping.h).
diff --git a/llvm/include/llvm/IR/ValueDeletionListener.h b/llvm/include/llvm/IR/ValueDeletionListener.h
index 340b081325453..465ca2b5b9597 100644
--- a/llvm/include/llvm/IR/ValueDeletionListener.h
+++ b/llvm/include/llvm/IR/ValueDeletionListener.h
@@ -11,8 +11,8 @@
 // an analysis that uses this as "a value handle to many values" — a single
 // registration replaces one handle per tracked value.
 //
-// Registration and unregistration happen automatically via RAII: the
-// constructor registers with an LLVMContext, and the destructor unregisters.
+// Registration and deregistration happen automatically via RAII: the
+// constructor registers with an LLVMContext, and the destructor deregisters.
 //
 //===----------------------------------------------------------------------===//
 
@@ -34,7 +34,7 @@ class Value;
 /// the point of registration.
 ///
 /// Lifetime is managed via RAII: the constructor registers with the
-/// LLVMContext, and the destructor unregisters.
+/// LLVMContext, and the destructor deregisters.
 class ValueDeletionListener {
 public:
   using CallbackT = void (*)(ValueDeletionListener *, const Value *);

>From 10d637ff875bb440fe33e2060e4d07f5a1786c72 Mon Sep 17 00:00:00 2001
From: padivedi <padivedi at amd.com>
Date: Wed, 22 Apr 2026 14:20:36 +0530
Subject: [PATCH 4/5] review: address suggestion

---
 llvm/lib/IR/LLVMContext.cpp   | 7 +++++--
 llvm/lib/IR/LLVMContextImpl.h | 2 +-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp
index 165c82272f691..78a99dfb46630 100644
--- a/llvm/lib/IR/LLVMContext.cpp
+++ b/llvm/lib/IR/LLVMContext.cpp
@@ -218,11 +218,14 @@ ValueDeletionListener::~ValueDeletionListener() {
 }
 
 void LLVMContext::addValueDeletionListener(ValueDeletionListener *L) {
-  pImpl->ValueDeletionListeners.insert(L);
+  assert(!llvm::is_contained(pImpl->ValueDeletionListeners, L) &&
+         "Listener already registered");
+  pImpl->ValueDeletionListeners.push_back(L);
 }
 
 void LLVMContext::removeValueDeletionListener(ValueDeletionListener *L) {
-  pImpl->ValueDeletionListeners.erase(L);
+  pImpl->ValueDeletionListeners.erase(
+      llvm::find(pImpl->ValueDeletionListeners, L));
 }
 
 void LLVMContext::notifyValueDeleted(Value *V) {
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 09d31a0da58af..8c7604ad86c21 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -1751,7 +1751,7 @@ class LLVMContextImpl {
   /// Context-level listeners notified when any Value in this context is
   /// deleted. Used by analyses that track Value pointers (e.g. UniformValues)
   /// to remove stale entries without per-value handle overhead.
-  SmallPtrSet<ValueDeletionListener *, 2> ValueDeletionListeners;
+  SmallVector<ValueDeletionListener *, 2> ValueDeletionListeners;
 
   /// CustomMDKindNames - Map to hold the metadata string to ID mapping.
   StringMap<unsigned> CustomMDKindNames;

>From 65521516c55e20437b037bdf4edb75d12524903a Mon Sep 17 00:00:00 2001
From: padivedi <padivedi at amd.com>
Date: Thu, 23 Apr 2026 16:02:24 +0530
Subject: [PATCH 5/5] change the listener design from ~value per context to
 instructiper function

---
 llvm/include/llvm/IR/Function.h               |  17 ++
 llvm/include/llvm/IR/Instruction.h            |   4 +
 .../llvm/IR/InstructionDeletionListener.h     |  61 ++++++
 llvm/include/llvm/IR/LLVMContext.h            |  11 -
 llvm/include/llvm/IR/ValueDeletionListener.h  |  58 ------
 llvm/lib/IR/BasicBlock.cpp                    |   6 +-
 llvm/lib/IR/Function.cpp                      |  28 +++
 llvm/lib/IR/Instruction.cpp                   |  10 +
 llvm/lib/IR/LLVMContext.cpp                   |  26 ---
 llvm/lib/IR/LLVMContextImpl.h                 |   6 -
 llvm/lib/IR/Value.cpp                         |   4 -
 llvm/unittests/IR/CMakeLists.txt              |   2 +-
 .../IR/InstructionDeletionListenerTest.cpp    | 189 ++++++++++++++++++
 .../IR/ValueDeletionListenerTest.cpp          | 132 ------------
 14 files changed, 315 insertions(+), 239 deletions(-)
 create mode 100644 llvm/include/llvm/IR/InstructionDeletionListener.h
 delete mode 100644 llvm/include/llvm/IR/ValueDeletionListener.h
 create mode 100644 llvm/unittests/IR/InstructionDeletionListenerTest.cpp
 delete mode 100644 llvm/unittests/IR/ValueDeletionListenerTest.cpp

diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index f39fe509a49a4..a6010a90d8869 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -18,6 +18,7 @@
 #define LLVM_IR_FUNCTION_H
 
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/ADT/ilist_node.h"
@@ -61,6 +62,7 @@ class Type;
 class User;
 class BranchProbabilityInfo;
 class BlockFrequencyInfo;
+class InstructionDeletionListener;
 
 class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
 public:
@@ -112,7 +114,22 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
 
   friend class SymbolTableListTraits<Function>;
 
+  friend class InstructionDeletionListener;
+  SmallVector<InstructionDeletionListener *, 0> InstructionDeletionListeners;
+
+  void addInstructionDeletionListener(InstructionDeletionListener *L);
+  void removeInstructionDeletionListener(InstructionDeletionListener *L);
+
 public:
+  /// Notify all registered listeners that an instruction is being removed
+  /// from this function. Called from Instruction::setParent and
+  /// BasicBlock::setParent when the parent is being set to null.
+  LLVM_ABI void notifyInstructionRemoved(Instruction *I);
+
+  bool hasInstructionDeletionListeners() const {
+    return !InstructionDeletionListeners.empty();
+  }
+
   /// hasLazyArguments/CheckLazyArguments - The argument list of a function is
   /// built on demand, so that the list isn't allocated until the first client
   /// needs it.  The hasLazyArguments predicate returns true if the arg list
diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h
index 0b57ad4d0a379..a2661702d35a1 100644
--- a/llvm/include/llvm/IR/Instruction.h
+++ b/llvm/include/llvm/IR/Instruction.h
@@ -1081,6 +1081,10 @@ class Instruction : public User,
                                      ilist_parent<BasicBlock>>;
   friend class BasicBlock; // For renumbering.
 
+  /// Overrides ilist-provided setParent to notify per-function
+  /// InstructionDeletionListeners when this instruction is removed.
+  void setParent(BasicBlock *P);
+
   // Shadow Value::setValueSubclassData with a private forwarding method so that
   // subclasses cannot accidentally use it.
   void setValueSubclassData(unsigned short D) {
diff --git a/llvm/include/llvm/IR/InstructionDeletionListener.h b/llvm/include/llvm/IR/InstructionDeletionListener.h
new file mode 100644
index 0000000000000..c96b374a0ae51
--- /dev/null
+++ b/llvm/include/llvm/IR/InstructionDeletionListener.h
@@ -0,0 +1,61 @@
+//===- llvm/IR/InstructionDeletionListener.h - Instruction removal hook
+//----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares InstructionDeletionListener, an interface that allows
+// analyses to be notified when an Instruction is removed from a Function.
+// Think of an analysis that uses this as "a value handle to many values" —
+// a single per-function registration replaces one handle per tracked value.
+//
+// Registration and deregistration happen automatically via RAII: the
+// constructor registers with a Function, and the destructor deregisters.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_INSTRUCTIONDELETIONLISTENER_H
+#define LLVM_IR_INSTRUCTIONDELETIONLISTENER_H
+
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+class Function;
+class Instruction;
+
+/// A per-function listener notified when an Instruction is removed from its
+/// parent BasicBlock.
+///
+/// Subclasses implement the callback by passing a static function to the
+/// constructor, which static_casts the listener back to the derived type.
+/// This avoids virtual dispatch overhead while preserving type safety.
+///
+/// Lifetime is managed via RAII: the constructor registers with the
+/// Function, and the destructor deregisters.
+class InstructionDeletionListener {
+public:
+  using CallbackT = void (*)(InstructionDeletionListener *, Instruction *);
+
+private:
+  Function &F;
+  CallbackT Callback;
+
+public:
+  LLVM_ABI InstructionDeletionListener(Function &F, CallbackT CB);
+  LLVM_ABI ~InstructionDeletionListener();
+
+  InstructionDeletionListener(const InstructionDeletionListener &) = delete;
+  InstructionDeletionListener &
+  operator=(const InstructionDeletionListener &) = delete;
+
+  void instructionRemoved(Instruction *I) { Callback(this, I); }
+  Function &getFunction() const { return F; }
+};
+
+} // namespace llvm
+
+#endif // LLVM_IR_INSTRUCTIONDELETIONLISTENER_H
diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h
index c3be962c14cb8..646a04673dbd0 100644
--- a/llvm/include/llvm/IR/LLVMContext.h
+++ b/llvm/include/llvm/IR/LLVMContext.h
@@ -32,12 +32,10 @@ class Instruction;
 class LLVMContextImpl;
 class Module;
 class OptPassGate;
-class ValueDeletionListener;
 template <typename T> class SmallVectorImpl;
 template <typename T> class StringMapEntry;
 class StringRef;
 class Twine;
-class Value;
 class LLVMRemarkStreamer;
 
 namespace remarks {
@@ -364,15 +362,6 @@ class LLVMContext {
 
   /// removeModule - Unregister a module from this context.
   void removeModule(Module *);
-
-  // ValueDeletionListener registers/deregisters via its constructor/destructor.
-  friend class ValueDeletionListener;
-  // Value calls notifyValueDeleted from ~Value().
-  friend class Value;
-
-  LLVM_ABI void addValueDeletionListener(ValueDeletionListener *L);
-  LLVM_ABI void removeValueDeletionListener(ValueDeletionListener *L);
-  LLVM_ABI void notifyValueDeleted(Value *V);
 };
 
 // Create wrappers for C Binding types (see CBindingWrapping.h).
diff --git a/llvm/include/llvm/IR/ValueDeletionListener.h b/llvm/include/llvm/IR/ValueDeletionListener.h
deleted file mode 100644
index 465ca2b5b9597..0000000000000
--- a/llvm/include/llvm/IR/ValueDeletionListener.h
+++ /dev/null
@@ -1,58 +0,0 @@
-//===- llvm/IR/ValueDeletionListener.h - Callback on Value deletion -------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-//
-// This file declares ValueDeletionListener, an interface that allows analyses
-// to be notified when any Value in a given LLVMContext is deleted. Think of
-// an analysis that uses this as "a value handle to many values" — a single
-// registration replaces one handle per tracked value.
-//
-// Registration and deregistration happen automatically via RAII: the
-// constructor registers with an LLVMContext, and the destructor deregisters.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_IR_VALUEDELETIONLISTENER_H
-#define LLVM_IR_VALUEDELETIONLISTENER_H
-
-#include "llvm/Support/Compiler.h"
-
-namespace llvm {
-
-class LLVMContext;
-class Value;
-
-/// A listener that is notified whenever a Value is deleted in its LLVMContext.
-///
-/// Subclasses implement the callback by passing a static function to the
-/// constructor, which static_casts the listener back to the derived type.
-/// This avoids virtual dispatch overhead while preserving type safety at
-/// the point of registration.
-///
-/// Lifetime is managed via RAII: the constructor registers with the
-/// LLVMContext, and the destructor deregisters.
-class ValueDeletionListener {
-public:
-  using CallbackT = void (*)(ValueDeletionListener *, const Value *);
-
-private:
-  LLVMContext &Ctx;
-  CallbackT Callback;
-
-public:
-  LLVM_ABI ValueDeletionListener(LLVMContext &C, CallbackT CB);
-  LLVM_ABI ~ValueDeletionListener();
-
-  ValueDeletionListener(const ValueDeletionListener &) = delete;
-  ValueDeletionListener &operator=(const ValueDeletionListener &) = delete;
-
-  void valueDeleted(const Value *V) { Callback(this, V); }
-};
-
-} // namespace llvm
-
-#endif // LLVM_IR_VALUEDELETIONLISTENER_H
diff --git a/llvm/lib/IR/BasicBlock.cpp b/llvm/lib/IR/BasicBlock.cpp
index da97b26f7cec5..ba52916581476 100644
--- a/llvm/lib/IR/BasicBlock.cpp
+++ b/llvm/lib/IR/BasicBlock.cpp
@@ -17,6 +17,7 @@
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/DebugProgramInstruction.h"
+#include "llvm/IR/Function.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/LLVMContext.h"
@@ -195,7 +196,10 @@ BasicBlock::~BasicBlock() {
 }
 
 void BasicBlock::setParent(Function *parent) {
-  // Set Parent=parent, updating instruction symtab entries as appropriate.
+  if (!parent && Parent && Parent->hasInstructionDeletionListeners()) {
+    for (Instruction &I : *this)
+      Parent->notifyInstructionRemoved(&I);
+  }
   if (Parent != parent)
     Number = parent ? parent->NextBlockNum++ : -1u;
   InstList.setSymTabObject(&Parent, parent);
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index a6568bb50f0c8..f3ba502dd3029 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -30,6 +30,7 @@
 #include "llvm/IR/GlobalValue.h"
 #include "llvm/IR/InstIterator.h"
 #include "llvm/IR/Instruction.h"
+#include "llvm/IR/InstructionDeletionListener.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/LLVMContext.h"
@@ -62,6 +63,33 @@ using ProfileCount = Function::ProfileCount;
 // are not in the public header file...
 template class LLVM_EXPORT_TEMPLATE llvm::SymbolTableListTraits<BasicBlock>;
 
+InstructionDeletionListener::InstructionDeletionListener(Function &F,
+                                                         CallbackT CB)
+    : F(F), Callback(CB) {
+  F.addInstructionDeletionListener(this);
+}
+
+InstructionDeletionListener::~InstructionDeletionListener() {
+  F.removeInstructionDeletionListener(this);
+}
+
+void Function::addInstructionDeletionListener(InstructionDeletionListener *L) {
+  assert(!llvm::is_contained(InstructionDeletionListeners, L) &&
+         "Listener already registered");
+  InstructionDeletionListeners.push_back(L);
+}
+
+void Function::removeInstructionDeletionListener(
+    InstructionDeletionListener *L) {
+  InstructionDeletionListeners.erase(
+      llvm::find(InstructionDeletionListeners, L));
+}
+
+void Function::notifyInstructionRemoved(Instruction *I) {
+  for (InstructionDeletionListener *L : InstructionDeletionListeners)
+    L->instructionRemoved(I);
+}
+
 static cl::opt<int> NonGlobalValueMaxNameSize(
     "non-global-value-max-name-size", cl::Hidden, cl::init(1024),
     cl::desc("Maximum size for the name of non-global values."));
diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp
index 8aa19a436a157..7547640552895 100644
--- a/llvm/lib/IR/Instruction.cpp
+++ b/llvm/lib/IR/Instruction.cpp
@@ -16,6 +16,7 @@
 #include "llvm/IR/AttributeMask.h"
 #include "llvm/IR/Attributes.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/Function.h"
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
@@ -95,6 +96,15 @@ const DataLayout &Instruction::getDataLayout() const {
   return getModule()->getDataLayout();
 }
 
+void Instruction::setParent(BasicBlock *P) {
+  if (!P && getParent() && getParent()->getParent())
+    getParent()->getParent()->notifyInstructionRemoved(this);
+  using Base =
+      ilist_node_with_parent<Instruction, BasicBlock, ilist_iterator_bits<true>,
+                             ilist_parent<BasicBlock>>;
+  Base::setParent(P);
+}
+
 void Instruction::removeFromParent() {
   // Perform any debug-info maintenence required.
   handleMarkerRemoval();
diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp
index 78a99dfb46630..10aba759185a7 100644
--- a/llvm/lib/IR/LLVMContext.cpp
+++ b/llvm/lib/IR/LLVMContext.cpp
@@ -20,7 +20,6 @@
 #include "llvm/IR/DiagnosticInfo.h"
 #include "llvm/IR/DiagnosticPrinter.h"
 #include "llvm/IR/LLVMRemarkStreamer.h"
-#include "llvm/IR/ValueDeletionListener.h"
 #include "llvm/Remarks/RemarkStreamer.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -208,31 +207,6 @@ void LLVMContext::yield() {
     pImpl->YieldCallback(this, pImpl->YieldOpaqueHandle);
 }
 
-ValueDeletionListener::ValueDeletionListener(LLVMContext &C, CallbackT CB)
-    : Ctx(C), Callback(CB) {
-  Ctx.addValueDeletionListener(this);
-}
-
-ValueDeletionListener::~ValueDeletionListener() {
-  Ctx.removeValueDeletionListener(this);
-}
-
-void LLVMContext::addValueDeletionListener(ValueDeletionListener *L) {
-  assert(!llvm::is_contained(pImpl->ValueDeletionListeners, L) &&
-         "Listener already registered");
-  pImpl->ValueDeletionListeners.push_back(L);
-}
-
-void LLVMContext::removeValueDeletionListener(ValueDeletionListener *L) {
-  pImpl->ValueDeletionListeners.erase(
-      llvm::find(pImpl->ValueDeletionListeners, L));
-}
-
-void LLVMContext::notifyValueDeleted(Value *V) {
-  for (ValueDeletionListener *L : pImpl->ValueDeletionListeners)
-    L->valueDeleted(V);
-}
-
 void LLVMContext::emitError(const Twine &ErrorStr) {
   diagnose(DiagnosticInfoGeneric(ErrorStr));
 }
diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h
index 8c7604ad86c21..11245b4ea7803 100644
--- a/llvm/lib/IR/LLVMContextImpl.h
+++ b/llvm/lib/IR/LLVMContextImpl.h
@@ -73,7 +73,6 @@ class RemarkStreamer;
 template <typename T> class StringMapEntry;
 class StringRef;
 class TypedPointerType;
-class ValueDeletionListener;
 class ValueHandleBase;
 
 template <> struct DenseMapInfo<APFloat> {
@@ -1748,11 +1747,6 @@ class LLVMContextImpl {
   using ValueHandlesTy = DenseMap<Value *, ValueHandleBase *>;
   ValueHandlesTy ValueHandles;
 
-  /// Context-level listeners notified when any Value in this context is
-  /// deleted. Used by analyses that track Value pointers (e.g. UniformValues)
-  /// to remove stale entries without per-value handle overhead.
-  SmallVector<ValueDeletionListener *, 2> ValueDeletionListeners;
-
   /// CustomMDKindNames - Map to hold the metadata string to ID mapping.
   StringMap<unsigned> CustomMDKindNames;
 
diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index 32e355f20566e..360bf0f8fc47f 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -77,10 +77,6 @@ Value::~Value() {
   // Notify all ValueHandles (if present) that this value is going away.
   if (HasValueHandle)
     ValueHandleBase::ValueIsDeleted(this);
-
-  // Notify context-level deletion listeners (e.g. analyses tracking Value*).
-  getContext().notifyValueDeleted(this);
-
   if (isUsedByMetadata())
     ValueAsMetadata::handleDeletion(this);
 
diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt
index 3684f3b699594..b31996f5f69fa 100644
--- a/llvm/unittests/IR/CMakeLists.txt
+++ b/llvm/unittests/IR/CMakeLists.txt
@@ -31,6 +31,7 @@ add_llvm_unittest(IRTests
   GlobalObjectTest.cpp
   PassBuilderCallbacksTest.cpp
   IRBuilderTest.cpp
+  InstructionDeletionListenerTest.cpp
   InstructionsTest.cpp
   IntrinsicsTest.cpp
   LegacyPassManagerTest.cpp
@@ -49,7 +50,6 @@ add_llvm_unittest(IRTests
   TypesTest.cpp
   UseTest.cpp
   UserTest.cpp
-  ValueDeletionListenerTest.cpp
   ValueHandleTest.cpp
   ValueMapTest.cpp
   ValueTest.cpp
diff --git a/llvm/unittests/IR/InstructionDeletionListenerTest.cpp b/llvm/unittests/IR/InstructionDeletionListenerTest.cpp
new file mode 100644
index 0000000000000..53e3a7e5afee0
--- /dev/null
+++ b/llvm/unittests/IR/InstructionDeletionListenerTest.cpp
@@ -0,0 +1,189 @@
+//===- InstructionDeletionListenerTest.cpp - per-Function listener tests --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/InstructionDeletionListener.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+class TrackingListener : public InstructionDeletionListener {
+  DenseSet<const Value *> &UniformValuesRef;
+
+  static void callback(InstructionDeletionListener *Self, Instruction *I) {
+    static_cast<TrackingListener *>(Self)->UniformValuesRef.erase(I);
+  }
+
+public:
+  TrackingListener(Function &F, DenseSet<const Value *> &UniformValues)
+      : InstructionDeletionListener(F, &callback),
+        UniformValuesRef(UniformValues) {}
+};
+
+static Function *createFunction(Module &M, const char *Name) {
+  Type *I32Ty = Type::getInt32Ty(M.getContext());
+  FunctionType *FTy = FunctionType::get(I32Ty, {I32Ty, I32Ty}, false);
+  return Function::Create(FTy, GlobalValue::ExternalLinkage, Name, &M);
+}
+
+TEST(InstructionDeletionListenerTest, EraseFromParent) {
+  LLVMContext C;
+  Module M("test", C);
+  Function *F = createFunction(M, "f");
+  BasicBlock *BB = BasicBlock::Create(C, "entry", F);
+  IRBuilder<> Builder(BB);
+
+  Instruction *Add =
+      cast<Instruction>(Builder.CreateAdd(F->getArg(0), F->getArg(1)));
+  Builder.CreateRet(F->getArg(0));
+
+  DenseSet<const Value *> UniformValues;
+  UniformValues.insert(Add);
+
+  TrackingListener Listener(*F, UniformValues);
+
+  EXPECT_TRUE(UniformValues.contains(Add));
+  Add->eraseFromParent();
+  EXPECT_FALSE(UniformValues.contains(Add));
+}
+
+TEST(InstructionDeletionListenerTest, RemoveAndDelete) {
+  LLVMContext C;
+  Module M("test", C);
+  Function *F = createFunction(M, "f");
+  BasicBlock *BB = BasicBlock::Create(C, "entry", F);
+  IRBuilder<> Builder(BB);
+
+  Instruction *Add =
+      cast<Instruction>(Builder.CreateAdd(F->getArg(0), F->getArg(1)));
+  Builder.CreateRet(F->getArg(0));
+
+  DenseSet<const Value *> UniformValues;
+  UniformValues.insert(Add);
+
+  TrackingListener Listener(*F, UniformValues);
+
+  EXPECT_TRUE(UniformValues.contains(Add));
+  Add->removeFromParent();
+  EXPECT_FALSE(UniformValues.contains(Add));
+  Add->deleteValue();
+}
+
+TEST(InstructionDeletionListenerTest, BasicBlockErasure) {
+  LLVMContext C;
+  Module M("test", C);
+  Function *F = createFunction(M, "f");
+  BasicBlock *Entry = BasicBlock::Create(C, "entry", F);
+  BasicBlock *Dead = BasicBlock::Create(C, "dead", F);
+  IRBuilder<> Builder(Entry);
+  Builder.CreateRet(F->getArg(0));
+
+  Builder.SetInsertPoint(Dead);
+  Instruction *Add =
+      cast<Instruction>(Builder.CreateAdd(F->getArg(0), F->getArg(1)));
+  Builder.CreateUnreachable();
+
+  DenseSet<const Value *> UniformValues;
+  UniformValues.insert(Add);
+
+  TrackingListener Listener(*F, UniformValues);
+
+  EXPECT_TRUE(UniformValues.contains(Add));
+  Dead->eraseFromParent();
+  EXPECT_FALSE(UniformValues.contains(Add));
+}
+
+TEST(InstructionDeletionListenerTest, ListenerScopeRAII) {
+  LLVMContext C;
+  Module M("test", C);
+  Function *F = createFunction(M, "f");
+  BasicBlock *BB = BasicBlock::Create(C, "entry", F);
+  IRBuilder<> Builder(BB);
+  Builder.CreateRet(F->getArg(0));
+
+  EXPECT_FALSE(F->hasInstructionDeletionListeners());
+  {
+    DenseSet<const Value *> UniformValues;
+    TrackingListener Listener(*F, UniformValues);
+    EXPECT_TRUE(F->hasInstructionDeletionListeners());
+  }
+  EXPECT_FALSE(F->hasInstructionDeletionListeners());
+}
+
+TEST(InstructionDeletionListenerTest, MultipleListeners) {
+  LLVMContext C;
+  Module M("test", C);
+  Function *F = createFunction(M, "f");
+  BasicBlock *BB = BasicBlock::Create(C, "entry", F);
+  IRBuilder<> Builder(BB);
+
+  Instruction *Add =
+      cast<Instruction>(Builder.CreateAdd(F->getArg(0), F->getArg(1)));
+  Builder.CreateRet(F->getArg(0));
+
+  DenseSet<const Value *> UniformValues1;
+  DenseSet<const Value *> UniformValues2;
+  UniformValues1.insert(Add);
+  UniformValues2.insert(Add);
+
+  TrackingListener L1(*F, UniformValues1);
+  TrackingListener L2(*F, UniformValues2);
+
+  EXPECT_TRUE(UniformValues1.contains(Add));
+  EXPECT_TRUE(UniformValues2.contains(Add));
+  Add->eraseFromParent();
+  EXPECT_FALSE(UniformValues1.contains(Add));
+  EXPECT_FALSE(UniformValues2.contains(Add));
+}
+
+TEST(InstructionDeletionListenerTest, PerFunctionIsolation) {
+  LLVMContext C;
+  Module M("test", C);
+  Function *F1 = createFunction(M, "f1");
+  Function *F2 = createFunction(M, "f2");
+
+  BasicBlock *BB1 = BasicBlock::Create(C, "entry", F1);
+  BasicBlock *BB2 = BasicBlock::Create(C, "entry", F2);
+
+  IRBuilder<> B1(BB1);
+  Instruction *Add1 =
+      cast<Instruction>(B1.CreateAdd(F1->getArg(0), F1->getArg(1)));
+  B1.CreateRet(F1->getArg(0));
+
+  IRBuilder<> B2(BB2);
+  Instruction *Add2 =
+      cast<Instruction>(B2.CreateAdd(F2->getArg(0), F2->getArg(1)));
+  B2.CreateRet(F2->getArg(0));
+
+  DenseSet<const Value *> UniformValues1;
+  DenseSet<const Value *> UniformValues2;
+  UniformValues1.insert(Add1);
+  UniformValues2.insert(Add2);
+
+  TrackingListener L1(*F1, UniformValues1);
+  TrackingListener L2(*F2, UniformValues2);
+
+  // Erasing from F1 should not affect F2's listener.
+  Add1->eraseFromParent();
+  EXPECT_FALSE(UniformValues1.contains(Add1));
+  EXPECT_TRUE(UniformValues2.contains(Add2));
+
+  Add2->eraseFromParent();
+  EXPECT_FALSE(UniformValues2.contains(Add2));
+}
+
+} // end anonymous namespace
diff --git a/llvm/unittests/IR/ValueDeletionListenerTest.cpp b/llvm/unittests/IR/ValueDeletionListenerTest.cpp
deleted file mode 100644
index 0332b95550a82..0000000000000
--- a/llvm/unittests/IR/ValueDeletionListenerTest.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-//===- ValueDeletionListenerTest.cpp - Tests for ValueDeletionListener ----===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "llvm/IR/ValueDeletionListener.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/IR/Constants.h"
-#include "llvm/IR/Instructions.h"
-#include "llvm/IR/LLVMContext.h"
-#include "gtest/gtest.h"
-#include <memory>
-
-using namespace llvm;
-
-namespace {
-
-class TrackingListener : public ValueDeletionListener {
-  DenseSet<const Value *> &Tracked;
-
-  static void callback(ValueDeletionListener *Self, const Value *V) {
-    static_cast<TrackingListener *>(Self)->Tracked.erase(V);
-  }
-
-public:
-  TrackingListener(LLVMContext &C, DenseSet<const Value *> &S)
-      : ValueDeletionListener(C, &callback), Tracked(S) {}
-};
-
-TEST(ValueDeletionListenerTest, BasicDeletion) {
-  LLVMContext C;
-  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
-
-  DenseSet<const Value *> Tracked;
-  TrackingListener Listener(C, Tracked);
-
-  std::unique_ptr<BitCastInst> V(
-      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
-  Tracked.insert(V.get());
-  EXPECT_TRUE(Tracked.contains(V.get()));
-
-  // Deleting the value triggers the listener, removing it from the set.
-  V.reset();
-  EXPECT_TRUE(Tracked.empty());
-}
-
-TEST(ValueDeletionListenerTest, AddressReuse) {
-  LLVMContext C;
-  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
-
-  DenseSet<const Value *> Tracked;
-  TrackingListener Listener(C, Tracked);
-
-  // Create, track, and destroy a value.
-  const Value *OldAddr;
-  {
-    std::unique_ptr<BitCastInst> V1(
-        new BitCastInst(ConstantV, Type::getInt32Ty(C)));
-    OldAddr = V1.get();
-    Tracked.insert(OldAddr);
-    EXPECT_TRUE(Tracked.contains(OldAddr));
-  } // V1 destroyed here — listener removes it from Tracked.
-  EXPECT_FALSE(Tracked.contains(OldAddr));
-
-  // Allocate a new instruction. Even if the allocator reuses the same
-  // address, the set must not contain it.
-  std::unique_ptr<BitCastInst> V2(
-      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
-  EXPECT_FALSE(Tracked.contains(V2.get()));
-}
-
-TEST(ValueDeletionListenerTest, ListenerScopeRAII) {
-  LLVMContext C;
-  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
-
-  DenseSet<const Value *> Tracked;
-
-  std::unique_ptr<BitCastInst> V(
-      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
-  Tracked.insert(V.get());
-
-  {
-    TrackingListener Listener(C, Tracked);
-    EXPECT_TRUE(Tracked.contains(V.get()));
-  }
-  // Listener is destroyed (unregistered). Deleting the value now must not
-  // crash, but the set won't be updated since no listener is active.
-  const Value *Addr = V.get();
-  V.reset();
-  EXPECT_TRUE(Tracked.contains(Addr));
-}
-
-TEST(ValueDeletionListenerTest, MultipleListeners) {
-  LLVMContext C;
-  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
-
-  DenseSet<const Value *> Set1, Set2;
-  TrackingListener L1(C, Set1);
-  TrackingListener L2(C, Set2);
-
-  std::unique_ptr<BitCastInst> V(
-      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
-  Set1.insert(V.get());
-  Set2.insert(V.get());
-
-  V.reset();
-  EXPECT_TRUE(Set1.empty());
-  EXPECT_TRUE(Set2.empty());
-}
-
-TEST(ValueDeletionListenerTest, UnrelatedValueNotAffected) {
-  LLVMContext C;
-  Constant *ConstantV = ConstantInt::get(Type::getInt32Ty(C), 0);
-
-  DenseSet<const Value *> Tracked;
-  TrackingListener Listener(C, Tracked);
-
-  std::unique_ptr<BitCastInst> V1(
-      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
-  std::unique_ptr<BitCastInst> V2(
-      new BitCastInst(ConstantV, Type::getInt32Ty(C)));
-  Tracked.insert(V1.get());
-
-  // Deleting V2 (not tracked) should not affect V1 in the set.
-  V2.reset();
-  EXPECT_TRUE(Tracked.contains(V1.get()));
-}
-
-} // namespace



More information about the llvm-commits mailing list