/* -*- 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 jit_JitAllocPolicy_h #define jit_JitAllocPolicy_h #include "mozilla/Attributes.h" #include "mozilla/GuardObjects.h" #include "mozilla/OperatorNewExtensions.h" #include "mozilla/TypeTraits.h" #include "jscntxt.h" #include "ds/LifoAlloc.h" #include "jit/InlineList.h" #include "jit/Ion.h" namespace js { namespace jit { class TempAllocator { LifoAllocScope lifoScope_; public: // Most infallible JIT allocations are small, so we use a ballast of 16 // KiB. And with a ballast of 16 KiB, a chunk size of 32 KiB works well, // because TempAllocators with a peak allocation size of less than 16 KiB // (which is most of them) only have to allocate a single chunk. static const size_t BallastSize; // 16 KiB static const size_t PreferredLifoChunkSize; // 32 KiB explicit TempAllocator(LifoAlloc* lifoAlloc) : lifoScope_(lifoAlloc) { lifoAlloc->setAsInfallibleByDefault(); } void* allocateInfallible(size_t bytes) { return lifoScope_.alloc().allocInfallible(bytes); } MOZ_MUST_USE void* allocate(size_t bytes) { LifoAlloc::AutoFallibleScope fallibleAllocator(lifoAlloc()); void* p = lifoScope_.alloc().alloc(bytes); if (!ensureBallast()) return nullptr; return p; } template <typename T> MOZ_MUST_USE T* allocateArray(size_t n) { LifoAlloc::AutoFallibleScope fallibleAllocator(lifoAlloc()); size_t bytes; if (MOZ_UNLIKELY(!CalculateAllocSize<T>(n, &bytes))) return nullptr; T* p = static_cast<T*>(lifoScope_.alloc().alloc(bytes)); if (MOZ_UNLIKELY(!ensureBallast())) return nullptr; return p; } // View this allocator as a fallible allocator. struct Fallible { TempAllocator& alloc; }; Fallible fallible() { return { *this }; } LifoAlloc* lifoAlloc() { return &lifoScope_.alloc(); } MOZ_MUST_USE bool ensureBallast() { JS_OOM_POSSIBLY_FAIL_BOOL(); return lifoScope_.alloc().ensureUnusedApproximate(BallastSize); } }; class JitAllocPolicy { TempAllocator& alloc_; public: MOZ_IMPLICIT JitAllocPolicy(TempAllocator& alloc) : alloc_(alloc) {} template <typename T> T* maybe_pod_malloc(size_t numElems) { size_t bytes; if (MOZ_UNLIKELY(!CalculateAllocSize<T>(numElems, &bytes))) return nullptr; return static_cast<T*>(alloc_.allocate(bytes)); } template <typename T> T* maybe_pod_calloc(size_t numElems) { T* p = maybe_pod_malloc<T>(numElems); if (MOZ_LIKELY(p)) memset(p, 0, numElems * sizeof(T)); return p; } template <typename T> T* maybe_pod_realloc(T* p, size_t oldSize, size_t newSize) { T* n = pod_malloc<T>(newSize); if (MOZ_UNLIKELY(!n)) return n; MOZ_ASSERT(!(oldSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value)); memcpy(n, p, Min(oldSize * sizeof(T), newSize * sizeof(T))); return n; } template <typename T> T* pod_malloc(size_t numElems) { return maybe_pod_malloc<T>(numElems); } template <typename T> T* pod_calloc(size_t numElems) { return maybe_pod_calloc<T>(numElems); } template <typename T> T* pod_realloc(T* ptr, size_t oldSize, size_t newSize) { return maybe_pod_realloc<T>(ptr, oldSize, newSize); } void free_(void* p) { } void reportAllocOverflow() const { } MOZ_MUST_USE bool checkSimulatedOOM() const { return !js::oom::ShouldFailWithOOM(); } }; class AutoJitContextAlloc { TempAllocator tempAlloc_; JitContext* jcx_; TempAllocator* prevAlloc_; public: explicit AutoJitContextAlloc(JSContext* cx) : tempAlloc_(&cx->tempLifoAlloc()), jcx_(GetJitContext()), prevAlloc_(jcx_->temp) { jcx_->temp = &tempAlloc_; } ~AutoJitContextAlloc() { MOZ_ASSERT(jcx_->temp == &tempAlloc_); jcx_->temp = prevAlloc_; } }; struct TempObject { inline void* operator new(size_t nbytes, TempAllocator::Fallible view) throw() { return view.alloc.allocate(nbytes); } inline void* operator new(size_t nbytes, TempAllocator& alloc) { return alloc.allocateInfallible(nbytes); } template <class T> inline void* operator new(size_t nbytes, T* pos) { static_assert(mozilla::IsConvertible<T*, TempObject*>::value, "Placement new argument type must inherit from TempObject"); return pos; } template <class T> inline void* operator new(size_t nbytes, mozilla::NotNullTag, T* pos) { static_assert(mozilla::IsConvertible<T*, TempObject*>::value, "Placement new argument type must inherit from TempObject"); MOZ_ASSERT(pos); return pos; } }; template <typename T> class TempObjectPool { TempAllocator* alloc_; InlineForwardList<T> freed_; public: TempObjectPool() : alloc_(nullptr) {} void setAllocator(TempAllocator& alloc) { MOZ_ASSERT(freed_.empty()); alloc_ = &alloc; } T* allocate() { MOZ_ASSERT(alloc_); if (freed_.empty()) return new(alloc_->fallible()) T(); return freed_.popFront(); } void free(T* obj) { freed_.pushFront(obj); } void clear() { freed_.clear(); } }; } // namespace jit } // namespace js #endif /* jit_JitAllocPolicy_h */