forked from mirrors/gecko-dev
		
	Bug 1291292 - Use dynamic chunk allocation for the nursery r=terrence
This commit is contained in:
		
							parent
							
								
									7acfb399ba
								
							
						
					
					
						commit
						17304689a2
					
				
					 12 changed files with 261 additions and 196 deletions
				
			
		|  | @ -283,7 +283,6 @@ struct GCSizes | ||||||
| #define FOR_EACH_SIZE(macro) \ | #define FOR_EACH_SIZE(macro) \ | ||||||
|     macro(_, MallocHeap, marker) \ |     macro(_, MallocHeap, marker) \ | ||||||
|     macro(_, NonHeap,    nurseryCommitted) \ |     macro(_, NonHeap,    nurseryCommitted) \ | ||||||
|     macro(_, NonHeap,    nurseryDecommitted) \ |  | ||||||
|     macro(_, MallocHeap, nurseryMallocedBuffers) \ |     macro(_, MallocHeap, nurseryMallocedBuffers) \ | ||||||
|     macro(_, MallocHeap, storeBufferVals) \ |     macro(_, MallocHeap, storeBufferVals) \ | ||||||
|     macro(_, MallocHeap, storeBufferCells) \ |     macro(_, MallocHeap, storeBufferCells) \ | ||||||
|  |  | ||||||
|  | @ -260,30 +260,6 @@ GCRuntime::checkIncrementalZoneState(ExclusiveContext* cx, T* t) | ||||||
|  |  | ||||||
| // ///////////  Arena -> Thing Allocator  //////////////////////////////////////
 | // ///////////  Arena -> Thing Allocator  //////////////////////////////////////
 | ||||||
| 
 | 
 | ||||||
| // After pulling a Chunk out of the empty chunks pool, we want to run the
 |  | ||||||
| // background allocator to refill it. The code that takes Chunks does so under
 |  | ||||||
| // the GC lock. We need to start the background allocation under the helper
 |  | ||||||
| // threads lock. To avoid lock inversion we have to delay the start until after
 |  | ||||||
| // we are outside the GC lock. This class handles that delay automatically.
 |  | ||||||
