ScopedHashTable.h 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. #pragma once
  2. #ifdef __GNUC__
  3. #pragma GCC diagnostic push
  4. #pragma GCC diagnostic ignored "-Wunused-parameter"
  5. #endif
  6. //===- ScopedHashTable.h - A simple scoped hash table -----------*- C++ -*-===//
  7. //
  8. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  9. // See https://llvm.org/LICENSE.txt for license information.
  10. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  11. //
  12. //===----------------------------------------------------------------------===//
  13. //
  14. // This file implements an efficient scoped hash table, which is useful for
  15. // things like dominator-based optimizations. This allows clients to do things
  16. // like this:
  17. //
  18. // ScopedHashTable<int, int> HT;
  19. // {
  20. // ScopedHashTableScope<int, int> Scope1(HT);
  21. // HT.insert(0, 0);
  22. // HT.insert(1, 1);
  23. // {
  24. // ScopedHashTableScope<int, int> Scope2(HT);
  25. // HT.insert(0, 42);
  26. // }
  27. // }
  28. //
  29. // Looking up the value for "0" in the Scope2 block will return 42. Looking
  30. // up the value for 0 before 42 is inserted or after Scope2 is popped will
  31. // return 0.
  32. //
  33. //===----------------------------------------------------------------------===//
  34. #ifndef LLVM_ADT_SCOPEDHASHTABLE_H
  35. #define LLVM_ADT_SCOPEDHASHTABLE_H
  36. #include "llvm/ADT/DenseMap.h"
  37. #include "llvm/ADT/DenseMapInfo.h"
  38. #include "llvm/Support/AllocatorBase.h"
  39. #include <cassert>
  40. #include <new>
  41. namespace llvm {
  42. template <typename K, typename V, typename KInfo = DenseMapInfo<K>,
  43. typename AllocatorTy = MallocAllocator>
  44. class ScopedHashTable;
  45. template <typename K, typename V>
  46. class ScopedHashTableVal {
  47. ScopedHashTableVal *NextInScope;
  48. ScopedHashTableVal *NextForKey;
  49. K Key;
  50. V Val;
  51. ScopedHashTableVal(const K &key, const V &val) : Key(key), Val(val) {}
  52. public:
  53. const K &getKey() const { return Key; }
  54. const V &getValue() const { return Val; }
  55. V &getValue() { return Val; }
  56. ScopedHashTableVal *getNextForKey() { return NextForKey; }
  57. const ScopedHashTableVal *getNextForKey() const { return NextForKey; }
  58. ScopedHashTableVal *getNextInScope() { return NextInScope; }
  59. template <typename AllocatorTy>
  60. static ScopedHashTableVal *Create(ScopedHashTableVal *nextInScope,
  61. ScopedHashTableVal *nextForKey,
  62. const K &key, const V &val,
  63. AllocatorTy &Allocator) {
  64. ScopedHashTableVal *New = Allocator.template Allocate<ScopedHashTableVal>();
  65. // Set up the value.
  66. new (New) ScopedHashTableVal(key, val);
  67. New->NextInScope = nextInScope;
  68. New->NextForKey = nextForKey;
  69. return New;
  70. }
  71. template <typename AllocatorTy> void Destroy(AllocatorTy &Allocator) {
  72. // Free memory referenced by the item.
  73. this->~ScopedHashTableVal();
  74. Allocator.Deallocate(this);
  75. }
  76. };
  77. template <typename K, typename V, typename KInfo = DenseMapInfo<K>,
  78. typename AllocatorTy = MallocAllocator>
  79. class ScopedHashTableScope {
  80. /// HT - The hashtable that we are active for.
  81. ScopedHashTable<K, V, KInfo, AllocatorTy> &HT;
  82. /// PrevScope - This is the scope that we are shadowing in HT.
  83. ScopedHashTableScope *PrevScope;
  84. /// LastValInScope - This is the last value that was inserted for this scope
  85. /// or null if none have been inserted yet.
  86. ScopedHashTableVal<K, V> *LastValInScope;
  87. public:
  88. ScopedHashTableScope(ScopedHashTable<K, V, KInfo, AllocatorTy> &HT);
  89. ScopedHashTableScope(ScopedHashTableScope &) = delete;
  90. ScopedHashTableScope &operator=(ScopedHashTableScope &) = delete;
  91. ~ScopedHashTableScope();
  92. ScopedHashTableScope *getParentScope() { return PrevScope; }
  93. const ScopedHashTableScope *getParentScope() const { return PrevScope; }
  94. private:
  95. friend class ScopedHashTable<K, V, KInfo, AllocatorTy>;
  96. ScopedHashTableVal<K, V> *getLastValInScope() {
  97. return LastValInScope;
  98. }
  99. void setLastValInScope(ScopedHashTableVal<K, V> *Val) {
  100. LastValInScope = Val;
  101. }
  102. };
  103. template <typename K, typename V, typename KInfo = DenseMapInfo<K>>
  104. class ScopedHashTableIterator {
  105. ScopedHashTableVal<K, V> *Node;
  106. public:
  107. ScopedHashTableIterator(ScopedHashTableVal<K, V> *node) : Node(node) {}
  108. V &operator*() const {
  109. assert(Node && "Dereference end()");
  110. return Node->getValue();
  111. }
  112. V *operator->() const {
  113. return &Node->getValue();
  114. }
  115. bool operator==(const ScopedHashTableIterator &RHS) const {
  116. return Node == RHS.Node;
  117. }
  118. bool operator!=(const ScopedHashTableIterator &RHS) const {
  119. return Node != RHS.Node;
  120. }
  121. inline ScopedHashTableIterator& operator++() { // Preincrement
  122. assert(Node && "incrementing past end()");
  123. Node = Node->getNextForKey();
  124. return *this;
  125. }
  126. ScopedHashTableIterator operator++(int) { // Postincrement
  127. ScopedHashTableIterator tmp = *this; ++*this; return tmp;
  128. }
  129. };
  130. template <typename K, typename V, typename KInfo, typename AllocatorTy>
  131. class ScopedHashTable {
  132. public:
  133. /// ScopeTy - This is a helpful typedef that allows clients to get easy access
  134. /// to the name of the scope for this hash table.
  135. using ScopeTy = ScopedHashTableScope<K, V, KInfo, AllocatorTy>;
  136. using size_type = unsigned;
  137. private:
  138. friend class ScopedHashTableScope<K, V, KInfo, AllocatorTy>;
  139. using ValTy = ScopedHashTableVal<K, V>;
  140. DenseMap<K, ValTy*, KInfo> TopLevelMap;
  141. ScopeTy *CurScope = nullptr;
  142. AllocatorTy Allocator;
  143. public:
  144. ScopedHashTable() = default;
  145. ScopedHashTable(AllocatorTy A) : Allocator(A) {}
  146. ScopedHashTable(const ScopedHashTable &) = delete;
  147. ScopedHashTable &operator=(const ScopedHashTable &) = delete;
  148. ~ScopedHashTable() {
  149. assert(!CurScope && TopLevelMap.empty() && "Scope imbalance!");
  150. }
  151. /// Access to the allocator.
  152. AllocatorTy &getAllocator() { return Allocator; }
  153. const AllocatorTy &getAllocator() const { return Allocator; }
  154. /// Return 1 if the specified key is in the table, 0 otherwise.
  155. size_type count(const K &Key) const {
  156. return TopLevelMap.count(Key);
  157. }
  158. V lookup(const K &Key) const {
  159. auto I = TopLevelMap.find(Key);
  160. if (I != TopLevelMap.end())
  161. return I->second->getValue();
  162. return V();
  163. }
  164. void insert(const K &Key, const V &Val) {
  165. insertIntoScope(CurScope, Key, Val);
  166. }
  167. using iterator = ScopedHashTableIterator<K, V, KInfo>;
  168. iterator end() { return iterator(nullptr); }
  169. iterator begin(const K &Key) {
  170. typename DenseMap<K, ValTy*, KInfo>::iterator I =
  171. TopLevelMap.find(Key);
  172. if (I == TopLevelMap.end()) return end();
  173. return iterator(I->second);
  174. }
  175. ScopeTy *getCurScope() { return CurScope; }
  176. const ScopeTy *getCurScope() const { return CurScope; }
  177. /// insertIntoScope - This inserts the specified key/value at the specified
  178. /// (possibly not the current) scope. While it is ok to insert into a scope
  179. /// that isn't the current one, it isn't ok to insert *underneath* an existing
  180. /// value of the specified key.
  181. void insertIntoScope(ScopeTy *S, const K &Key, const V &Val) {
  182. assert(S && "No scope active!");
  183. ScopedHashTableVal<K, V> *&KeyEntry = TopLevelMap[Key];
  184. KeyEntry = ValTy::Create(S->getLastValInScope(), KeyEntry, Key, Val,
  185. Allocator);
  186. S->setLastValInScope(KeyEntry);
  187. }
  188. };
  189. /// ScopedHashTableScope ctor - Install this as the current scope for the hash
  190. /// table.
  191. template <typename K, typename V, typename KInfo, typename Allocator>
  192. ScopedHashTableScope<K, V, KInfo, Allocator>::
  193. ScopedHashTableScope(ScopedHashTable<K, V, KInfo, Allocator> &ht) : HT(ht) {
  194. PrevScope = HT.CurScope;
  195. HT.CurScope = this;
  196. LastValInScope = nullptr;
  197. }
  198. template <typename K, typename V, typename KInfo, typename Allocator>
  199. ScopedHashTableScope<K, V, KInfo, Allocator>::~ScopedHashTableScope() {
  200. assert(HT.CurScope == this && "Scope imbalance!");
  201. HT.CurScope = PrevScope;
  202. // Pop and delete all values corresponding to this scope.
  203. while (ScopedHashTableVal<K, V> *ThisEntry = LastValInScope) {
  204. // Pop this value out of the TopLevelMap.
  205. if (!ThisEntry->getNextForKey()) {
  206. assert(HT.TopLevelMap[ThisEntry->getKey()] == ThisEntry &&
  207. "Scope imbalance!");
  208. HT.TopLevelMap.erase(ThisEntry->getKey());
  209. } else {
  210. ScopedHashTableVal<K, V> *&KeyEntry = HT.TopLevelMap[ThisEntry->getKey()];
  211. assert(KeyEntry == ThisEntry && "Scope imbalance!");
  212. KeyEntry = ThisEntry->getNextForKey();
  213. }
  214. // Pop this value out of the scope.
  215. LastValInScope = ThisEntry->getNextInScope();
  216. // Delete this entry.
  217. ThisEntry->Destroy(HT.getAllocator());
  218. }
  219. }
  220. } // end namespace llvm
  221. #endif // LLVM_ADT_SCOPEDHASHTABLE_H
  222. #ifdef __GNUC__
  223. #pragma GCC diagnostic pop
  224. #endif