Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sw=4 et tw=78: |
michael@0 | 3 | * |
michael@0 | 4 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 6 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 7 | |
michael@0 | 8 | #ifndef gc_Nursery_h |
michael@0 | 9 | #define gc_Nursery_h |
michael@0 | 10 | |
michael@0 | 11 | #ifdef JSGC_GENERATIONAL |
michael@0 | 12 | |
michael@0 | 13 | #include "jsalloc.h" |
michael@0 | 14 | #include "jspubtd.h" |
michael@0 | 15 | |
michael@0 | 16 | #include "ds/BitArray.h" |
michael@0 | 17 | #include "gc/Heap.h" |
michael@0 | 18 | #include "gc/Memory.h" |
michael@0 | 19 | #include "js/GCAPI.h" |
michael@0 | 20 | #include "js/HashTable.h" |
michael@0 | 21 | #include "js/HeapAPI.h" |
michael@0 | 22 | #include "js/Value.h" |
michael@0 | 23 | #include "js/Vector.h" |
michael@0 | 24 | |
michael@0 | 25 | namespace JS { |
michael@0 | 26 | struct Zone; |
michael@0 | 27 | } |
michael@0 | 28 | |
michael@0 | 29 | namespace js { |
michael@0 | 30 | |
michael@0 | 31 | class ObjectElements; |
michael@0 | 32 | class HeapSlot; |
michael@0 | 33 | void SetGCZeal(JSRuntime *, uint8_t, uint32_t); |
michael@0 | 34 | |
michael@0 | 35 | namespace gc { |
michael@0 | 36 | class Cell; |
michael@0 | 37 | class MinorCollectionTracer; |
michael@0 | 38 | } /* namespace gc */ |
michael@0 | 39 | |
michael@0 | 40 | namespace types { |
michael@0 | 41 | struct TypeObject; |
michael@0 | 42 | } |
michael@0 | 43 | |
michael@0 | 44 | namespace jit { |
michael@0 | 45 | class CodeGenerator; |
michael@0 | 46 | class MacroAssembler; |
michael@0 | 47 | class ICStubCompiler; |
michael@0 | 48 | class BaselineCompiler; |
michael@0 | 49 | } |
michael@0 | 50 | |
michael@0 | 51 | class Nursery |
michael@0 | 52 | { |
michael@0 | 53 | public: |
michael@0 | 54 | static const int NumNurseryChunks = 16; |
michael@0 | 55 | static const int LastNurseryChunk = NumNurseryChunks - 1; |
michael@0 | 56 | static const size_t Alignment = gc::ChunkSize; |
michael@0 | 57 | static const size_t ChunkShift = gc::ChunkShift; |
michael@0 | 58 | static const size_t NurserySize = gc::ChunkSize * NumNurseryChunks; |
michael@0 | 59 | |
michael@0 | 60 | explicit Nursery(JSRuntime *rt) |
michael@0 | 61 | : runtime_(rt), |
michael@0 | 62 | position_(0), |
michael@0 | 63 | currentStart_(0), |
michael@0 | 64 | currentEnd_(0), |
michael@0 | 65 | currentChunk_(0), |
michael@0 | 66 | numActiveChunks_(0) |
michael@0 | 67 | {} |
michael@0 | 68 | ~Nursery(); |
michael@0 | 69 | |
michael@0 | 70 | bool init(); |
michael@0 | 71 | |
michael@0 | 72 | void enable(); |
michael@0 | 73 | void disable(); |
michael@0 | 74 | bool isEnabled() const { return numActiveChunks_ != 0; } |
michael@0 | 75 | |
michael@0 | 76 | /* Return true if no allocations have been made since the last collection. */ |
michael@0 | 77 | bool isEmpty() const; |
michael@0 | 78 | |
michael@0 | 79 | template <typename T> |
michael@0 | 80 | MOZ_ALWAYS_INLINE bool isInside(const T *p) const { |
michael@0 | 81 | return gc::IsInsideNursery((JS::shadow::Runtime *)runtime_, p); |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | /* |
michael@0 | 85 | * Allocate and return a pointer to a new GC object with its |slots| |
michael@0 | 86 | * pointer pre-filled. Returns nullptr if the Nursery is full. |
michael@0 | 87 | */ |
michael@0 | 88 | JSObject *allocateObject(JSContext *cx, size_t size, size_t numDynamic); |
michael@0 | 89 | |
michael@0 | 90 | /* Allocate a slots array for the given object. */ |
michael@0 | 91 | HeapSlot *allocateSlots(JSContext *cx, JSObject *obj, uint32_t nslots); |
michael@0 | 92 | |
michael@0 | 93 | /* Allocate an elements vector for the given object. */ |
michael@0 | 94 | ObjectElements *allocateElements(JSContext *cx, JSObject *obj, uint32_t nelems); |
michael@0 | 95 | |
michael@0 | 96 | /* Resize an existing slots array. */ |
michael@0 | 97 | HeapSlot *reallocateSlots(JSContext *cx, JSObject *obj, HeapSlot *oldSlots, |
michael@0 | 98 | uint32_t oldCount, uint32_t newCount); |
michael@0 | 99 | |
michael@0 | 100 | /* Resize an existing elements vector. */ |
michael@0 | 101 | ObjectElements *reallocateElements(JSContext *cx, JSObject *obj, ObjectElements *oldHeader, |
michael@0 | 102 | uint32_t oldCount, uint32_t newCount); |
michael@0 | 103 | |
michael@0 | 104 | /* Free a slots array. */ |
michael@0 | 105 | void freeSlots(JSContext *cx, HeapSlot *slots); |
michael@0 | 106 | |
michael@0 | 107 | /* Add a slots to our tracking list if it is out-of-line. */ |
michael@0 | 108 | void notifyInitialSlots(gc::Cell *cell, HeapSlot *slots); |
michael@0 | 109 | |
michael@0 | 110 | typedef Vector<types::TypeObject *, 0, SystemAllocPolicy> TypeObjectList; |
michael@0 | 111 | |
michael@0 | 112 | /* |
michael@0 | 113 | * Do a minor collection, optionally specifying a list to store types which |
michael@0 | 114 | * should be pretenured afterwards. |
michael@0 | 115 | */ |
michael@0 | 116 | void collect(JSRuntime *rt, JS::gcreason::Reason reason, TypeObjectList *pretenureTypes); |
michael@0 | 117 | |
michael@0 | 118 | /* |
michael@0 | 119 | * Check if the thing at |*ref| in the Nursery has been forwarded. If so, |
michael@0 | 120 | * sets |*ref| to the new location of the object and returns true. Otherwise |
michael@0 | 121 | * returns false and leaves |*ref| unset. |
michael@0 | 122 | */ |
michael@0 | 123 | template <typename T> |
michael@0 | 124 | MOZ_ALWAYS_INLINE bool getForwardedPointer(T **ref); |
michael@0 | 125 | |
michael@0 | 126 | /* Forward a slots/elements pointer stored in an Ion frame. */ |
michael@0 | 127 | void forwardBufferPointer(HeapSlot **pSlotsElems); |
michael@0 | 128 | |
michael@0 | 129 | size_t sizeOfHeapCommitted() const { |
michael@0 | 130 | return numActiveChunks_ * gc::ChunkSize; |
michael@0 | 131 | } |
michael@0 | 132 | size_t sizeOfHeapDecommitted() const { |
michael@0 | 133 | return (NumNurseryChunks - numActiveChunks_) * gc::ChunkSize; |
michael@0 | 134 | } |
michael@0 | 135 | size_t sizeOfHugeSlots(mozilla::MallocSizeOf mallocSizeOf) const { |
michael@0 | 136 | size_t total = 0; |
michael@0 | 137 | for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront()) |
michael@0 | 138 | total += mallocSizeOf(r.front()); |
michael@0 | 139 | total += hugeSlots.sizeOfExcludingThis(mallocSizeOf); |
michael@0 | 140 | return total; |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | MOZ_ALWAYS_INLINE uintptr_t start() const { |
michael@0 | 144 | JS_ASSERT(runtime_); |
michael@0 | 145 | return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_; |
michael@0 | 146 | } |
michael@0 | 147 | |
michael@0 | 148 | MOZ_ALWAYS_INLINE uintptr_t heapEnd() const { |
michael@0 | 149 | JS_ASSERT(runtime_); |
michael@0 | 150 | return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_; |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | private: |
michael@0 | 154 | /* |
michael@0 | 155 | * The start and end pointers are stored under the runtime so that we can |
michael@0 | 156 | * inline the isInsideNursery check into embedder code. Use the start() |
michael@0 | 157 | * and heapEnd() functions to access these values. |
michael@0 | 158 | */ |
michael@0 | 159 | JSRuntime *runtime_; |
michael@0 | 160 | |
michael@0 | 161 | /* Pointer to the first unallocated byte in the nursery. */ |
michael@0 | 162 | uintptr_t position_; |
michael@0 | 163 | |
michael@0 | 164 | /* Pointer to the logical start of the Nursery. */ |
michael@0 | 165 | uintptr_t currentStart_; |
michael@0 | 166 | |
michael@0 | 167 | /* Pointer to the last byte of space in the current chunk. */ |
michael@0 | 168 | uintptr_t currentEnd_; |
michael@0 | 169 | |
michael@0 | 170 | /* The index of the chunk that is currently being allocated from. */ |
michael@0 | 171 | int currentChunk_; |
michael@0 | 172 | |
michael@0 | 173 | /* The index after the last chunk that we will allocate from. */ |
michael@0 | 174 | int numActiveChunks_; |
michael@0 | 175 | |
michael@0 | 176 | /* |
michael@0 | 177 | * The set of externally malloced slots potentially kept live by objects |
michael@0 | 178 | * stored in the nursery. Any external slots that do not belong to a |
michael@0 | 179 | * tenured thing at the end of a minor GC must be freed. |
michael@0 | 180 | */ |
michael@0 | 181 | typedef HashSet<HeapSlot *, PointerHasher<HeapSlot *, 3>, SystemAllocPolicy> HugeSlotsSet; |
michael@0 | 182 | HugeSlotsSet hugeSlots; |
michael@0 | 183 | |
michael@0 | 184 | /* The maximum number of slots allowed to reside inline in the nursery. */ |
michael@0 | 185 | static const size_t MaxNurserySlots = 128; |
michael@0 | 186 | |
michael@0 | 187 | /* The amount of space in the mapped nursery available to allocations. */ |
michael@0 | 188 | static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer); |
michael@0 | 189 | |
michael@0 | 190 | struct NurseryChunkLayout { |
michael@0 | 191 | char data[NurseryChunkUsableSize]; |
michael@0 | 192 | gc::ChunkTrailer trailer; |
michael@0 | 193 | uintptr_t start() { return uintptr_t(&data); } |
michael@0 | 194 | uintptr_t end() { return uintptr_t(&trailer); } |
michael@0 | 195 | }; |
michael@0 | 196 | static_assert(sizeof(NurseryChunkLayout) == gc::ChunkSize, |
michael@0 | 197 | "Nursery chunk size must match gc::Chunk size."); |
michael@0 | 198 | NurseryChunkLayout &chunk(int index) const { |
michael@0 | 199 | JS_ASSERT(index < NumNurseryChunks); |
michael@0 | 200 | JS_ASSERT(start()); |
michael@0 | 201 | return reinterpret_cast<NurseryChunkLayout *>(start())[index]; |
michael@0 | 202 | } |
michael@0 | 203 | |
michael@0 | 204 | MOZ_ALWAYS_INLINE void initChunk(int chunkno) { |
michael@0 | 205 | NurseryChunkLayout &c = chunk(chunkno); |
michael@0 | 206 | c.trailer.location = gc::ChunkLocationNursery; |
michael@0 | 207 | c.trailer.runtime = runtime(); |
michael@0 | 208 | } |
michael@0 | 209 | |
michael@0 | 210 | MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) { |
michael@0 | 211 | JS_ASSERT(chunkno < NumNurseryChunks); |
michael@0 | 212 | JS_ASSERT(chunkno < numActiveChunks_); |
michael@0 | 213 | currentChunk_ = chunkno; |
michael@0 | 214 | position_ = chunk(chunkno).start(); |
michael@0 | 215 | currentEnd_ = chunk(chunkno).end(); |
michael@0 | 216 | initChunk(chunkno); |
michael@0 | 217 | } |
michael@0 | 218 | |
michael@0 | 219 | void updateDecommittedRegion() { |
michael@0 | 220 | #ifndef JS_GC_ZEAL |
michael@0 | 221 | if (numActiveChunks_ < NumNurseryChunks) { |
michael@0 | 222 | // Bug 994054: madvise on MacOS is too slow to make this |
michael@0 | 223 | // optimization worthwhile. |
michael@0 | 224 | # ifndef XP_MACOSX |
michael@0 | 225 | uintptr_t decommitStart = chunk(numActiveChunks_).start(); |
michael@0 | 226 | JS_ASSERT(decommitStart == AlignBytes(decommitStart, 1 << 20)); |
michael@0 | 227 | gc::MarkPagesUnused(runtime(), (void *)decommitStart, heapEnd() - decommitStart); |
michael@0 | 228 | # endif |
michael@0 | 229 | } |
michael@0 | 230 | #endif |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | MOZ_ALWAYS_INLINE uintptr_t allocationEnd() const { |
michael@0 | 234 | JS_ASSERT(numActiveChunks_ > 0); |
michael@0 | 235 | return chunk(numActiveChunks_ - 1).end(); |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | MOZ_ALWAYS_INLINE bool isFullyGrown() const { |
michael@0 | 239 | return numActiveChunks_ == NumNurseryChunks; |
michael@0 | 240 | } |
michael@0 | 241 | |
michael@0 | 242 | MOZ_ALWAYS_INLINE uintptr_t currentEnd() const { |
michael@0 | 243 | JS_ASSERT(runtime_); |
michael@0 | 244 | JS_ASSERT(currentEnd_ == chunk(currentChunk_).end()); |
michael@0 | 245 | return currentEnd_; |
michael@0 | 246 | } |
michael@0 | 247 | void *addressOfCurrentEnd() const { |
michael@0 | 248 | JS_ASSERT(runtime_); |
michael@0 | 249 | return (void *)¤tEnd_; |
michael@0 | 250 | } |
michael@0 | 251 | |
michael@0 | 252 | uintptr_t position() const { return position_; } |
michael@0 | 253 | void *addressOfPosition() const { return (void*)&position_; } |
michael@0 | 254 | |
michael@0 | 255 | JSRuntime *runtime() const { return runtime_; } |
michael@0 | 256 | |
michael@0 | 257 | /* Allocates and registers external slots with the nursery. */ |
michael@0 | 258 | HeapSlot *allocateHugeSlots(JSContext *cx, size_t nslots); |
michael@0 | 259 | |
michael@0 | 260 | /* Allocates a new GC thing from the tenured generation during minor GC. */ |
michael@0 | 261 | void *allocateFromTenured(JS::Zone *zone, gc::AllocKind thingKind); |
michael@0 | 262 | |
michael@0 | 263 | struct TenureCountCache; |
michael@0 | 264 | |
michael@0 | 265 | /* Common internal allocator function. */ |
michael@0 | 266 | void *allocate(size_t size); |
michael@0 | 267 | |
michael@0 | 268 | /* |
michael@0 | 269 | * Move the object at |src| in the Nursery to an already-allocated cell |
michael@0 | 270 | * |dst| in Tenured. |
michael@0 | 271 | */ |
michael@0 | 272 | void collectToFixedPoint(gc::MinorCollectionTracer *trc, TenureCountCache &tenureCounts); |
michael@0 | 273 | MOZ_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer *trc, JSObject *src); |
michael@0 | 274 | MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, uint32_t nslots); |
michael@0 | 275 | MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer *trc, HeapSlot *vp, HeapSlot *end); |
michael@0 | 276 | MOZ_ALWAYS_INLINE void markSlot(gc::MinorCollectionTracer *trc, HeapSlot *slotp); |
michael@0 | 277 | void *moveToTenured(gc::MinorCollectionTracer *trc, JSObject *src); |
michael@0 | 278 | size_t moveObjectToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); |
michael@0 | 279 | size_t moveElementsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); |
michael@0 | 280 | size_t moveSlotsToTenured(JSObject *dst, JSObject *src, gc::AllocKind dstKind); |
michael@0 | 281 | void forwardTypedArrayPointers(JSObject *dst, JSObject *src); |
michael@0 | 282 | |
michael@0 | 283 | /* Handle relocation of slots/elements pointers stored in Ion frames. */ |
michael@0 | 284 | void setSlotsForwardingPointer(HeapSlot *oldSlots, HeapSlot *newSlots, uint32_t nslots); |
michael@0 | 285 | void setElementsForwardingPointer(ObjectElements *oldHeader, ObjectElements *newHeader, |
michael@0 | 286 | uint32_t nelems); |
michael@0 | 287 | |
michael@0 | 288 | /* Free malloced pointers owned by freed things in the nursery. */ |
michael@0 | 289 | void freeHugeSlots(JSRuntime *rt); |
michael@0 | 290 | |
michael@0 | 291 | /* |
michael@0 | 292 | * Frees all non-live nursery-allocated things at the end of a minor |
michael@0 | 293 | * collection. |
michael@0 | 294 | */ |
michael@0 | 295 | void sweep(JSRuntime *rt); |
michael@0 | 296 | |
michael@0 | 297 | /* Change the allocable space provided by the nursery. */ |
michael@0 | 298 | void growAllocableSpace(); |
michael@0 | 299 | void shrinkAllocableSpace(); |
michael@0 | 300 | |
michael@0 | 301 | static void MinorGCCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind); |
michael@0 | 302 | |
michael@0 | 303 | #ifdef JS_GC_ZEAL |
michael@0 | 304 | /* |
michael@0 | 305 | * In debug and zeal builds, these bytes indicate the state of an unused |
michael@0 | 306 | * segment of nursery-allocated memory. |
michael@0 | 307 | */ |
michael@0 | 308 | void enterZealMode() { |
michael@0 | 309 | if (isEnabled()) |
michael@0 | 310 | numActiveChunks_ = NumNurseryChunks; |
michael@0 | 311 | } |
michael@0 | 312 | void leaveZealMode() { |
michael@0 | 313 | if (isEnabled()) { |
michael@0 | 314 | JS_ASSERT(isEmpty()); |
michael@0 | 315 | setCurrentChunk(0); |
michael@0 | 316 | currentStart_ = start(); |
michael@0 | 317 | } |
michael@0 | 318 | } |
michael@0 | 319 | #else |
michael@0 | 320 | void enterZealMode() {} |
michael@0 | 321 | void leaveZealMode() {} |
michael@0 | 322 | #endif |
michael@0 | 323 | |
michael@0 | 324 | friend class gc::MinorCollectionTracer; |
michael@0 | 325 | friend class jit::MacroAssembler; |
michael@0 | 326 | friend void SetGCZeal(JSRuntime *, uint8_t, uint32_t); |
michael@0 | 327 | }; |
michael@0 | 328 | |
michael@0 | 329 | } /* namespace js */ |
michael@0 | 330 | |
michael@0 | 331 | #endif /* JSGC_GENERATIONAL */ |
michael@0 | 332 | #endif /* gc_Nursery_h */ |