[Analysis] Replace SCEVCallbackVH with per-function InstructionListener - #196485
[Analysis] Replace SCEVCallbackVH with per-function InstructionListener#196485PankajDwivedi-25 wants to merge 3 commits into
Conversation
|
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-llvm-ir Author: Pankaj Dwivedi (PankajDwivedi-25) ChangesThis replaces the per-value Instead of creating one CallbackVH per cached value in Changes:
Built on the InstructionListener infrastructure introduced in PR #193198. This is an experimental patch as per discussion in RFC: https://discourse.llvm.org/t/rfc-valuedeletionlistener-context-level-value-deletion-notifications/90624/23 Patch is 30.80 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/196485.diff 11 Files Affected:
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 5c01da0855f66..0926418af4bae 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -31,6 +31,7 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/ConstantRange.h"
+#include "llvm/IR/InstructionListener.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/ValueHandle.h"
@@ -1619,19 +1620,19 @@ class ScalarEvolution {
};
private:
- /// A CallbackVH to arrange for ScalarEvolution to be notified whenever a
- /// Value is deleted.
- class LLVM_ABI SCEVCallbackVH final : public CallbackVH {
+ /// A per-function listener that invalidates SCEV caches when an instruction
+ /// is removed or RAUW'd. Replaces per-value SCEVCallbackVH handles.
+ class SCEVInstructionListener : public InstructionListener {
ScalarEvolution *SE;
- void deleted() override;
- void allUsesReplacedWith(Value *New) override;
+ static void onRemoved(InstructionListener *Self, Instruction *I);
+ static void onRAUW(InstructionListener *Self, Instruction *Old, Value *New);
public:
- SCEVCallbackVH(Value *V, ScalarEvolution *SE = nullptr);
+ SCEVInstructionListener(Function &F, ScalarEvolution *SE)
+ : InstructionListener(F, &onRemoved, &onRAUW), SE(SE) {}
};
- friend class SCEVCallbackVH;
friend class SCEVExpander;
friend class SCEVUnknown;
@@ -1676,12 +1677,14 @@ class ScalarEvolution {
ExprValueMapType ExprValueMap;
/// The type for ValueExprMap.
- using ValueExprMapType =
- DenseMap<SCEVCallbackVH, const SCEV *, DenseMapInfo<Value *>>;
+ using ValueExprMapType = DenseMap<Value *, const SCEV *>;
/// This is a cache of the values we have analyzed so far.
ValueExprMapType ValueExprMap;
+ /// Listener that invalidates SCEV caches on instruction removal/RAUW.
+ SCEVInstructionListener InstListener;
+
/// This is a cache for expressions that got folded to a different existing
/// SCEV.
DenseMap<FoldID, const SCEV *> FoldCache;
diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index f39fe509a49a4..c846fd5d55f31 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 InstructionListener;
class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
public:
@@ -112,7 +114,24 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
friend class SymbolTableListTraits<Function>;
+ friend class InstructionListener;
+ SmallVector<InstructionListener *, 0> InstructionListeners;
+
+ void addInstructionListener(InstructionListener *L);
+ void removeInstructionListener(InstructionListener *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);
+
+ /// Notify all registered listeners that an instruction in this function is
+ /// being RAUW'd (replaced with another value). Called from Value::doRAUW().
+ LLVM_ABI void notifyInstructionRAUW(Instruction *Old, Value *New);
+
+ bool hasInstructionListeners() const { return !InstructionListeners.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..7420e6d0cd357 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
+ /// InstructionListeners 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/InstructionListener.h b/llvm/include/llvm/IR/InstructionListener.h
new file mode 100644
index 0000000000000..093bace4c1bef
--- /dev/null
+++ b/llvm/include/llvm/IR/InstructionListener.h
@@ -0,0 +1,73 @@
+//===- llvm/IR/InstructionListener.h - Per-function instruction listener --===//
+//
+// 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 InstructionListener, a per-function interface that
+// notifies analyses when an Instruction is removed from a Function or
+// RAUW'd (replaced with another value). Think of it 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_INSTRUCTIONLISTENER_H
+#define LLVM_IR_INSTRUCTIONLISTENER_H
+
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+class Function;
+class Instruction;
+class Value;
+
+/// A per-function listener notified when an Instruction is removed from its
+/// parent BasicBlock or when an Instruction is RAUW'd.
+///
+/// Subclasses implement the callbacks by passing static functions 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 InstructionListener {
+public:
+ using CallbackT = void (*)(InstructionListener *, Instruction *);
+ using RAUWCallbackT = void (*)(InstructionListener *, Instruction *Old,
+ Value *New);
+
+private:
+ Function *F;
+ CallbackT Callback;
+ RAUWCallbackT RAUWCallback;
+
+public:
+ LLVM_ABI InstructionListener(Function &F, CallbackT CB,
+ RAUWCallbackT RAUWCB = nullptr);
+ LLVM_ABI ~InstructionListener();
+
+ InstructionListener(const InstructionListener &) = delete;
+ InstructionListener &operator=(const InstructionListener &) = delete;
+
+ /// Called by ~Function() to detach the listener before the Function is
+ /// destroyed. After this call, the destructor becomes a no-op.
+ void detach() { F = nullptr; }
+
+ void instructionRemoved(Instruction *I) { Callback(this, I); }
+ void instructionRAUW(Instruction *Old, Value *New) {
+ if (RAUWCallback)
+ RAUWCallback(this, Old, New);
+ }
+ Function *getFunction() const { return F; }
+};
+
+} // namespace llvm
+
+#endif // LLVM_IR_INSTRUCTIONLISTENER_H
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 9f362deb7cca9..6b57a0dd53a15 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -4695,7 +4695,7 @@ ArrayRef<Value *> ScalarEvolution::getSCEVValues(const SCEV *S) {
/// cannot be used separately. eraseValueFromMap should be used to remove
/// V from ValueExprMap and ExprValueMap at the same time.
void ScalarEvolution::eraseValueFromMap(Value *V) {
- ValueExprMapType::iterator I = ValueExprMap.find_as(V);
+ ValueExprMapType::iterator I = ValueExprMap.find(V);
if (I != ValueExprMap.end()) {
auto EVIt = ExprValueMap.find(I->second);
bool Removed = EVIt->second.remove(V);
@@ -4709,9 +4709,9 @@ void ScalarEvolution::insertValueToMap(Value *V, const SCEV *S) {
// A recursive query may have already computed the SCEV. It should be
// equivalent, but may not necessarily be exactly the same, e.g. due to lazily
// inferred nowrap flags.
- auto It = ValueExprMap.find_as(V);
+ auto It = ValueExprMap.find(V);
if (It == ValueExprMap.end()) {
- ValueExprMap.insert({SCEVCallbackVH(V, this), S});
+ ValueExprMap.insert({V, S});
ExprValueMap[S].insert(V);
}
}
@@ -4729,7 +4729,7 @@ const SCEV *ScalarEvolution::getSCEV(Value *V) {
const SCEV *ScalarEvolution::getExistingSCEV(Value *V) {
assert(isSCEVable(V->getType()) && "Value is not SCEVable!");
- ValueExprMapType::iterator I = ValueExprMap.find_as(V);
+ ValueExprMapType::iterator I = ValueExprMap.find(V);
if (I != ValueExprMap.end()) {
const SCEV *S = I->second;
assert(checkValidity(S) &&
@@ -5966,7 +5966,7 @@ const SCEV *ScalarEvolution::createAddRecFromPHI(PHINode *PN) {
if (!BEValueV || !StartValueV)
return nullptr;
- assert(ValueExprMap.find_as(PN) == ValueExprMap.end() &&
+ assert(ValueExprMap.find(PN) == ValueExprMap.end() &&
"PHI node already processed?");
// First, try to find AddRec expression without creating a fictituos symbolic
@@ -8752,8 +8752,7 @@ void ScalarEvolution::visitAndClearUsers(
if (!isSCEVable(I->getType()) && !isa<WithOverflowInst>(I))
continue;
- ValueExprMapType::iterator It =
- ValueExprMap.find_as(static_cast<Value *>(I));
+ ValueExprMapType::iterator It = ValueExprMap.find(static_cast<Value *>(I));
if (It != ValueExprMap.end()) {
eraseValueFromMap(It->first);
ToForget.push_back(It->second);
@@ -13941,30 +13940,24 @@ const SCEV *ScalarEvolution::getElementSize(Instruction *Inst) {
}
//===----------------------------------------------------------------------===//
-// SCEVCallbackVH Class Implementation
+// SCEVInstructionListener Implementation
//===----------------------------------------------------------------------===//
-void ScalarEvolution::SCEVCallbackVH::deleted() {
- assert(SE && "SCEVCallbackVH called with a null ScalarEvolution!");
- if (PHINode *PN = dyn_cast<PHINode>(getValPtr()))
+void ScalarEvolution::SCEVInstructionListener::onRemoved(
+ InstructionListener *Self, Instruction *I) {
+ ScalarEvolution *SE = static_cast<SCEVInstructionListener *>(Self)->SE;
+ if (PHINode *PN = dyn_cast<PHINode>(I))
SE->ConstantEvolutionLoopExitValue.erase(PN);
- SE->eraseValueFromMap(getValPtr());
- // this now dangles!
+ SE->eraseValueFromMap(I);
}
-void ScalarEvolution::SCEVCallbackVH::allUsesReplacedWith(Value *V) {
- assert(SE && "SCEVCallbackVH called with a null ScalarEvolution!");
-
- // Forget all the expressions associated with users of the old value,
- // so that future queries will recompute the expressions using the new
- // value.
- SE->forgetValue(getValPtr());
- // this now dangles!
+void ScalarEvolution::SCEVInstructionListener::onRAUW(InstructionListener *Self,
+ Instruction *Old,
+ Value *) {
+ ScalarEvolution *SE = static_cast<SCEVInstructionListener *>(Self)->SE;
+ SE->forgetValue(Old);
}
-ScalarEvolution::SCEVCallbackVH::SCEVCallbackVH(Value *V, ScalarEvolution *se)
- : CallbackVH(V), SE(se) {}
-
//===----------------------------------------------------------------------===//
// ScalarEvolution Class Implementation
//===----------------------------------------------------------------------===//
@@ -13973,8 +13966,8 @@ ScalarEvolution::ScalarEvolution(Function &F, TargetLibraryInfo &TLI,
AssumptionCache &AC, DominatorTree &DT,
LoopInfo &LI)
: F(F), DL(F.getDataLayout()), TLI(TLI), AC(AC), DT(DT), LI(LI),
- CouldNotCompute(new SCEVCouldNotCompute()), ValuesAtScopes(64),
- LoopDispositions(64), BlockDispositions(64) {
+ CouldNotCompute(new SCEVCouldNotCompute()), InstListener(F, this),
+ ValuesAtScopes(64), LoopDispositions(64), BlockDispositions(64) {
// To use guards for proving predicates, we need to scan every instruction in
// relevant basic blocks, and not just terminators. Doing this is a waste of
// time if the IR does not actually contain any calls to
@@ -13993,7 +13986,7 @@ ScalarEvolution::ScalarEvolution(Function &F, TargetLibraryInfo &TLI,
ScalarEvolution::ScalarEvolution(ScalarEvolution &&Arg)
: F(Arg.F), DL(Arg.DL), HasGuards(Arg.HasGuards), TLI(Arg.TLI), AC(Arg.AC),
DT(Arg.DT), LI(Arg.LI), CouldNotCompute(std::move(Arg.CouldNotCompute)),
- ValueExprMap(std::move(Arg.ValueExprMap)),
+ ValueExprMap(std::move(Arg.ValueExprMap)), InstListener(F, this),
PendingLoopPredicates(std::move(Arg.PendingLoopPredicates)),
PendingMerges(std::move(Arg.PendingMerges)),
ConstantMultipleCache(std::move(Arg.ConstantMultipleCache)),
@@ -14585,7 +14578,7 @@ void ScalarEvolution::forgetMemoizedResultsImpl(const SCEV *S) {
auto ExprIt = ExprValueMap.find(S);
if (ExprIt != ExprValueMap.end()) {
for (Value *V : ExprIt->second) {
- auto ValueIt = ValueExprMap.find_as(V);
+ auto ValueIt = ValueExprMap.find(V);
if (ValueIt != ValueExprMap.end())
ValueExprMap.erase(ValueIt);
}
diff --git a/llvm/lib/IR/BasicBlock.cpp b/llvm/lib/IR/BasicBlock.cpp
index da97b26f7cec5..c2afe8139e078 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,6 +196,11 @@ BasicBlock::~BasicBlock() {
}
void BasicBlock::setParent(Function *parent) {
+ // Notify per-function listeners when BB is removed from its parent function.
+ if (!parent && Parent && Parent->hasInstructionListeners()) {
+ for (Instruction &I : *this)
+ Parent->notifyInstructionRemoved(&I);
+ }
// Set Parent=parent, updating instruction symtab entries as appropriate.
if (Parent != parent)
Number = parent ? parent->NextBlockNum++ : -1u;
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index a6568bb50f0c8..68c1d10f1d40e 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/InstructionListener.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/LLVMContext.h"
@@ -62,6 +63,37 @@ using ProfileCount = Function::ProfileCount;
// are not in the public header file...
template class LLVM_EXPORT_TEMPLATE llvm::SymbolTableListTraits<BasicBlock>;
+InstructionListener::InstructionListener(Function &F, CallbackT CB,
+ RAUWCallbackT RAUWCB)
+ : F(&F), Callback(CB), RAUWCallback(RAUWCB) {
+ F.addInstructionListener(this);
+}
+
+InstructionListener::~InstructionListener() {
+ if (F)
+ F->removeInstructionListener(this);
+}
+
+void Function::addInstructionListener(InstructionListener *L) {
+ assert(!llvm::is_contained(InstructionListeners, L) &&
+ "Listener already registered");
+ InstructionListeners.push_back(L);
+}
+
+void Function::removeInstructionListener(InstructionListener *L) {
+ InstructionListeners.erase(llvm::find(InstructionListeners, L));
+}
+
+void Function::notifyInstructionRemoved(Instruction *I) {
+ for (InstructionListener *L : InstructionListeners)
+ L->instructionRemoved(I);
+}
+
+void Function::notifyInstructionRAUW(Instruction *Old, Value *New) {
+ for (InstructionListener *L : InstructionListeners)
+ L->instructionRAUW(Old, New);
+}
+
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."));
@@ -516,6 +548,12 @@ Function::Function(FunctionType *Ty, LinkageTypes Linkage, unsigned AddrSpace,
}
Function::~Function() {
+ // Detach all instruction listeners before destruction so their destructors
+ // don't try to call back into this Function.
+ for (InstructionListener *L : InstructionListeners)
+ L->detach();
+ InstructionListeners.clear();
+
validateBlockNumbers();
dropAllReferences(); // After this it is safe to delete instructions.
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/Value.cpp b/llvm/lib/IR/Value.cpp
index 360bf0f8fc47f..3ea087692a777 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -525,6 +525,14 @@ void Value::doRAUW(Value *New, ReplaceMetadataUses ReplaceMetaUses) {
// Notify all ValueHandles (if present) that this value is going away.
if (HasValueHandle)
ValueHandleBase::ValueIsRAUWd(this, New);
+
+ // Notify per-function listeners when an instruction is RAUW'd.
+ if (Instruction *I = dyn_cast<Instruction>(this))
+ if (BasicBlock *BB = I->getParent())
+ if (Function *F = BB->getParent())
+ if (F->hasInstructionListeners())
+ F->notifyInstructionRAUW(I, New);
+
if (ReplaceMetaUses == ReplaceMetadataUses::Yes && isUsedByMetadata())
ValueAsMetadata::handleRAUW(this, New);
diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt
index d62ce66ef9d34..92b082f82690e 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
+ InstructionListenerTest.cpp
InstructionsTest.cpp
IntrinsicsTest.cpp
LegacyPassManagerTest.cpp
diff --git a/llvm/unittests/IR/InstructionListenerTest.cpp b/llvm/unittests/IR/InstructionListenerTest.cpp
new file mode 100644
index 0000000000000..a6b4f53b31580
--- /dev/null
+++ b/llvm/unittests/IR/InstructionListenerTest.cpp
@@ -0,0 +1,380 @@
+//===- InstructionListenerTest.cpp - per-Function instruction listener ----===//
+//
+// 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/InstructionListener.h"
+#include "llvm/ADT/DenseMap.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 InstructionListener {
+ DenseSet<const Value *> &UniformValuesRef;
+
+ static void onRemoved(InstructionList...
[truncated]
|
|
@nikic, could you please add my fork to the compile-time tracker? |
8fd5c75 to
c0b0552
Compare
Done. Note that it will test all commits on the branch, so it's usually best to not use |
thanks, Yes this is just experimental branch. |
25e8d92 to
fb72448
Compare
fb72448 to
8faa457
Compare
This replaces the per-value
SCEVCallbackVHmechanism in ScalarEvolutionwith a single
InstructionListenerregistration per function.Instead of creating one CallbackVH per cached value in
ValueExprMap(O(N) handles, each with linked-list and DenseMap entries in LLVMContext),
a single listener handles deletion and RAUW notifications for all
instructions in the function.
Changes:
SCEVCallbackVHclassValueExprMapkey fromSCEVCallbackVHto plainValue*SCEVInstructionListenerthat callseraseValueFromMap()ondeletion and
forgetValue()on RAUWdetach()in~Function()to handle safe destruction orderingwhen module passes delete functions
SCEVUnknownretains itsCallbackVH(wraps arbitrary Values includingArguments, outside InstructionListener's scope).
Built on the InstructionListener infrastructure introduced in PR #193198.
NOTE: This is an experimental patch as per discussion in RFC: https://discourse.llvm.org/t/rfc-valuedeletionlistener-context-level-value-deletion-notifications/90624/23
CTT: https://llvm-compile-time-tracker.com/compare.php?stat=instructions%3Au&from=df6e1f485683dc9050d853bef510da012816b4a7&to=c0b055259f914d992a996c7e9dbafac5129b27a7