| class MOZ_RAII js::gc::AutoMaybeStartBackgroundAllocation |  | ||||||
| { |  | ||||||
|     JSRuntime* runtime; |  | ||||||
| 
 |  | ||||||
|   public: |  | ||||||
|     AutoMaybeStartBackgroundAllocation() |  | ||||||
|       : runtime(nullptr) |  | ||||||
|     {} |  | ||||||
| 
 |  | ||||||
|     void tryToStartBackgroundAllocation(JSRuntime* rt) { |  | ||||||
|         runtime = rt; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ~AutoMaybeStartBackgroundAllocation() { |  | ||||||
|         if (runtime) |  | ||||||
|             runtime->gc.startBackgroundAllocTaskIfIdle(); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void | void | ||||||
| GCRuntime::startBackgroundAllocTaskIfIdle() | GCRuntime::startBackgroundAllocTaskIfIdle() | ||||||
| { | { | ||||||
|  | @ -547,7 +523,7 @@ GCRuntime::getOrAllocChunk(const AutoLockGC& lock, | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (wantBackgroundAllocation(lock)) |     if (wantBackgroundAllocation(lock)) | ||||||
|         maybeStartBackgroundAllocation.tryToStartBackgroundAllocation(rt); |         maybeStartBackgroundAllocation.tryToStartBackgroundAllocation(rt->gc); | ||||||
| 
 | 
 | ||||||
|     return chunk; |     return chunk; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1387,6 +1387,30 @@ class MOZ_RAII AutoEnterIteration { | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // After pulling a Chunk out of the empty chunks pool, we want to run the
 | ||||||
|  | // background allocator to refill it. The code that takes Chunks does so under
 | ||||||
|  | // the GC lock. We need to start the background allocation under the helper
 | ||||||
|  | // threads lock. To avoid lock inversion we have to delay the start until after
 | ||||||
|  | // we are outside the GC lock. This class handles that delay automatically.
 | ||||||
|  | class MOZ_RAII AutoMaybeStartBackgroundAllocation | ||||||
|  | { | ||||||
|  |     GCRuntime* gc; | ||||||
|  | 
 | ||||||
|  |   public: | ||||||
|  |     AutoMaybeStartBackgroundAllocation() | ||||||
|  |       : gc(nullptr) | ||||||
|  |     {} | ||||||
|  | 
 | ||||||
|  |     void tryToStartBackgroundAllocation(GCRuntime& gc) { | ||||||
|  |         this->gc = &gc; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     ~AutoMaybeStartBackgroundAllocation() { | ||||||
|  |         if (gc) | ||||||
|  |             gc->startBackgroundAllocTaskIfIdle(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| #ifdef JS_GC_ZEAL | #ifdef JS_GC_ZEAL | ||||||
| 
 | 
 | ||||||
| inline bool | inline bool | ||||||
|  |  | ||||||
|  | @ -1002,7 +1002,7 @@ struct Chunk | ||||||
|     void decommitAllArenasWithoutUnlocking(const AutoLockGC& lock); |     void decommitAllArenasWithoutUnlocking(const AutoLockGC& lock); | ||||||
| 
 | 
 | ||||||
|     static Chunk* allocate(JSRuntime* rt); |     static Chunk* allocate(JSRuntime* rt); | ||||||
|     inline void init(JSRuntime* rt); |     void init(JSRuntime* rt); | ||||||
| 
 | 
 | ||||||
|   private: |   private: | ||||||
|     void decommitAllArenas(JSRuntime* rt); |     void decommitAllArenas(JSRuntime* rt); | ||||||
|  |  | ||||||
|  | @ -82,16 +82,41 @@ struct js::Nursery::Canary | ||||||
| }; | }; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | inline void | ||||||
|  | js::Nursery::NurseryChunk::poisonAndInit(JSRuntime* rt, uint8_t poison) | ||||||
|  | { | ||||||
|  |     JS_POISON(this, poison, ChunkSize); | ||||||
|  |     init(rt); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline void | ||||||
|  | js::Nursery::NurseryChunk::init(JSRuntime* rt) | ||||||
|  | { | ||||||
|  |     new (&trailer) gc::ChunkTrailer(rt, &rt->gc.storeBuffer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* static */ inline js::Nursery::NurseryChunk* | ||||||
|  | js::Nursery::NurseryChunk::fromChunk(Chunk* chunk) | ||||||
|  | { | ||||||
|  |     return reinterpret_cast<NurseryChunk*>(chunk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline Chunk* | ||||||
|  | js::Nursery::NurseryChunk::toChunk(JSRuntime* rt) | ||||||
|  | { | ||||||
|  |     auto chunk = reinterpret_cast<Chunk*>(this); | ||||||
|  |     chunk->init(rt); | ||||||
|  |     return chunk; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| js::Nursery::Nursery(JSRuntime* rt) | js::Nursery::Nursery(JSRuntime* rt) | ||||||
|   : runtime_(rt) |   : runtime_(rt) | ||||||
|   , position_(0) |   , position_(0) | ||||||
|   , currentStart_(0) |   , currentStartChunk_(0) | ||||||
|  |   , currentStartPosition_(0) | ||||||
|   , currentEnd_(0) |   , currentEnd_(0) | ||||||
|   , heapStart_(0) |  | ||||||
|   , heapEnd_(0) |  | ||||||
|   , currentChunk_(0) |   , currentChunk_(0) | ||||||
|   , numActiveChunks_(0) |   , maxNurseryChunks_(0) | ||||||
|   , numNurseryChunks_(0) |  | ||||||
|   , previousPromotionRate_(0) |   , previousPromotionRate_(0) | ||||||
|   , profileThreshold_(0) |   , profileThreshold_(0) | ||||||
|   , enableProfiling_(false) |   , enableProfiling_(false) | ||||||
|  | @ -104,13 +129,13 @@ js::Nursery::Nursery(JSRuntime* rt) | ||||||
| {} | {} | ||||||
| 
 | 
 | ||||||
| bool | bool | ||||||
| js::Nursery::init(uint32_t maxNurseryBytes) | js::Nursery::init(uint32_t maxNurseryBytes, AutoLockGC& lock) | ||||||
| { | { | ||||||
|     /* maxNurseryBytes parameter is rounded down to a multiple of chunk size. */ |     /* maxNurseryBytes parameter is rounded down to a multiple of chunk size. */ | ||||||
|     numNurseryChunks_ = maxNurseryBytes >> ChunkShift; |     maxNurseryChunks_ = maxNurseryBytes >> ChunkShift; | ||||||
| 
 | 
 | ||||||
|     /* If no chunks are specified then the nursery is permenantly disabled. */ |     /* If no chunks are specified then the nursery is permenantly disabled. */ | ||||||
|     if (numNurseryChunks_ == 0) |     if (maxNurseryChunks_ == 0) | ||||||
|         return true; |         return true; | ||||||
| 
 | 
 | ||||||
|     if (!mallocedBuffers.init()) |     if (!mallocedBuffers.init()) | ||||||
|  | @ -119,21 +144,16 @@ js::Nursery::init(uint32_t maxNurseryBytes) | ||||||
|     if (!cellsWithUid_.init()) |     if (!cellsWithUid_.init()) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     void* heap = MapAlignedPages(nurserySize(), Alignment); |  | ||||||
|     if (!heap) |  | ||||||
|         return false; |  | ||||||
| 
 |  | ||||||
|     freeMallocedBuffersTask = js_new<FreeMallocedBuffersTask>(runtime()->defaultFreeOp()); |     freeMallocedBuffersTask = js_new<FreeMallocedBuffersTask>(runtime()->defaultFreeOp()); | ||||||
|     if (!freeMallocedBuffersTask || !freeMallocedBuffersTask->init()) |     if (!freeMallocedBuffersTask || !freeMallocedBuffersTask->init()) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|     heapStart_ = uintptr_t(heap); |     updateNumChunksLocked(1, lock); | ||||||
|     heapEnd_ = heapStart_ + nurserySize(); |     if (numChunks() == 0) | ||||||
|     currentStart_ = start(); |         return false; | ||||||
|     numActiveChunks_ = numNurseryChunks_; | 
 | ||||||
|     JS_POISON(heap, JS_FRESH_NURSERY_PATTERN, nurserySize()); |  | ||||||
|     updateNumActiveChunks(1); |  | ||||||
|     setCurrentChunk(0); |     setCurrentChunk(0); | ||||||
|  |     setStartPosition(); | ||||||
| 
 | 
 | ||||||
|     char* env = getenv("JS_GC_PROFILE_NURSERY"); |     char* env = getenv("JS_GC_PROFILE_NURSERY"); | ||||||
|     if (env) { |     if (env) { | ||||||
|  | @ -150,15 +170,16 @@ js::Nursery::init(uint32_t maxNurseryBytes) | ||||||
|     PodZero(&profileTimes_); |     PodZero(&profileTimes_); | ||||||
|     PodZero(&totalTimes_); |     PodZero(&totalTimes_); | ||||||
| 
 | 
 | ||||||
|  |     if (!runtime()->gc.storeBuffer.enable()) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|     MOZ_ASSERT(isEnabled()); |     MOZ_ASSERT(isEnabled()); | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| js::Nursery::~Nursery() | js::Nursery::~Nursery() | ||||||
| { | { | ||||||
|     if (start()) |     disable(); | ||||||
|         UnmapPages((void*)start(), nurserySize()); |  | ||||||
| 
 |  | ||||||
|     js_delete(freeMallocedBuffersTask); |     js_delete(freeMallocedBuffersTask); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -169,13 +190,20 @@ js::Nursery::enable() | ||||||
|     MOZ_ASSERT(!runtime()->gc.isVerifyPreBarriersEnabled()); |     MOZ_ASSERT(!runtime()->gc.isVerifyPreBarriersEnabled()); | ||||||
|     if (isEnabled()) |     if (isEnabled()) | ||||||
|         return; |         return; | ||||||
|     updateNumActiveChunks(1); | 
 | ||||||
|  |     updateNumChunks(1); | ||||||
|  |     if (numChunks() == 0) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|     setCurrentChunk(0); |     setCurrentChunk(0); | ||||||
|     currentStart_ = position(); |     setStartPosition(); | ||||||
| #ifdef JS_GC_ZEAL | #ifdef JS_GC_ZEAL | ||||||
|     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) |     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) | ||||||
|         enterZealMode(); |         enterZealMode(); | ||||||
| #endif | #endif | ||||||
|  | 
 | ||||||
|  |     MOZ_ALWAYS_TRUE(runtime()->gc.storeBuffer.enable()); | ||||||
|  |     return; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  | @ -184,8 +212,9 @@ js::Nursery::disable() | ||||||
|     MOZ_ASSERT(isEmpty()); |     MOZ_ASSERT(isEmpty()); | ||||||
|     if (!isEnabled()) |     if (!isEnabled()) | ||||||
|         return; |         return; | ||||||
|     updateNumActiveChunks(0); |     updateNumChunks(0); | ||||||
|     currentEnd_ = 0; |     currentEnd_ = 0; | ||||||
|  |     runtime()->gc.storeBuffer.disable(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool | bool | ||||||
|  | @ -194,15 +223,19 @@ js::Nursery::isEmpty() const | ||||||
|     MOZ_ASSERT(runtime_); |     MOZ_ASSERT(runtime_); | ||||||
|     if (!isEnabled()) |     if (!isEnabled()) | ||||||
|         return true; |         return true; | ||||||
|     MOZ_ASSERT_IF(!runtime_->hasZealMode(ZealMode::GenerationalGC), currentStart_ == start()); | 
 | ||||||
|     return position() == currentStart_; |     if (!runtime_->hasZealMode(ZealMode::GenerationalGC)) { | ||||||
|  |         MOZ_ASSERT(currentStartChunk_ == 0); | ||||||
|  |         MOZ_ASSERT(currentStartPosition_ == chunk(0).start()); | ||||||
|  |     } | ||||||
|  |     return position() == currentStartPosition_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef JS_GC_ZEAL | #ifdef JS_GC_ZEAL | ||||||
| void | void | ||||||
| js::Nursery::enterZealMode() { | js::Nursery::enterZealMode() { | ||||||
|     if (isEnabled()) |     if (isEnabled()) | ||||||
|         numActiveChunks_ = numNurseryChunks_; |         updateNumChunks(maxNurseryChunks_); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  | @ -210,7 +243,7 @@ js::Nursery::leaveZealMode() { | ||||||
|     if (isEnabled()) { |     if (isEnabled()) { | ||||||
|         MOZ_ASSERT(isEmpty()); |         MOZ_ASSERT(isEmpty()); | ||||||
|         setCurrentChunk(0); |         setCurrentChunk(0); | ||||||
|         currentStart_ = start(); |         setStartPosition(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| #endif // JS_GC_ZEAL
 | #endif // JS_GC_ZEAL
 | ||||||
|  | @ -260,7 +293,7 @@ js::Nursery::allocate(size_t size) | ||||||
| { | { | ||||||
|     MOZ_ASSERT(isEnabled()); |     MOZ_ASSERT(isEnabled()); | ||||||
|     MOZ_ASSERT(!runtime()->isHeapBusy()); |     MOZ_ASSERT(!runtime()->isHeapBusy()); | ||||||
|     MOZ_ASSERT(position() >= currentStart_); |     MOZ_ASSERT_IF(currentChunk_ == currentStartChunk_, position() >= currentStartPosition_); | ||||||
|     MOZ_ASSERT(position() % gc::CellSize == 0); |     MOZ_ASSERT(position() % gc::CellSize == 0); | ||||||
|     MOZ_ASSERT(size % gc::CellSize == 0); |     MOZ_ASSERT(size % gc::CellSize == 0); | ||||||
| 
 | 
 | ||||||
|  | @ -271,7 +304,7 @@ js::Nursery::allocate(size_t size) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     if (currentEnd() < position() + size) { |     if (currentEnd() < position() + size) { | ||||||
|         if (currentChunk_ + 1 == numActiveChunks_) |         if (currentChunk_ + 1 == numChunks()) | ||||||
|             return nullptr; |             return nullptr; | ||||||
|         setCurrentChunk(currentChunk_ + 1); |         setCurrentChunk(currentChunk_ + 1); | ||||||
|     } |     } | ||||||
|  | @ -366,10 +399,10 @@ Nursery::setForwardingPointer(void* oldData, void* newData, bool direct) | ||||||
| { | { | ||||||
|     MOZ_ASSERT(isInside(oldData)); |     MOZ_ASSERT(isInside(oldData)); | ||||||
| 
 | 
 | ||||||
|     // Bug 1196210: If a zero-capacity header lands in the last 2 words of the
 |     // Bug 1196210: If a zero-capacity header lands in the last 2 words of a
 | ||||||
|     // jemalloc chunk abutting the start of the nursery, the (invalid) newData
 |     // jemalloc chunk abutting the start of a nursery chunk, the (invalid)
 | ||||||
|     // pointer will appear to be "inside" the nursery.
 |     // newData pointer will appear to be "inside" the nursery.
 | ||||||
|     MOZ_ASSERT(!isInside(newData) || uintptr_t(newData) == heapStart_); |     MOZ_ASSERT(!isInside(newData) || (uintptr_t(newData) & ChunkMask) == 0); | ||||||
| 
 | 
 | ||||||
|     if (direct) { |     if (direct) { | ||||||
|         *reinterpret_cast<void**>(oldData) = newData; |         *reinterpret_cast<void**>(oldData) = newData; | ||||||
|  | @ -539,7 +572,7 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason) | ||||||
|     AutoDisableProxyCheck disableStrictProxyChecking(rt); |     AutoDisableProxyCheck disableStrictProxyChecking(rt); | ||||||
|     mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion; |     mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion; | ||||||
| 
 | 
 | ||||||
|     size_t initialUsedSpace = position() - start(); |     size_t initialUsedSpace = usedSpace(); | ||||||
| 
 | 
 | ||||||
|     // Move objects pointed to by roots from the nursery to the major heap.
 |     // Move objects pointed to by roots from the nursery to the major heap.
 | ||||||
|     TenuringTracer mover(rt, this); |     TenuringTracer mover(rt, this); | ||||||
|  | @ -697,10 +730,10 @@ js::Nursery::collect(JSRuntime* rt, JS::gcreason::Reason reason) | ||||||
|             printProfileHeader(); |             printProfileHeader(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         fprintf(stderr, "MinorGC: %20s %5.1f%% %4d ", |         fprintf(stderr, "MinorGC: %20s %5.1f%% %4u ", | ||||||
|                 JS::gcreason::ExplainReason(reason), |                 JS::gcreason::ExplainReason(reason), | ||||||
|                 promotionRate * 100, |                 promotionRate * 100, | ||||||
|                 numActiveChunks_); |                 numChunks()); | ||||||
|         printProfileTimes(profileTimes_); |         printProfileTimes(profileTimes_); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -768,40 +801,69 @@ js::Nursery::sweep() | ||||||
| 
 | 
 | ||||||
| #ifdef JS_GC_ZEAL | #ifdef JS_GC_ZEAL | ||||||
|     /* Poison the nursery contents so touching a freed object will crash. */ |     /* Poison the nursery contents so touching a freed object will crash. */ | ||||||
|     JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, nurserySize()); |     for (unsigned i = 0; i < numChunks(); i++) | ||||||
|     for (int i = 0; i < numNurseryChunks_; ++i) |         chunk(i).poisonAndInit(runtime(), JS_SWEPT_NURSERY_PATTERN); | ||||||
|         initChunk(i); |  | ||||||
| 
 | 
 | ||||||
|     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) { |     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) { | ||||||
|         MOZ_ASSERT(numActiveChunks_ == numNurseryChunks_); |  | ||||||
| 
 |  | ||||||
|         /* Only reset the alloc point when we are close to the end. */ |         /* Only reset the alloc point when we are close to the end. */ | ||||||
|         if (currentChunk_ + 1 == numNurseryChunks_) |         if (currentChunk_ + 1 == numChunks()) | ||||||
|             setCurrentChunk(0); |             setCurrentChunk(0); | ||||||
|     } else |     } else | ||||||
| #endif | #endif | ||||||
|     { |     { | ||||||
| #ifdef JS_CRASH_DIAGNOSTICS | #ifdef JS_CRASH_DIAGNOSTICS | ||||||
|         JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, allocationEnd() - start()); |         for (unsigned i = 0; i < numChunks(); ++i) | ||||||
|         for (int i = 0; i < numActiveChunks_; ++i) |             chunk(i).poisonAndInit(runtime(), JS_SWEPT_NURSERY_PATTERN); | ||||||
|             initChunk(i); |  | ||||||
| #endif | #endif | ||||||
|         setCurrentChunk(0); |         setCurrentChunk(0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* Set current start position for isEmpty checks. */ |     /* Set current start position for isEmpty checks. */ | ||||||
|     currentStart_ = position(); |     setStartPosition(); | ||||||
|     MemProfiler::SweepNursery(runtime()); |     MemProfiler::SweepNursery(runtime()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | size_t | ||||||
|  | js::Nursery::usedSpace() const | ||||||
|  | { | ||||||
|  |     MOZ_ASSERT(currentChunk_ >= currentStartChunk_); | ||||||
|  |     MOZ_ASSERT(currentStartPosition_ - chunk(currentStartChunk_).start() <= NurseryChunkUsableSize); | ||||||
|  |     MOZ_ASSERT(position_ - chunk(currentChunk_).start() <= NurseryChunkUsableSize); | ||||||
|  | 
 | ||||||
|  |     if (currentChunk_ == currentStartChunk_) | ||||||
|  |         return position_ - currentStartPosition_; | ||||||
|  | 
 | ||||||
|  |     size_t bytes = (chunk(currentStartChunk_).end() - currentStartPosition_) + | ||||||
|  |                    ((currentChunk_ - currentStartChunk_ - 1) * NurseryChunkUsableSize) + | ||||||
|  |                    position_ - chunk(currentChunk_).start(); | ||||||
|  | 
 | ||||||
|  |     MOZ_ASSERT(bytes <= numChunks() * NurseryChunkUsableSize); | ||||||
|  | 
 | ||||||
|  |     return bytes; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MOZ_ALWAYS_INLINE void | ||||||
|  | js::Nursery::setCurrentChunk(unsigned chunkno) | ||||||
|  | { | ||||||
|  |     MOZ_ASSERT(chunkno < maxChunks()); | ||||||
|  |     MOZ_ASSERT(chunkno < numChunks()); | ||||||
|  |     currentChunk_ = chunkno; | ||||||
|  |     position_ = chunk(chunkno).start(); | ||||||
|  |     currentEnd_ = chunk(chunkno).end(); | ||||||
|  |     chunk(chunkno).poisonAndInit(runtime(), JS_FRESH_NURSERY_PATTERN); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MOZ_ALWAYS_INLINE void | ||||||
|  | js::Nursery::setStartPosition() | ||||||
|  | { | ||||||
|  |     currentStartChunk_ = currentChunk_; | ||||||
|  |     currentStartPosition_ = position(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void | void | ||||||
| js::Nursery::growAllocableSpace() | js::Nursery::growAllocableSpace() | ||||||
| { | { | ||||||
| #ifdef JS_GC_ZEAL |     updateNumChunks(Min(numChunks() * 2, maxNurseryChunks_)); | ||||||
|     MOZ_ASSERT_IF(runtime()->hasZealMode(ZealMode::GenerationalGC), |  | ||||||
|                   numActiveChunks_ == numNurseryChunks_); |  | ||||||
| #endif |  | ||||||
|     updateNumActiveChunks(Min(numActiveChunks_ * 2, numNurseryChunks_)); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  | @ -811,30 +873,51 @@ js::Nursery::shrinkAllocableSpace() | ||||||
|     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) |     if (runtime()->hasZealMode(ZealMode::GenerationalGC)) | ||||||
|         return; |         return; | ||||||
| #endif | #endif | ||||||
|     updateNumActiveChunks(Max(numActiveChunks_ - 1, 1)); |     updateNumChunks(Max(numChunks() - 1, 1u)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
| js::Nursery::updateNumActiveChunks(int newCount) | js::Nursery::updateNumChunks(unsigned newCount) | ||||||
| { | { | ||||||
| #ifndef JS_GC_ZEAL |     if (numChunks() != newCount) { | ||||||
|     int priorChunks = numActiveChunks_; |         AutoLockGC lock(runtime()); | ||||||
| #endif |         updateNumChunksLocked(newCount, lock); | ||||||
|     numActiveChunks_ = newCount; |     } | ||||||
| 
 | } | ||||||
|     // In zeal mode, we want to keep the unused memory poisoned so that we
 | 
 | ||||||
|     // will crash sooner. Avoid decommit in that case to avoid having the
 | void | ||||||
|     // system zero the pages.
 | js::Nursery::updateNumChunksLocked(unsigned newCount, AutoLockGC& lock) | ||||||
| #ifndef JS_GC_ZEAL | { | ||||||
|     if (numActiveChunks_ < priorChunks) { |     // The GC nursery is an optimization and so if we fail to allocate nursery
 | ||||||
|         uintptr_t decommitStart = chunk(numActiveChunks_).start(); |     // chunks we do not report an error.
 | ||||||
|         uintptr_t decommitSize = chunk(priorChunks - 1).start() + ChunkSize - decommitStart; | 
 | ||||||
|         MOZ_ASSERT(decommitSize != 0); |     unsigned priorCount = numChunks(); | ||||||
|         MOZ_ASSERT(decommitStart == AlignBytes(decommitStart, Alignment)); |     MOZ_ASSERT(priorCount != newCount); | ||||||
|         MOZ_ASSERT(decommitSize == AlignBytes(decommitSize, Alignment)); | 
 | ||||||
|         MarkPagesUnused((void*)decommitStart, decommitSize); |     AutoMaybeStartBackgroundAllocation maybeBgAlloc; | ||||||
|  | 
 | ||||||
|  |     if (newCount < priorCount) { | ||||||
|  |         // Shrink the nursery and free unused chunks.
 | ||||||
|  |         for (unsigned i = newCount; i < priorCount; i++) | ||||||
|  |             runtime()->gc.recycleChunk(chunk(i).toChunk(runtime()), lock); | ||||||
|  |         chunks_.shrinkTo(newCount); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Grow the nursery and allocate new chunks.
 | ||||||
|  |     if (!chunks_.resize(newCount)) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     for (unsigned i = priorCount; i < newCount; i++) { | ||||||
|  |         auto newChunk = runtime()->gc.getOrAllocChunk(lock, maybeBgAlloc); | ||||||
|  |         if (!newChunk) { | ||||||
|  |             chunks_.shrinkTo(i); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         chunks_[i] = NurseryChunk::fromChunk(newChunk); | ||||||
|  |         chunk(i).poisonAndInit(runtime(), JS_FRESH_NURSERY_PATTERN); | ||||||
|     } |     } | ||||||
| #endif // !defined(JS_GC_ZEAL)
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  |  | ||||||
|  | @ -121,15 +121,17 @@ class Nursery | ||||||
|     explicit Nursery(JSRuntime* rt); |     explicit Nursery(JSRuntime* rt); | ||||||
|     ~Nursery(); |     ~Nursery(); | ||||||
| 
 | 
 | ||||||
|     MOZ_MUST_USE bool init(uint32_t maxNurseryBytes); |     MOZ_MUST_USE bool init(uint32_t maxNurseryBytes, AutoLockGC& lock); | ||||||
| 
 | 
 | ||||||
|     bool exists() const { return numNurseryChunks_ != 0; } |     unsigned maxChunks() const { return maxNurseryChunks_; } | ||||||
|     size_t numChunks() const { return numNurseryChunks_; } |     unsigned numChunks() const { return chunks_.length(); } | ||||||
|     size_t nurserySize() const { return numNurseryChunks_ << ChunkShift; } | 
 | ||||||
|  |     bool exists() const { return maxChunks() != 0; } | ||||||
|  |     size_t nurserySize() const { return maxChunks() << ChunkShift; } | ||||||
| 
 | 
 | ||||||
|     void enable(); |     void enable(); | ||||||
|     void disable(); |     void disable(); | ||||||
|     bool isEnabled() const { return numActiveChunks_ != 0; } |     bool isEnabled() const { return numChunks() != 0; } | ||||||
| 
 | 
 | ||||||
|     /* Return true if no allocations have been made since the last collection. */ |     /* Return true if no allocations have been made since the last collection. */ | ||||||
|     bool isEmpty() const; |     bool isEmpty() const; | ||||||
|  | @ -140,7 +142,11 @@ class Nursery | ||||||
|      */ |      */ | ||||||
|     MOZ_ALWAYS_INLINE bool isInside(gc::Cell* cellp) const = delete; |     MOZ_ALWAYS_INLINE bool isInside(gc::Cell* cellp) const = delete; | ||||||
|     MOZ_ALWAYS_INLINE bool isInside(const void* p) const { |     MOZ_ALWAYS_INLINE bool isInside(const void* p) const { | ||||||
|         return uintptr_t(p) >= heapStart_ && uintptr_t(p) < heapEnd_; |         for (auto chunk : chunks_) { | ||||||
|  |             if (uintptr_t(p) - chunk->start() < gc::ChunkSize) | ||||||
|  |                 return true; | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|     } |     } | ||||||
|     template<typename T> |     template<typename T> | ||||||
|     bool isInside(const SharedMem<T>& p) const { |     bool isInside(const SharedMem<T>& p) const { | ||||||
|  | @ -212,10 +218,7 @@ class Nursery | ||||||
|     void queueSweepAction(SweepThunk thunk, void* data); |     void queueSweepAction(SweepThunk thunk, void* data); | ||||||
| 
 | 
 | ||||||
|     size_t sizeOfHeapCommitted() const { |     size_t sizeOfHeapCommitted() const { | ||||||
|         return numActiveChunks_ * gc::ChunkSize; |         return numChunks() * gc::ChunkSize; | ||||||
|     } |  | ||||||
|     size_t sizeOfHeapDecommitted() const { |  | ||||||
|         return (numNurseryChunks_ - numActiveChunks_) * gc::ChunkSize; |  | ||||||
|     } |     } | ||||||
|     size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const { |     size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const { | ||||||
|         size_t total = 0; |         size_t total = 0; | ||||||
|  | @ -225,17 +228,13 @@ class Nursery | ||||||
|         return total; |         return total; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     MOZ_ALWAYS_INLINE uintptr_t start() const { |     size_t usedSpace() const; | ||||||
|         return heapStart_; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     MOZ_ALWAYS_INLINE uintptr_t heapEnd() const { |  | ||||||
|         return heapEnd_; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     // Free space remaining, not counting chunk trailers.
 |     // Free space remaining, not counting chunk trailers.
 | ||||||
|     MOZ_ALWAYS_INLINE size_t approxFreeSpace() const { |     MOZ_ALWAYS_INLINE size_t freeSpace() const { | ||||||
|         return heapEnd_ - position_; |         MOZ_ASSERT(currentEnd_ - position_ <= NurseryChunkUsableSize); | ||||||
|  |         return (currentEnd_ - position_) + | ||||||
|  |                (numChunks() - currentChunk_ - 1) * NurseryChunkUsableSize; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #ifdef JS_GC_ZEAL | #ifdef JS_GC_ZEAL | ||||||
|  | @ -247,6 +246,22 @@ class Nursery | ||||||
|     void printTotalProfileTimes(); |     void printTotalProfileTimes(); | ||||||
| 
 | 
 | ||||||
|   private: |   private: | ||||||
|  |     /* The amount of space in the mapped nursery available to allocations. */ | ||||||
|  |     static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer); | ||||||
|  | 
 | ||||||
|  |     struct NurseryChunk { | ||||||
|  |         char data[NurseryChunkUsableSize]; | ||||||
|  |         gc::ChunkTrailer trailer; | ||||||
|  |         static NurseryChunk* fromChunk(gc::Chunk* chunk); | ||||||
|  |         void init(JSRuntime* rt); | ||||||
|  |         void poisonAndInit(JSRuntime* rt, uint8_t poison); | ||||||
|  |         uintptr_t start() const { return uintptr_t(&data); } | ||||||
|  |         uintptr_t end() const { return uintptr_t(&trailer); } | ||||||
|  |         gc::Chunk* toChunk(JSRuntime* rt); | ||||||
|  |     }; | ||||||
|  |     static_assert(sizeof(NurseryChunk) == gc::ChunkSize, | ||||||
|  |                   "Nursery chunk size must match gc::Chunk size."); | ||||||
|  | 
 | ||||||
|     /*
 |     /*
 | ||||||
|      * The start and end pointers are stored under the runtime so that we can |      * The start and end pointers are stored under the runtime so that we can | ||||||
|      * inline the isInsideNursery check into embedder code. Use the start() |      * inline the isInsideNursery check into embedder code. Use the start() | ||||||
|  | @ -254,27 +269,24 @@ class Nursery | ||||||
|      */ |      */ | ||||||
|     JSRuntime* runtime_; |     JSRuntime* runtime_; | ||||||
| 
 | 
 | ||||||
|  |     /* Vector of allocated chunks to allocate from. */ | ||||||
|  |     Vector<NurseryChunk*, 0, SystemAllocPolicy> chunks_; | ||||||
|  | 
 | ||||||
|     /* Pointer to the first unallocated byte in the nursery. */ |     /* Pointer to the first unallocated byte in the nursery. */ | ||||||
|     uintptr_t position_; |     uintptr_t position_; | ||||||
| 
 | 
 | ||||||
|     /* Pointer to the logical start of the Nursery. */ |     /* Pointer to the logical start of the Nursery. */ | ||||||
|     uintptr_t currentStart_; |     unsigned currentStartChunk_; | ||||||
|  |     uintptr_t currentStartPosition_; | ||||||
| 
 | 
 | ||||||
|     /* Pointer to the last byte of space in the current chunk. */ |     /* Pointer to the last byte of space in the current chunk. */ | ||||||
|     uintptr_t currentEnd_; |     uintptr_t currentEnd_; | ||||||
| 
 | 
 | ||||||
|     /* Pointer to first and last address of the total nursery allocation. */ |  | ||||||
|     uintptr_t heapStart_; |  | ||||||
|     uintptr_t heapEnd_; |  | ||||||
| 
 |  | ||||||
|     /* The index of the chunk that is currently being allocated from. */ |     /* The index of the chunk that is currently being allocated from. */ | ||||||
|     int currentChunk_; |     unsigned currentChunk_; | ||||||
| 
 | 
 | ||||||
|     /* The index after the last chunk that we will allocate from. */ |     /* Maximum number of chunks to allocate for the nursery. */ | ||||||
|     int numActiveChunks_; |     unsigned maxNurseryChunks_; | ||||||
| 
 |  | ||||||
|     /* Number of chunks allocated for the nursery. */ |  | ||||||
|     int numNurseryChunks_; |  | ||||||
| 
 | 
 | ||||||
|     /* Promotion rate for the previous minor collection. */ |     /* Promotion rate for the previous minor collection. */ | ||||||
|     double previousPromotionRate_; |     double previousPromotionRate_; | ||||||
|  | @ -346,42 +358,21 @@ class Nursery | ||||||
|     Canary* lastCanary_; |     Canary* lastCanary_; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     /* The amount of space in the mapped nursery available to allocations. */ |     NurseryChunk* allocChunk(); | ||||||
|     static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer); |  | ||||||
| 
 | 
 | ||||||
|     struct NurseryChunkLayout { |     NurseryChunk& chunk(unsigned index) const { | ||||||
|         char data[NurseryChunkUsableSize]; |         return *chunks_[index]; | ||||||
|         gc::ChunkTrailer trailer; |  | ||||||
|         uintptr_t start() const { return uintptr_t(&data); } |  | ||||||
|         uintptr_t end() const { return uintptr_t(&trailer); } |  | ||||||
|     }; |  | ||||||
|     static_assert(sizeof(NurseryChunkLayout) == gc::ChunkSize, |  | ||||||
|                   "Nursery chunk size must match gc::Chunk size."); |  | ||||||
|     NurseryChunkLayout& chunk(int index) const { |  | ||||||
|         MOZ_ASSERT(index < numNurseryChunks_); |  | ||||||
|         MOZ_ASSERT(start()); |  | ||||||
|         return reinterpret_cast<NurseryChunkLayout*>(start())[index]; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     MOZ_ALWAYS_INLINE void initChunk(int chunkno) { |     void setCurrentChunk(unsigned chunkno); | ||||||
|         gc::StoreBuffer* sb = JS::shadow::Runtime::asShadowRuntime(runtime())->gcStoreBufferPtr(); |     void setStartPosition(); | ||||||
|         new (&chunk(chunkno).trailer) gc::ChunkTrailer(runtime(), sb); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) { |     void updateNumChunks(unsigned newCount); | ||||||
|         MOZ_ASSERT(chunkno < numNurseryChunks_); |     void updateNumChunksLocked(unsigned newCount, AutoLockGC& lock); | ||||||
|         MOZ_ASSERT(chunkno < numActiveChunks_); |  | ||||||
|         currentChunk_ = chunkno; |  | ||||||
|         position_ = chunk(chunkno).start(); |  | ||||||
|         currentEnd_ = chunk(chunkno).end(); |  | ||||||
|         initChunk(chunkno); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void updateNumActiveChunks(int newCount); |  | ||||||
| 
 | 
 | ||||||
|     MOZ_ALWAYS_INLINE uintptr_t allocationEnd() const { |     MOZ_ALWAYS_INLINE uintptr_t allocationEnd() const { | ||||||
|         MOZ_ASSERT(numActiveChunks_ > 0); |         MOZ_ASSERT(numChunks() > 0); | ||||||
|         return chunk(numActiveChunks_ - 1).end(); |         return chunks_.back()->end(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     MOZ_ALWAYS_INLINE uintptr_t currentEnd() const { |     MOZ_ALWAYS_INLINE uintptr_t currentEnd() const { | ||||||
|  |  | ||||||
|  | @ -138,7 +138,7 @@ js::gc::AllocateWholeCellSet(Arena* arena) | ||||||
|         return nullptr; |         return nullptr; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (nursery.approxFreeSpace() < ArenaCellSet::NurseryFreeThresholdBytes) |     if (nursery.freeSpace() < ArenaCellSet::NurseryFreeThresholdBytes) | ||||||
|         rt->gc.storeBuffer.setAboutToOverflow(); |         rt->gc.storeBuffer.setAboutToOverflow(); | ||||||
| 
 | 
 | ||||||
|     auto cells = static_cast<ArenaCellSet*>(data); |     auto cells = static_cast<ArenaCellSet*>(data); | ||||||
|  |  | ||||||
|  | @ -384,7 +384,7 @@ class StoreBuffer | ||||||
|     { |     { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool enable(); |     MOZ_MUST_USE bool enable(); | ||||||
|     void disable(); |     void disable(); | ||||||
|     bool isEnabled() const { return enabled_; } |     bool isEnabled() const { return enabled_; } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1031,11 +1031,13 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) | ||||||
|     if (!rootsHash.init(256)) |     if (!rootsHash.init(256)) | ||||||
|         return false; |         return false; | ||||||
| 
 | 
 | ||||||
|  |     { | ||||||
|  |         AutoLockGC lock(rt); | ||||||
|  | 
 | ||||||
|         /*
 |         /*
 | ||||||
|          * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes |          * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes | ||||||
|          * for default backward API compatibility. |          * for default backward API compatibility. | ||||||
|          */ |          */ | ||||||
|     AutoLockGC lock(rt); |  | ||||||
|         MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_BYTES, maxbytes, lock)); |         MOZ_ALWAYS_TRUE(tunables.setParameter(JSGC_MAX_BYTES, maxbytes, lock)); | ||||||
|         setMaxMallocBytes(maxbytes); |         setMaxMallocBytes(maxbytes); | ||||||
| 
 | 
 | ||||||
|  | @ -1045,7 +1047,7 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) | ||||||
| 
 | 
 | ||||||
|         jitReleaseNumber = majorGCNumber + JIT_SCRIPT_RELEASE_TYPES_PERIOD; |         jitReleaseNumber = majorGCNumber + JIT_SCRIPT_RELEASE_TYPES_PERIOD; | ||||||
| 
 | 
 | ||||||
|     if (!nursery.init(maxNurseryBytes)) |         if (!nursery.init(maxNurseryBytes, lock)) | ||||||
|             return false; |             return false; | ||||||
| 
 | 
 | ||||||
|         if (!nursery.isEnabled()) { |         if (!nursery.isEnabled()) { | ||||||
|  | @ -1053,8 +1055,7 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes) | ||||||
|             ++rt->gc.generationalDisabled; |             ++rt->gc.generationalDisabled; | ||||||
|         } else { |         } else { | ||||||
|             MOZ_ASSERT(nursery.nurserySize() > 0); |             MOZ_ASSERT(nursery.nurserySize() > 0); | ||||||
|         if (!storeBuffer.enable()) |         } | ||||||
|             return false; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| #ifdef JS_GC_ZEAL | #ifdef JS_GC_ZEAL | ||||||
|  | @ -6494,7 +6495,6 @@ GCRuntime::disableGenerationalGC() | ||||||
|     if (isGenerationalGCEnabled()) { |     if (isGenerationalGCEnabled()) { | ||||||
|         evictNursery(JS::gcreason::API); |         evictNursery(JS::gcreason::API); | ||||||
|         nursery.disable(); |         nursery.disable(); | ||||||
|         storeBuffer.disable(); |  | ||||||
|     } |     } | ||||||
|     ++rt->gc.generationalDisabled; |     ++rt->gc.generationalDisabled; | ||||||
| } | } | ||||||
|  | @ -6504,10 +6504,8 @@ GCRuntime::enableGenerationalGC() | ||||||
| { | { | ||||||
|     MOZ_ASSERT(generationalDisabled > 0); |     MOZ_ASSERT(generationalDisabled > 0); | ||||||
|     --generationalDisabled; |     --generationalDisabled; | ||||||
|     if (generationalDisabled == 0) { |     if (generationalDisabled == 0) | ||||||
|         nursery.enable(); |         nursery.enable(); | ||||||
|         storeBuffer.enable(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool | bool | ||||||
|  |  | ||||||
|  | @ -532,7 +532,6 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim | ||||||
| 
 | 
 | ||||||
|     rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf); |     rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf); | ||||||
|     rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted(); |     rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted(); | ||||||
|     rtSizes->gc.nurseryDecommitted += gc.nursery.sizeOfHeapDecommitted(); |  | ||||||
|     rtSizes->gc.nurseryMallocedBuffers += gc.nursery.sizeOfMallocedBuffers(mallocSizeOf); |     rtSizes->gc.nurseryMallocedBuffers += gc.nursery.sizeOfMallocedBuffers(mallocSizeOf); | ||||||
|     gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); |     gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -491,16 +491,16 @@ class TypedArrayObjectTemplate : public TypedArrayObject | ||||||
|             // If the buffer is for an inline typed object, the data pointer
 |             // If the buffer is for an inline typed object, the data pointer
 | ||||||
|             // may be in the nursery, so include a barrier to make sure this
 |             // may be in the nursery, so include a barrier to make sure this
 | ||||||
|             // object is updated if that typed object moves.
 |             // object is updated if that typed object moves.
 | ||||||
|             if (!IsInsideNursery(obj) && cx->runtime()->gc.nursery.isInside(buffer->dataPointerEither())) { |             auto ptr = buffer->dataPointerEither(); | ||||||
|                 // Shared buffer data should never be nursery-allocated, so
 |             if (!IsInsideNursery(obj) && cx->runtime()->gc.nursery.isInside(ptr)) { | ||||||
|                 // we need to fail here if isSharedMemory.  However, mmap()
 |                 // Shared buffer data should never be nursery-allocated, so we
 | ||||||
|                 // can place a SharedArrayRawBuffer up against the bottom end
 |                 // need to fail here if isSharedMemory.  However, mmap() can
 | ||||||
|                 // of the nursery, and a zero-length buffer will erroneously be
 |                 // place a SharedArrayRawBuffer up against the bottom end of a
 | ||||||
|  |                 // nursery chunk, and a zero-length buffer will erroneously be
 | ||||||
|                 // perceived as being inside the nursery; sidestep that.
 |                 // perceived as being inside the nursery; sidestep that.
 | ||||||
|                 if (isSharedMemory) { |                 if (isSharedMemory) { | ||||||
|                     MOZ_ASSERT(buffer->byteLength() == 0 && |                     MOZ_ASSERT(buffer->byteLength() == 0 && | ||||||
|                                cx->runtime()->gc.nursery.start() == |                                (uintptr_t(ptr.unwrapValue()) & gc::ChunkMask) == 0); | ||||||
|                                    buffer->dataPointerEither().unwrapValue()); |  | ||||||
|                 } else { |                 } else { | ||||||
|                     cx->runtime()->gc.storeBuffer.putWholeCell(obj); |                     cx->runtime()->gc.storeBuffer.putWholeCell(obj); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  | @ -2631,11 +2631,6 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats, | ||||||
|         "GC arenas in non-empty chunks that is decommitted, i.e. it takes up " |         "GC arenas in non-empty chunks that is decommitted, i.e. it takes up " | ||||||
|         "address space but no physical memory or swap space."); |         "address space but no physical memory or swap space."); | ||||||
| 
 | 
 | ||||||
|     REPORT_BYTES(rtPath2 + NS_LITERAL_CSTRING("runtime/gc/nursery-decommitted"), |  | ||||||
|         KIND_NONHEAP, rtStats.runtime.gc.nurseryDecommitted, |  | ||||||
|         "Memory allocated to the GC's nursery that is decommitted, i.e. it takes up " |  | ||||||
|         "address space but no physical memory or swap space."); |  | ||||||
| 
 |  | ||||||
|     REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"), |     REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"), | ||||||
|         rtStats.gcHeapUnusedChunks, |         rtStats.gcHeapUnusedChunks, | ||||||
|         "Empty GC chunks which will soon be released unless claimed for new " |         "Empty GC chunks which will soon be released unless claimed for new " | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Jon Coppeard
						Jon Coppeard