[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