michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sw=4 et tw=78: michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef gc_Nursery_h michael@0: #define gc_Nursery_h michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: michael@0: #include "jsalloc.h" michael@0: #include "jspubtd.h" michael@0: michael@0: #include "ds/BitArray.h" michael@0: #include "gc/Heap.h" michael@0: #include "gc/Memory.h" michael@0: #include "js/GCAPI.h" michael@0: #include "js/HashTable.h" michael@0: #include "js/HeapAPI.h" michael@0: #include "js/Value.h" michael@0: #include "js/Vector.h" michael@0: michael@0: namespace JS { michael@0: struct Zone; michael@0: } michael@0: michael@0: namespace js { michael@0: michael@0: class ObjectElements; michael@0: class HeapSlot; michael@0: void SetGCZeal(JSRuntime *, uint8_t, uint32_t); michael@0: michael@0: namespace gc { michael@0: class Cell; michael@0: class MinorCollectionTracer; michael@0: } /* namespace gc */ michael@0: michael@0: namespace types { michael@0: struct TypeObject; michael@0: } michael@0: michael@0: namespace jit { michael@0: class CodeGenerator; michael@0: class MacroAssembler; michael@0: class ICStubCompiler; michael@0: class BaselineCompiler; michael@0: } michael@0: michael@0: class Nursery michael@0: { michael@0: public: michael@0: static const int NumNurseryChunks = 16; michael@0: static const int LastNurseryChunk = NumNurseryChunks - 1; michael@0: static const size_t Alignment = gc::ChunkSize; michael@0: static const size_t ChunkShift = gc::ChunkShift; michael@0: static const size_t NurserySize = gc::ChunkSize * NumNurseryChunks; michael@0: michael@0: explicit Nursery(JSRuntime *rt) michael@0: : runtime_(rt), michael@0: position_(0), michael@0: currentStart_(0), michael@0: currentEnd_(0), michael@0: currentChunk_(0), michael@0: numActiveChunks_(0) michael@0: {} michael@0: ~Nursery(); michael@0: michael@0: bool init(); michael@0: michael@0: void enable(); michael@0: void disable(); michael@0: bool isEnabled() const { return numActiveChunks_ != 0; } michael@0: michael@0: /* Return true if no allocations have been made since the last collection. */ michael@0: bool isEmpty() const; michael@0: michael@0: template michael@0: MOZ_ALWAYS_INLINE bool isInside(const T *p) const { michael@0: return gc::IsInsideNursery((JS::shadow::Runtime *)runtime_, p); michael@0: } michael@0: michael@0: /* michael@0: * Allocate and return a pointer to a new GC object with its |slots| michael@0: * pointer pre-filled. Returns nullptr if the Nursery is full. michael@0: */ michael@0: JSObject *allocateObject(JSContext *cx, size_t size, size_t numDynamic); michael@0: michael@0: /* Allocate a slots array for the given object. */ michael@0: HeapSlot *allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots); michael@0: michael@0: /* Allocate an elements vector for the given object. */ michael@0: ObjectElements *allocateElements(JSContext *cx, JSObject *obj, uint32_t nelems); michael@0: michael@0: /* Resize an existing slots array. */ michael@0: HeapSlot *reallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots, michael@0: uint32_t oldCount, uint32_t newCount); michael@0: michael@0: /* Resize an existing elements vector. */ michael@0: ObjectElements *reallocateElements(JSContext *cx, JSObject *obj, ObjectElements *oldHeader, michael@0: uint32_t oldCount, uint32_t newCount); michael@0: michael@0: /* Free a slots array. */ michael@0: void freeSlots(JSContext *cx, HeapSlot *slots); michael@0: michael@0: /* Add a slots to our tracking list if it is out-of-line. */ michael@0: void notifyInitialSlots(gc::Cell *cell, HeapSlot *slots); michael@0: michael@0: typedef Vector TypeObjectList; michael@0: michael@0: /* michael@0: * Do a minor collection, optionally specifying a list to store types which michael@0: * should be pretenured afterwards. michael@0: */ michael@0: void collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList *pretenureTypes); michael@0: michael@0: /* michael@0: * Check if the thing at |*ref| in the Nursery has been forwarded. If so, michael@0: * sets |*ref| to the new location of the object and returns true. Otherwise michael@0: * returns false and leaves |*ref| unset. michael@0: */ michael@0: template michael@0: MOZ_ALWAYS_INLINE bool getForwardedPointer(T **ref); michael@0: michael@0: /* Forward a slots/elements pointer stored in an Ion frame. */ michael@0: void forwardBufferPointer(HeapSlot **pSlotsElems); michael@0: michael@0: size_t sizeOfHeapCommitted() const { michael@0: return numActiveChunks_ * gc::ChunkSize; michael@0: } michael@0: size_t sizeOfHeapDecommitted() const { michael@0: return (NumNurseryChunks - numActiveChunks_) * gc::ChunkSize; michael@0: } michael@0: size_t sizeOfHugeSlots(mozilla::MallocSizeOf mallocSizeOf) const { michael@0: size_t total = 0; michael@0: for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront()) michael@0: total += mallocSizeOf(r.front()); michael@0: total += hugeSlots.sizeOfExcludingThis(mallocSizeOf); michael@0: return total; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE uintptr_t start() const { michael@0: JS_ASSERT(runtime_); michael@0: return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE uintptr_t heapEnd() const { michael@0: JS_ASSERT(runtime_); michael@0: return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_; michael@0: } michael@0: michael@0: private: michael@0: /* michael@0: * The start and end pointers are stored under the runtime so that we can michael@0: * inline the isInsideNursery check into embedder code. Use the start() michael@0: * and heapEnd() functions to access these values. michael@0: */ michael@0: JSRuntime *runtime_; michael@0: michael@0: /* Pointer to the first unallocated byte in the nursery. */ michael@0: uintptr_t position_; michael@0: michael@0: /* Pointer to the logical start of the Nursery. */ michael@0: uintptr_t currentStart_; michael@0: michael@0: /* Pointer to the last byte of space in the current chunk. */ michael@0: uintptr_t currentEnd_; michael@0: michael@0: /* The index of the chunk that is currently being allocated from. */ michael@0: int currentChunk_; michael@0: michael@0: /* The index after the last chunk that we will allocate from. */ michael@0: int numActiveChunks_; michael@0: michael@0: /* michael@0: * The set of externally malloced slots potentially kept live by objects michael@0: * stored in the nursery. Any external slots that do not belong to a michael@0: * tenured thing at the end of a minor GC must be freed. michael@0: */ michael@0: typedef HashSet, SystemAllocPolicy> HugeSlotsSet; michael@0: HugeSlotsSet hugeSlots; michael@0: michael@0: /* The maximum number of slots allowed to reside inline in the nursery. */ michael@0: static const size_t MaxNurserySlots = 128; michael@0: michael@0: /* The amount of space in the mapped nursery available to allocations. */ michael@0: static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer); michael@0: michael@0: struct NurseryChunkLayout { michael@0: char data[NurseryChunkUsableSize]; michael@0: gc::ChunkTrailer trailer; michael@0: uintptr_t start() { return uintptr_t(&data); } michael@0: uintptr_t end() { return uintptr_t(&trailer); } michael@0: }; michael@0: static_assert(sizeof(NurseryChunkLayout) == gc::ChunkSize, michael@0: "Nursery chunk size must match gc::Chunk size."); michael@0: NurseryChunkLayout &chunk(int index) const { michael@0: JS_ASSERT(index < NumNurseryChunks); michael@0: JS_ASSERT(start()); michael@0: return reinterpret_cast(start())[index]; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE void initChunk(int chunkno) { michael@0: NurseryChunkLayout &c = chunk(chunkno); michael@0: c.trailer.location = gc::ChunkLocationNursery; michael@0: c.trailer.runtime = runtime(); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) { michael@0: JS_ASSERT(chunkno < NumNurseryChunks); michael@0: JS_ASSERT(chunkno < numActiveChunks_); michael@0: currentChunk_ = chunkno; michael@0: position_ = chunk(chunkno).start(); michael@0: currentEnd_ = chunk(chunkno).end(); michael@0: initChunk(chunkno); michael@0: } michael@0: michael@0: void updateDecommittedRegion() { michael@0: #ifndef JS_GC_ZEAL michael@0: if (numActiveChunks_ < NumNurseryChunks) { michael@0: // Bug 994054: madvise on MacOS is too slow to make this michael@0: // optimization worthwhile. michael@0: # ifndef XP_MACOSX michael@0: uintptr_t decommitStart = chunk(numActiveChunks_).start(); michael@0: JS_ASSERT(decommitStart == AlignBytes(decommitStart, 1 << 20)); michael@0: gc::MarkPagesUnused(runtime(), (void *)decommitStart, heapEnd() - decommitStart); michael@0: # endif michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE uintptr_t allocationEnd() const { michael@0: JS_ASSERT(numActiveChunks_ > 0); michael@0: return chunk(numActiveChunks_ - 1).end(); michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE bool isFullyGrown() const { michael@0: return numActiveChunks_ == NumNurseryChunks; michael@0: } michael@0: michael@0: MOZ_ALWAYS_INLINE uintptr_t currentEnd() const { michael@0: JS_ASSERT(runtime_); michael@0: JS_ASSERT(currentEnd_ == chunk(currentChunk_).end()); michael@0: return currentEnd_; michael@0: } michael@0: void *addressOfCurrentEnd() const { michael@0: JS_ASSERT(runtime_); michael@0: return (void *)¤tEnd_; michael@0: } michael@0: michael@0: uintptr_t position() const { return position_; } michael@0: void *addressOfPosition() const { return (void*)&position_; } michael@0: michael@0: JSRuntime *runtime() const { return runtime_; } michael@0: michael@0: /* Allocates and registers external slots with the nursery. */ michael@0: HeapSlot *allocateHugeSlots(JSContext *cx, size_t nslots); michael@0: michael@0: /* Allocates a new GC thing from the tenured generation during minor GC. */ michael@0: void *allocateFromTenured(JS::Zone *zone, gc::AllocKind thingKind); michael@0: michael@0: struct TenureCountCache; michael@0: michael@0: /* Common internal allocator function. */ michael@0: void *allocate(size_t size); michael@0: michael@0: /* michael@0: * Move the object at |src| in the Nursery to an already-allocated cell michael@0: * |dst| in Tenured. michael@0: */ michael@0: void collectToFixedPoint(gc::MinorCollectionTracer *trc, TenureCountCache &tenureCounts); michael@0: MOZ_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer *trc, JSObject *src); michael@0: MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, uint32_t nslots); michael@0: MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, HeapSlot *end); michael@0: MOZ_ALWAYS_INLINE void markSlot(gc::MinorCollectionTracer *trc, HeapSlot *slotp); michael@0: void *moveToTenured(gc::MinorCollectionTracer *trc, JSObject *src); michael@0: size_t moveObjectToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); michael@0: size_t moveElementsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); michael@0: size_t moveSlotsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); michael@0: void forwardTypedArrayPointers(JSObject *dst, JSObject *src); michael@0: michael@0: /* Handle relocation of slots/elements pointers stored in Ion frames. */ michael@0: void setSlotsForwardingPointer(HeapSlot *oldSlots, HeapSlot *newSlots, uint32_t nslots); michael@0: void setElementsForwardingPointer(ObjectElements *oldHeader, ObjectElements *newHeader, michael@0: uint32_t nelems); michael@0: michael@0: /* Free malloced pointers owned by freed things in the nursery. */ michael@0: void freeHugeSlots(JSRuntime *rt); michael@0: michael@0: /* michael@0: * Frees all non-live nursery-allocated things at the end of a minor michael@0: * collection. michael@0: */ michael@0: void sweep(JSRuntime *rt); michael@0: michael@0: /* Change the allocable space provided by the nursery. */ michael@0: void growAllocableSpace(); michael@0: void shrinkAllocableSpace(); michael@0: michael@0: static void MinorGCCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind); michael@0: michael@0: #ifdef JS_GC_ZEAL michael@0: /* michael@0: * In debug and zeal builds, these bytes indicate the state of an unused michael@0: * segment of nursery-allocated memory. michael@0: */ michael@0: void enterZealMode() { michael@0: if (isEnabled()) michael@0: numActiveChunks_ = NumNurseryChunks; michael@0: } michael@0: void leaveZealMode() { michael@0: if (isEnabled()) { michael@0: JS_ASSERT(isEmpty()); michael@0: setCurrentChunk(0); michael@0: currentStart_ = start(); michael@0: } michael@0: } michael@0: #else michael@0: void enterZealMode() {} michael@0: void leaveZealMode() {} michael@0: #endif michael@0: michael@0: friend class gc::MinorCollectionTracer; michael@0: friend class jit::MacroAssembler; michael@0: friend void SetGCZeal(JSRuntime *, uint8_t, uint32_t); michael@0: }; michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #endif /* JSGC_GENERATIONAL */ michael@0: #endif /* gc_Nursery_h */