/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef frontend_NameCollections_h #define frontend_NameCollections_h #include "ds/InlineTable.h" #include "frontend/NameAnalysisTypes.h" #include "js/Vector.h" #include "vm/Stack.h" namespace js { namespace frontend { // A pool of recyclable containers for use in the frontend. The Parser and // BytecodeEmitter create many maps for name analysis that are short-lived // (i.e., for the duration of parsing or emitting a lexical scope). Making // them recyclable cuts down significantly on allocator churn. template class CollectionPool { using RecyclableCollections = Vector; RecyclableCollections all_; RecyclableCollections recyclable_; static RepresentativeCollection* asRepresentative(void* p) { return reinterpret_cast(p); } RepresentativeCollection* allocate() { size_t newAllLength = all_.length() + 1; if (!all_.reserve(newAllLength) || !recyclable_.reserve(newAllLength)) return nullptr; RepresentativeCollection* collection = js_new(); if (collection) all_.infallibleAppend(collection); return collection; } public: ~CollectionPool() { purgeAll(); } bool empty() const { return all_.empty(); } void purgeAll() { void** end = all_.end(); for (void** it = all_.begin(); it != end; ++it) js_delete(asRepresentative(*it)); all_.clearAndFree(); recyclable_.clearAndFree(); } // Fallibly aquire one of the supported collection types from the pool. template Collection* acquire(ExclusiveContext* cx) { ConcreteCollectionPool::template assertInvariants(); RepresentativeCollection* collection; if (recyclable_.empty()) { collection = allocate(); if (!collection) ReportOutOfMemory(cx); } else { collection = asRepresentative(recyclable_.popCopy()); collection->clear(); } return reinterpret_cast(collection); } // Release a collection back to the pool. template void release(Collection** collection) { ConcreteCollectionPool::template assertInvariants(); MOZ_ASSERT(*collection); #ifdef DEBUG bool ok = false; // Make sure the collection is in |all_| but not already in |recyclable_|. for (void** it = all_.begin(); it != all_.end(); ++it) { if (*it == *collection) { ok = true; break; } } MOZ_ASSERT(ok); for (void** it = recyclable_.begin(); it != recyclable_.end(); ++it) MOZ_ASSERT(*it != *collection); #endif MOZ_ASSERT(recyclable_.length() < all_.length()); // Reserved in allocateFresh. recyclable_.infallibleAppend(*collection); *collection = nullptr; } }; template struct RecyclableAtomMapValueWrapper { union { Wrapped wrapped; uint64_t dummy; }; static void assertInvariant() { static_assert(sizeof(Wrapped) <= sizeof(uint64_t), "Can only recycle atom maps with values smaller than uint64"); } RecyclableAtomMapValueWrapper() { assertInvariant(); } MOZ_IMPLICIT RecyclableAtomMapValueWrapper(Wrapped w) : wrapped(w) { assertInvariant(); } MOZ_IMPLICIT operator Wrapped&() { return wrapped; } MOZ_IMPLICIT operator Wrapped&() const { return wrapped; } Wrapped* operator->() { return &wrapped; } const Wrapped* operator->() const { return &wrapped; } }; template using RecyclableNameMap = InlineMap, 24, DefaultHasher, SystemAllocPolicy>; using DeclaredNameMap = RecyclableNameMap; using CheckTDZMap = RecyclableNameMap; using NameLocationMap = RecyclableNameMap; using AtomIndexMap = RecyclableNameMap; #undef RECYCLABLE_NAME_MAP_TYPE template class InlineTablePool : public CollectionPool> { public: template static void assertInvariants() { static_assert(Table::SizeOfInlineEntries == RepresentativeTable::SizeOfInlineEntries, "Only tables with the same size for inline entries are usable in the pool."); static_assert(mozilla::IsPod::value, "Only tables with POD values are usable in the pool."); } }; using FunctionBoxVector = Vector; template class VectorPool : public CollectionPool> { public: template static void assertInvariants() { static_assert(Vector::sMaxInlineStorage == RepresentativeVector::sMaxInlineStorage, "Only vectors with the same size for inline entries are usable in the pool."); static_assert(mozilla::IsPod::value, "Only vectors of POD values are usable in the pool."); static_assert(sizeof(typename Vector::ElementType) == sizeof(typename RepresentativeVector::ElementType), "Only vectors with same-sized elements are usable in the pool."); } }; class NameCollectionPool { InlineTablePool mapPool_; VectorPool vectorPool_; uint32_t activeCompilations_; public: NameCollectionPool() : activeCompilations_(0) { } bool hasActiveCompilation() const { return activeCompilations_ != 0; } void addActiveCompilation() { activeCompilations_++; } void removeActiveCompilation() { MOZ_ASSERT(hasActiveCompilation()); activeCompilations_--; } template Map* acquireMap(ExclusiveContext* cx) { MOZ_ASSERT(hasActiveCompilation()); return mapPool_.acquire(cx); } template void releaseMap(Map** map) { MOZ_ASSERT(hasActiveCompilation()); MOZ_ASSERT(map); if (*map) mapPool_.release(map); } template Vector* acquireVector(ExclusiveContext* cx) { MOZ_ASSERT(hasActiveCompilation()); return vectorPool_.acquire(cx); } template void releaseVector(Vector** vec) { MOZ_ASSERT(hasActiveCompilation()); MOZ_ASSERT(vec); if (*vec) vectorPool_.release(vec); } void purge() { if (!hasActiveCompilation()) { mapPool_.purgeAll(); vectorPool_.purgeAll(); } } }; #define POOLED_COLLECTION_PTR_METHODS(N, T) \ NameCollectionPool& pool_; \ T* collection_; \ \ T& collection() { \ MOZ_ASSERT(collection_); \ return *collection_; \ } \ \ const T& collection() const { \ MOZ_ASSERT(collection_); \ return *collection_; \ } \ \ public: \ explicit N(NameCollectionPool& pool) \ : pool_(pool), \ collection_(nullptr) \ { } \ \ ~N() { \ pool_.release##T(&collection_); \ } \ \ bool acquire(ExclusiveContext* cx) { \ MOZ_ASSERT(!collection_); \ collection_ = pool_.acquire##T(cx); \ return !!collection_; \ } \ \ explicit operator bool() const { \ return !!collection_; \ } \ \ T* operator->() { \ return &collection(); \ } \ \ const T* operator->() const { \ return &collection(); \ } \ \ T& operator*() { \ return collection(); \ } \ \ const T& operator*() const { \ return collection(); \ } template class PooledMapPtr { POOLED_COLLECTION_PTR_METHODS(PooledMapPtr, Map) }; template class PooledVectorPtr { POOLED_COLLECTION_PTR_METHODS(PooledVectorPtr, Vector) typename Vector::ElementType& operator[](size_t index) { return collection()[index]; } const typename Vector::ElementType& operator[](size_t index) const { return collection()[index]; } }; #undef POOLED_COLLECTION_PTR_METHODS } // namespace frontend } // namespace js namespace mozilla { template <> struct IsPod : TrueType {}; template struct IsPod> : IsPod {}; } // namespace mozilla #endif // frontend_NameCollections_h