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