Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef frontend_NameCollections_h
8 : #define frontend_NameCollections_h
9 :
10 : #include "ds/InlineTable.h"
11 : #include "frontend/NameAnalysisTypes.h"
12 : #include "js/Vector.h"
13 : #include "vm/Stack.h"
14 :
15 : namespace js {
16 : namespace frontend {
17 :
18 : // A pool of recyclable containers for use in the frontend. The Parser and
19 : // BytecodeEmitter create many maps for name analysis that are short-lived
20 : // (i.e., for the duration of parsing or emitting a lexical scope). Making
21 : // them recyclable cuts down significantly on allocator churn.
22 : template <typename RepresentativeCollection, typename ConcreteCollectionPool>
23 80 : class CollectionPool
24 : {
25 : using RecyclableCollections = Vector<void*, 32, SystemAllocPolicy>;
26 :
27 : RecyclableCollections all_;
28 : RecyclableCollections recyclable_;
29 :
30 57150 : static RepresentativeCollection* asRepresentative(void* p) {
31 57150 : return reinterpret_cast<RepresentativeCollection*>(p);
32 : }
33 :
34 129 : RepresentativeCollection* allocate() {
35 129 : size_t newAllLength = all_.length() + 1;
36 129 : if (!all_.reserve(newAllLength) || !recyclable_.reserve(newAllLength))
37 0 : return nullptr;
38 :
39 129 : RepresentativeCollection* collection = js_new<RepresentativeCollection>();
40 129 : if (collection)
41 129 : all_.infallibleAppend(collection);
42 129 : return collection;
43 : }
44 :
45 : public:
46 0 : ~CollectionPool() {
47 0 : purgeAll();
48 0 : }
49 :
50 : bool empty() const {
51 : return all_.empty();
52 : }
53 :
54 2 : void purgeAll() {
55 2 : void** end = all_.end();
56 36 : for (void** it = all_.begin(); it != end; ++it)
57 34 : js_delete(asRepresentative(*it));
58 :
59 2 : all_.clearAndFree();
60 2 : recyclable_.clearAndFree();
61 2 : }
62 :
63 : // Fallibly aquire one of the supported collection types from the pool.
64 : template <typename Collection>
65 57189 : Collection* acquire(JSContext* cx) {
66 57189 : ConcreteCollectionPool::template assertInvariants<Collection>();
67 :
68 : RepresentativeCollection* collection;
69 57189 : if (recyclable_.empty()) {
70 129 : collection = allocate();
71 129 : if (!collection)
72 0 : ReportOutOfMemory(cx);
73 : } else {
74 57060 : collection = asRepresentative(recyclable_.popCopy());
75 57060 : collection->clear();
76 : }
77 57189 : return reinterpret_cast<Collection*>(collection);
78 : }
79 :
80 : // Release a collection back to the pool.
81 : template <typename Collection>
82 57194 : void release(Collection** collection) {
83 57194 : ConcreteCollectionPool::template assertInvariants<Collection>();
84 57194 : MOZ_ASSERT(*collection);
85 :
86 : #ifdef DEBUG
87 57194 : bool ok = false;
88 : // Make sure the collection is in |all_| but not already in |recyclable_|.
89 281026 : for (void** it = all_.begin(); it != all_.end(); ++it) {
90 281026 : if (*it == *collection) {
91 57194 : ok = true;
92 57194 : break;
93 : }
94 : }
95 57194 : MOZ_ASSERT(ok);
96 751149 : for (void** it = recyclable_.begin(); it != recyclable_.end(); ++it)
97 693955 : MOZ_ASSERT(*it != *collection);
98 : #endif
99 :
100 57194 : MOZ_ASSERT(recyclable_.length() < all_.length());
101 : // Reserved in allocateFresh.
102 57194 : recyclable_.infallibleAppend(*collection);
103 57194 : *collection = nullptr;
104 57194 : }
105 : };
106 :
107 : template <typename Wrapped>
108 : struct RecyclableAtomMapValueWrapper
109 : {
110 : union {
111 : Wrapped wrapped;
112 : uint64_t dummy;
113 : };
114 :
115 141353 : static void assertInvariant() {
116 : static_assert(sizeof(Wrapped) <= sizeof(uint64_t),
117 : "Can only recycle atom maps with values smaller than uint64");
118 141353 : }
119 :
120 2112 : RecyclableAtomMapValueWrapper() {
121 2112 : assertInvariant();
122 2112 : }
123 :
124 139241 : MOZ_IMPLICIT RecyclableAtomMapValueWrapper(Wrapped w)
125 139241 : : wrapped(w)
126 : {
127 139241 : assertInvariant();
128 139241 : }
129 :
130 82255 : MOZ_IMPLICIT operator Wrapped&() {
131 82255 : return wrapped;
132 : }
133 :
134 : MOZ_IMPLICIT operator Wrapped&() const {
135 : return wrapped;
136 : }
137 :
138 99275 : Wrapped* operator->() {
139 99275 : return &wrapped;
140 : }
141 :
142 : const Wrapped* operator->() const {
143 : return &wrapped;
144 : }
145 : };
146 :
147 : template <typename MapValue>
148 : using RecyclableNameMap = InlineMap<JSAtom*,
149 : RecyclableAtomMapValueWrapper<MapValue>,
150 : 24,
151 : DefaultHasher<JSAtom*>,
152 : SystemAllocPolicy>;
153 :
154 : using DeclaredNameMap = RecyclableNameMap<DeclaredNameInfo>;
155 : using CheckTDZMap = RecyclableNameMap<MaybeCheckTDZ>;
156 : using NameLocationMap = RecyclableNameMap<NameLocation>;
157 : using AtomIndexMap = RecyclableNameMap<uint32_t>;
158 :
159 : #undef RECYCLABLE_NAME_MAP_TYPE
160 :
161 : template <typename RepresentativeTable>
162 40 : class InlineTablePool
163 : : public CollectionPool<RepresentativeTable, InlineTablePool<RepresentativeTable>>
164 : {
165 : public:
166 : template <typename Table>
167 84355 : static void assertInvariants() {
168 : static_assert(Table::SizeOfInlineEntries == RepresentativeTable::SizeOfInlineEntries,
169 : "Only tables with the same size for inline entries are usable in the pool.");
170 : static_assert(mozilla::IsPod<typename Table::Table::Entry>::value,
171 : "Only tables with POD values are usable in the pool.");
172 84355 : }
173 : };
174 :
175 : using FunctionBoxVector = Vector<FunctionBox*, 24, SystemAllocPolicy>;
176 :
177 : template <typename RepresentativeVector>
178 40 : class VectorPool : public CollectionPool<RepresentativeVector, VectorPool<RepresentativeVector>>
179 : {
180 : public:
181 : template <typename Vector>
182 30028 : static void assertInvariants() {
183 : static_assert(Vector::sMaxInlineStorage == RepresentativeVector::sMaxInlineStorage,
184 : "Only vectors with the same size for inline entries are usable in the pool.");
185 : static_assert(mozilla::IsPod<typename Vector::ElementType>::value,
186 : "Only vectors of POD values are usable in the pool.");
187 : static_assert(sizeof(typename Vector::ElementType) ==
188 : sizeof(typename RepresentativeVector::ElementType),
189 : "Only vectors with same-sized elements are usable in the pool.");
190 30028 : }
191 : };
192 :
193 0 : class NameCollectionPool
194 : {
195 : InlineTablePool<AtomIndexMap> mapPool_;
196 : VectorPool<AtomVector> vectorPool_;
197 : uint32_t activeCompilations_;
198 :
199 : public:
200 40 : NameCollectionPool()
201 40 : : activeCompilations_(0)
202 40 : { }
203 :
204 167350 : bool hasActiveCompilation() const {
205 167350 : return activeCompilations_ != 0;
206 : }
207 :
208 1902 : void addActiveCompilation() {
209 1902 : activeCompilations_++;
210 1902 : }
211 :
212 1903 : void removeActiveCompilation() {
213 1903 : MOZ_ASSERT(hasActiveCompilation());
214 1903 : activeCompilations_--;
215 1903 : }
216 :
217 : template <typename Map>
218 42175 : Map* acquireMap(JSContext* cx) {
219 42175 : MOZ_ASSERT(hasActiveCompilation());
220 42175 : return mapPool_.acquire<Map>(cx);
221 : }
222 :
223 : template <typename Map>
224 74039 : void releaseMap(Map** map) {
225 74039 : MOZ_ASSERT(hasActiveCompilation());
226 74039 : MOZ_ASSERT(map);
227 74039 : if (*map)
228 42180 : mapPool_.release(map);
229 74039 : }
230 :
231 : template <typename Vector>
232 15014 : Vector* acquireVector(JSContext* cx) {
233 15014 : MOZ_ASSERT(hasActiveCompilation());
234 15014 : return vectorPool_.acquire<Vector>(cx);
235 : }
236 :
237 : template <typename Vector>
238 34116 : void releaseVector(Vector** vec) {
239 34116 : MOZ_ASSERT(hasActiveCompilation());
240 34116 : MOZ_ASSERT(vec);
241 34116 : if (*vec)
242 15014 : vectorPool_.release(vec);
243 34116 : }
244 :
245 1 : void purge() {
246 1 : if (!hasActiveCompilation()) {
247 1 : mapPool_.purgeAll();
248 1 : vectorPool_.purgeAll();
249 : }
250 1 : }
251 : };
252 :
253 : #define POOLED_COLLECTION_PTR_METHODS(N, T) \
254 : NameCollectionPool& pool_; \
255 : T* collection_; \
256 : \
257 : T& collection() { \
258 : MOZ_ASSERT(collection_); \
259 : return *collection_; \
260 : } \
261 : \
262 : const T& collection() const { \
263 : MOZ_ASSERT(collection_); \
264 : return *collection_; \
265 : } \
266 : \
267 : public: \
268 : explicit N(NameCollectionPool& pool) \
269 : : pool_(pool), \
270 : collection_(nullptr) \
271 : { } \
272 : \
273 : ~N() { \
274 : pool_.release##T(&collection_); \
275 : } \
276 : \
277 : bool acquire(JSContext* cx) { \
278 : MOZ_ASSERT(!collection_); \
279 : collection_ = pool_.acquire##T<T>(cx); \
280 : return !!collection_; \
281 : } \
282 : \
283 : explicit operator bool() const { \
284 : return !!collection_; \
285 : } \
286 : \
287 : T* operator->() { \
288 : return &collection(); \
289 : } \
290 : \
291 : const T* operator->() const { \
292 : return &collection(); \
293 : } \
294 : \
295 : T& operator*() { \
296 : return collection(); \
297 : } \
298 : \
299 : const T& operator*() const { \
300 : return collection(); \
301 : }
302 :
303 : template <typename Map>
304 : class PooledMapPtr
305 : {
306 1320793 : POOLED_COLLECTION_PTR_METHODS(PooledMapPtr, Map)
307 : };
308 :
309 : template <typename Vector>
310 : class PooledVectorPtr
311 : {
312 169164 : POOLED_COLLECTION_PTR_METHODS(PooledVectorPtr, Vector)
313 :
314 : typename Vector::ElementType& operator[](size_t index) {
315 : return collection()[index];
316 : }
317 :
318 : const typename Vector::ElementType& operator[](size_t index) const {
319 : return collection()[index];
320 : }
321 : };
322 :
323 : #undef POOLED_COLLECTION_PTR_METHODS
324 :
325 : } // namespace frontend
326 : } // namespace js
327 :
328 : namespace mozilla {
329 :
330 : template <>
331 : struct IsPod<js::MaybeCheckTDZ> : TrueType {};
332 :
333 : template <typename T>
334 : struct IsPod<js::frontend::RecyclableAtomMapValueWrapper<T>> : IsPod<T> {};
335 :
336 : } // namespace mozilla
337 :
338 : #endif // frontend_NameCollections_h
|