[llvm] 6252237 - [llvm][ADT] Add erase method to ScopedHashTableScope (#193516)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 29 04:56:48 PDT 2026


Author: lonely eagle
Date: 2026-04-29T19:56:43+08:00
New Revision: 6252237008b8a2cce7dc6da599cdfb74b057f410

URL: https://github.com/llvm/llvm-project/commit/6252237008b8a2cce7dc6da599cdfb74b057f410
DIFF: https://github.com/llvm/llvm-project/commit/6252237008b8a2cce7dc6da599cdfb74b057f410.diff

LOG: [llvm][ADT] Add erase method to ScopedHashTableScope (#193516)

This PR introduces an `erase` method to `ScopedHashTable`, designed to
remove the most recent value associated with a given key within the
scope stack. To support efficient deletion, the internal
`ScopedHashTableVal` structure has been refactored into a doubly linked
list, allowing the predecessor of a node to be identified in O(1) time
during removal. Fix the MLIR CSE issue
https://github.com/llvm/llvm-project/pull/191135#discussion_r3100850607.
Part of https://github.com/llvm/llvm-project/pull/193778.

Added: 
    

Modified: 
    llvm/include/llvm/ADT/ScopedHashTable.h
    llvm/unittests/ADT/ScopedHashTableTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/ScopedHashTable.h b/llvm/include/llvm/ADT/ScopedHashTable.h
index 97d15463fdfd7..cc977012b2394 100644
--- a/llvm/include/llvm/ADT/ScopedHashTable.h
+++ b/llvm/include/llvm/ADT/ScopedHashTable.h
@@ -46,6 +46,7 @@ template <typename K, typename V>
 class ScopedHashTableVal {
   ScopedHashTableVal *NextInScope;
   ScopedHashTableVal *NextForKey;
+  ScopedHashTableVal *PreInScope;
   K Key;
   V Val;
 
@@ -59,6 +60,7 @@ class ScopedHashTableVal {
   ScopedHashTableVal *getNextForKey() { return NextForKey; }
   const ScopedHashTableVal *getNextForKey() const { return NextForKey; }
   ScopedHashTableVal *getNextInScope() { return NextInScope; }
+  ScopedHashTableVal *getPreInScope() { return PreInScope; }
 
   template <typename AllocatorTy>
   static ScopedHashTableVal *Create(ScopedHashTableVal *nextInScope,
@@ -70,6 +72,9 @@ class ScopedHashTableVal {
     new (New) ScopedHashTableVal(key, val);
     New->NextInScope = nextInScope;
     New->NextForKey = nextForKey;
+    New->PreInScope = nullptr;
+    if (nextInScope)
+      nextInScope->PreInScope = New;
     return New;
   }
 
@@ -78,6 +83,20 @@ class ScopedHashTableVal {
     this->~ScopedHashTableVal();
     Allocator.Deallocate(this);
   }
+
+  template <typename AllocatorTy>
+  static void erase(ScopedHashTableVal<K, V> *&ThisEntry,
+                    AllocatorTy &Allocator) {
+    ScopedHashTableVal<K, V> *ToDestroy = ThisEntry;
+    ScopedHashTableVal<K, V> *NextInScope = ThisEntry->NextInScope;
+    ScopedHashTableVal<K, V> *PrevInScope = ThisEntry->PreInScope;
+    if (PrevInScope)
+      PrevInScope->NextInScope = NextInScope;
+    if (NextInScope)
+      NextInScope->PreInScope = PrevInScope;
+    ThisEntry = ThisEntry->NextForKey;
+    ToDestroy->Destroy(Allocator);
+  }
 };
 
 template <typename K, typename V, typename KInfo = DenseMapInfo<K>,
@@ -101,6 +120,7 @@ class ScopedHashTableScope {
 
   ScopedHashTableScope *getParentScope() { return PrevScope; }
   const ScopedHashTableScope *getParentScope() const { return PrevScope; }
+  void erase(const K &key);
 
 private:
   friend class ScopedHashTable<K, V, KInfo, AllocatorTy>;
@@ -219,6 +239,8 @@ class ScopedHashTable : detail::AllocatorHolder<AllocatorTy> {
                              getAllocator());
     S->setLastValInScope(KeyEntry);
   }
+
+  void erase(const K &key) { CurScope->erase(key); }
 };
 
 /// ScopedHashTableScope ctor - Install this as the current scope for the hash
@@ -257,6 +279,33 @@ ScopedHashTableScope<K, V, KInfo, Allocator>::~ScopedHashTableScope() {
   }
 }
 
+/// This method undoes the latest binding of the given key, effectively
+/// reverting to the previous state for that key. In the example at the
+/// beginning of this file, if we execute `HT.erase(0)` immediately after
+/// `HT.insert(0, 42);`, then the value associated with key "0" reverts to 0.
+/// This value is owned by "Scope1(HT)".
+template <typename K, typename V, typename KInfo, typename Allocator>
+void ScopedHashTableScope<K, V, KInfo, Allocator>::erase(const K &Key) {
+  auto It = HT.TopLevelMap.find(Key);
+  if (It == HT.TopLevelMap.end())
+    return;
+  ScopedHashTableVal<K, V> *&ThisEntry = It->second;
+
+  // `ThisEntry` may be the LastValInScope of a parent scope rather than the
+  // current scope. We iterate through the scope chain to find the scope
+  // that owns ThisEntry as its LastValInScope and update it accordingly.
+  auto *S = this;
+  while (S) {
+    if (ThisEntry == S->LastValInScope) {
+      S->LastValInScope = ThisEntry->getNextInScope();
+      break;
+    }
+    S = S->PrevScope;
+  }
+  if (ThisEntry->getNextForKey() == nullptr)
+    HT.TopLevelMap.erase(It);
+  ScopedHashTableVal<K, V>::erase(ThisEntry, HT.getAllocator());
+}
 } // end namespace llvm
 
 #endif // LLVM_ADT_SCOPEDHASHTABLE_H

diff  --git a/llvm/unittests/ADT/ScopedHashTableTest.cpp b/llvm/unittests/ADT/ScopedHashTableTest.cpp
index 8ce5c7cecf998..d6f32b4002dcc 100644
--- a/llvm/unittests/ADT/ScopedHashTableTest.cpp
+++ b/llvm/unittests/ADT/ScopedHashTableTest.cpp
@@ -31,6 +31,7 @@ class ScopedHashTableTest : public Test {
   static constexpr StringLiteral kLocalName = "local";
   static constexpr StringLiteral kLocalValue = "lvalue";
   static constexpr StringLiteral kLocalValue2 = "lvalue2";
+  static constexpr StringLiteral KLocalValue3 = "lvalue3";
 };
 
 TEST_F(ScopedHashTableTest, AccessWithNoActiveScope) {
@@ -151,3 +152,32 @@ TEST_F(ScopedHashTableTest, DISABLED_PopScopeOnStack) {
   // There is no way to write an assert on an assert in googletest so that we
   // mark the test case as DISABLED.
 }
+
+TEST_F(ScopedHashTableTest, EraseKeyInHashTable) {
+  using SymbolTableScopeTy = ScopedHashTable<StringRef, StringRef>::ScopeTy;
+  StringRef curTestVarA = "a";
+  StringRef curTestVarB = "b";
+  SymbolTableScopeTy funScope(symbolTable);
+  symbolTable.insert(kLocalName, kLocalValue);
+  symbolTable.insert(curTestVarA, curTestVarA);
+  symbolTable.insert(curTestVarB, curTestVarB);
+
+  SymbolTableScopeTy funScope2(symbolTable);
+  symbolTable.insert(curTestVarA, curTestVarA);
+  symbolTable.insert(kLocalName, kLocalValue2);
+  symbolTable.insert(curTestVarB, curTestVarB);
+
+  SymbolTableScopeTy funScope3(symbolTable);
+  symbolTable.insert(curTestVarA, curTestVarA);
+  symbolTable.insert(curTestVarB, curTestVarB);
+  symbolTable.insert(kLocalName, KLocalValue3);
+  SymbolTableScopeTy funScope4(symbolTable);
+
+  EXPECT_EQ(symbolTable.lookup(kLocalName), KLocalValue3);
+  symbolTable.erase(kLocalName);
+  EXPECT_EQ(symbolTable.lookup(kLocalName), kLocalValue2);
+  symbolTable.erase(kLocalName);
+  EXPECT_EQ(symbolTable.lookup(kLocalName), kLocalValue);
+  symbolTable.erase(kLocalName);
+  EXPECT_EQ(symbolTable.lookup(kLocalName), StringRef());
+}


        


More information about the llvm-commits mailing list