diff options
Diffstat (limited to 'js/src/frontend/NameCollections.h')
-rw-r--r-- | js/src/frontend/NameCollections.h | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/js/src/frontend/NameCollections.h b/js/src/frontend/NameCollections.h new file mode 100644 index 000000000..58c5d0ac0 --- /dev/null +++ b/js/src/frontend/NameCollections.h @@ -0,0 +1,338 @@ +/* -*- 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 <typename RepresentativeCollection, typename ConcreteCollectionPool> +class CollectionPool +{ + using RecyclableCollections = Vector<void*, 32, SystemAllocPolicy>; + + RecyclableCollections all_; + RecyclableCollections recyclable_; + + static RepresentativeCollection* asRepresentative(void* p) { + return reinterpret_cast<RepresentativeCollection*>(p); + } + + RepresentativeCollection* allocate() { + size_t newAllLength = all_.length() + 1; + if (!all_.reserve(newAllLength) || !recyclable_.reserve(newAllLength)) + return nullptr; + + RepresentativeCollection* collection = js_new<RepresentativeCollection>(); + 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 <typename Collection> + Collection* acquire(ExclusiveContext* cx) { + ConcreteCollectionPool::template assertInvariants<Collection>(); + + RepresentativeCollection* collection; + if (recyclable_.empty()) { + collection = allocate(); + if (!collection) + ReportOutOfMemory(cx); + } else { + collection = asRepresentative(recyclable_.popCopy()); + collection->clear(); + } + return reinterpret_cast<Collection*>(collection); + } + + // Release a collection back to the pool. + template <typename Collection> + void release(Collection** collection) { + ConcreteCollectionPool::template assertInvariants<Collection>(); + 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 <typename Wrapped> +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 <typename MapValue> +using RecyclableNameMap = InlineMap<JSAtom*, + RecyclableAtomMapValueWrapper<MapValue>, + 24, + DefaultHasher<JSAtom*>, + SystemAllocPolicy>; + +using DeclaredNameMap = RecyclableNameMap<DeclaredNameInfo>; +using CheckTDZMap = RecyclableNameMap<MaybeCheckTDZ>; +using NameLocationMap = RecyclableNameMap<NameLocation>; +using AtomIndexMap = RecyclableNameMap<uint32_t>; + +#undef RECYCLABLE_NAME_MAP_TYPE + +template <typename RepresentativeTable> +class InlineTablePool + : public CollectionPool<RepresentativeTable, InlineTablePool<RepresentativeTable>> +{ + public: + template <typename Table> + 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<typename Table::Table::Entry>::value, + "Only tables with POD values are usable in the pool."); + } +}; + +using FunctionBoxVector = Vector<FunctionBox*, 24, SystemAllocPolicy>; + +template <typename RepresentativeVector> +class VectorPool : public CollectionPool<RepresentativeVector, VectorPool<RepresentativeVector>> +{ + public: + template <typename Vector> + 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<typename Vector::ElementType>::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<AtomIndexMap> mapPool_; + VectorPool<AtomVector> vectorPool_; + uint32_t activeCompilations_; + + public: + NameCollectionPool() + : activeCompilations_(0) + { } + + bool hasActiveCompilation() const { + return activeCompilations_ != 0; + } + + void addActiveCompilation() { + activeCompilations_++; + } + + void removeActiveCompilation() { + MOZ_ASSERT(hasActiveCompilation()); + activeCompilations_--; + } + + template <typename Map> + Map* acquireMap(ExclusiveContext* cx) { + MOZ_ASSERT(hasActiveCompilation()); + return mapPool_.acquire<Map>(cx); + } + + template <typename Map> + void releaseMap(Map** map) { + MOZ_ASSERT(hasActiveCompilation()); + MOZ_ASSERT(map); + if (*map) + mapPool_.release(map); + } + + template <typename Vector> + Vector* acquireVector(ExclusiveContext* cx) { + MOZ_ASSERT(hasActiveCompilation()); + return vectorPool_.acquire<Vector>(cx); + } + + template <typename Vector> + 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<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 <typename Map> +class PooledMapPtr +{ + POOLED_COLLECTION_PTR_METHODS(PooledMapPtr, Map) +}; + +template <typename Vector> +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<js::MaybeCheckTDZ> : TrueType {}; + +template <typename T> +struct IsPod<js::frontend::RecyclableAtomMapValueWrapper<T>> : IsPod<T> {}; + +} // namespace mozilla + +#endif // frontend_NameCollections_h |