1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sw=2 et tw=80:
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #ifndef gc_Nursery_h
9 #define gc_Nursery_h
10
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/EnumeratedArray.h"
13 #include "mozilla/Maybe.h"
14 #include "mozilla/TimeStamp.h"
15
16 #include "gc/GCParallelTask.h"
17 #include "gc/Heap.h"
18 #include "js/AllocPolicy.h"
19 #include "js/Class.h"
20 #include "js/GCAPI.h"
21 #include "js/HeapAPI.h"
22 #include "js/TracingAPI.h"
23 #include "js/TypeDecls.h"
24 #include "js/Vector.h"
25 #include "util/Text.h"
26
27 #define FOR_EACH_NURSERY_PROFILE_TIME(_) \
28 /* Key Header text */ \
29 _(Total, "total") \
30 _(TraceValues, "mkVals") \
31 _(TraceCells, "mkClls") \
32 _(TraceSlots, "mkSlts") \
33 _(TraceWholeCells, "mcWCll") \
34 _(TraceGenericEntries, "mkGnrc") \
35 _(CheckHashTables, "ckTbls") \
36 _(MarkRuntime, "mkRntm") \
37 _(MarkDebugger, "mkDbgr") \
38 _(SweepCaches, "swpCch") \
39 _(CollectToObjFP, "colObj") \
40 _(CollectToStrFP, "colStr") \
41 _(ObjectsTenuredCallback, "tenCB") \
42 _(Sweep, "sweep") \
43 _(UpdateJitActivations, "updtIn") \
44 _(FreeMallocedBuffers, "frSlts") \
45 _(ClearStoreBuffer, "clrSB") \
46 _(ClearNursery, "clear") \
47 _(PurgeStringToAtomCache, "pStoA") \
48 _(Pretenure, "pretnr")
49
50 template <typename T>
51 class SharedMem;
52 class JSDependentString;
53
54 namespace js {
55
56 struct StringStats;
57 class AutoLockGCBgAlloc;
58 class ObjectElements;
59 class PlainObject;
60 class NativeObject;
61 class Nursery;
62 struct NurseryChunk;
63 class HeapSlot;
64 class JSONPrinter;
65 class MapObject;
66 class SetObject;
67 class TenuringTracer;
68
69 namespace gc {
70 class AutoGCSession;
71 class AutoMaybeStartBackgroundAllocation;
72 class AutoTraceSession;
73 struct Cell;
74 class GCSchedulingTunables;
75 class MinorCollectionTracer;
76 class RelocationOverlay;
77 class StringRelocationOverlay;
78 enum class AllocKind : uint8_t;
79 class TenuredCell;
80 } // namespace gc
81
82 namespace jit {
83 class MacroAssembler;
84 } // namespace jit
85
86 class NurseryDecommitTask : public GCParallelTask {
87 public:
88 explicit NurseryDecommitTask(gc::GCRuntime* gc);
89 bool reserveSpaceForBytes(size_t nbytes);
90
91 bool isEmpty(const AutoLockHelperThreadState& lock) const;
92
93 void queueChunk(NurseryChunk* chunk, const AutoLockHelperThreadState& lock);
94 void queueRange(size_t newCapacity, NurseryChunk& chunk,
95 const AutoLockHelperThreadState& lock);
96
97 private:
98 using NurseryChunkVector = Vector<NurseryChunk*, 0, SystemAllocPolicy>;
99
100 void run(AutoLockHelperThreadState& lock) override;
101
chunksToDecommit()102 NurseryChunkVector& chunksToDecommit() { return chunksToDecommit_.ref(); }
chunksToDecommit()103 const NurseryChunkVector& chunksToDecommit() const {
104 return chunksToDecommit_.ref();
105 }
106
107 MainThreadOrGCTaskData<NurseryChunkVector> chunksToDecommit_;
108
109 MainThreadOrGCTaskData<NurseryChunk*> partialChunk;
110 MainThreadOrGCTaskData<size_t> partialCapacity;
111 };
112
113 // Classes with JSCLASS_SKIP_NURSERY_FINALIZE or Wrapper classes with
114 // CROSS_COMPARTMENT flags will not have their finalizer called if they are
115 // nursery allocated and not promoted to the tenured heap. The finalizers for
116 // these classes must do nothing except free data which was allocated via
117 // Nursery::allocateBuffer.
CanNurseryAllocateFinalizedClass(const JSClass * const clasp)118 inline bool CanNurseryAllocateFinalizedClass(const JSClass* const clasp) {
119 MOZ_ASSERT(clasp->hasFinalize());
120 return clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE;
121 }
122
123 class Nursery {
124 public:
125 static const size_t Alignment = gc::ChunkSize;
126 static const size_t ChunkShift = gc::ChunkShift;
127
128 using BufferRelocationOverlay = void*;
129 using BufferSet = HashSet<void*, PointerHasher<void*>, SystemAllocPolicy>;
130
131 explicit Nursery(gc::GCRuntime* gc);
132 ~Nursery();
133
134 [[nodiscard]] bool init(AutoLockGCBgAlloc& lock);
135
136 // Number of allocated (ready to use) chunks.
allocatedChunkCount()137 unsigned allocatedChunkCount() const { return chunks_.length(); }
138
139 // Total number of chunks and the capacity of the nursery. Chunks will be
140 // lazilly allocated and added to the chunks array up to this limit, after
141 // that the nursery must be collected, this limit may be raised during
142 // collection.
maxChunkCount()143 unsigned maxChunkCount() const {
144 MOZ_ASSERT(capacity());
145 return HowMany(capacity(), gc::ChunkSize);
146 }
147
148 void enable();
149 void disable();
isEnabled()150 bool isEnabled() const { return capacity() != 0; }
151
152 void enableStrings();
153 void disableStrings();
canAllocateStrings()154 bool canAllocateStrings() const { return canAllocateStrings_; }
155
156 void enableBigInts();
157 void disableBigInts();
canAllocateBigInts()158 bool canAllocateBigInts() const { return canAllocateBigInts_; }
159
160 // Return true if no allocations have been made since the last collection.
161 bool isEmpty() const;
162
163 // Check whether an arbitrary pointer is within the nursery. This is
164 // slower than IsInsideNursery(Cell*), but works on all types of pointers.
165 MOZ_ALWAYS_INLINE bool isInside(gc::Cell* cellp) const = delete;
isInside(const void * p)166 MOZ_ALWAYS_INLINE bool isInside(const void* p) const {
167 for (auto chunk : chunks_) {
168 if (uintptr_t(p) - uintptr_t(chunk) < gc::ChunkSize) {
169 return true;
170 }
171 }
172 return false;
173 }
174
175 template <typename T>
176 inline bool isInside(const SharedMem<T>& p) const;
177
178 // Allocate and return a pointer to a new GC object with its |slots|
179 // pointer pre-filled. Returns nullptr if the Nursery is full.
180 JSObject* allocateObject(gc::AllocSite* site, size_t size,
181 size_t numDynamicSlots, const JSClass* clasp);
182
183 // Allocate and return a pointer to a new GC thing. Returns nullptr if the
184 // Nursery is full.
185 gc::Cell* allocateCell(gc::AllocSite* site, size_t size, JS::TraceKind kind);
186
allocateBigInt(gc::AllocSite * site,size_t size)187 gc::Cell* allocateBigInt(gc::AllocSite* site, size_t size) {
188 return allocateCell(site, size, JS::TraceKind::BigInt);
189 }
190 gc::Cell* allocateString(gc::AllocSite* site, size_t size);
191
nurseryCellHeaderSize()192 static size_t nurseryCellHeaderSize() {
193 return sizeof(gc::NurseryCellHeader);
194 }
195
196 // Allocate a buffer for a given zone, using the nursery if possible.
197 void* allocateBuffer(JS::Zone* zone, size_t nbytes);
198
199 // Allocate a buffer for a given object, using the nursery if possible and
200 // obj is in the nursery.
201 void* allocateBuffer(JSObject* obj, size_t nbytes);
202
203 // Allocate a buffer for a given object, always using the nursery if obj is
204 // in the nursery. The requested size must be less than or equal to
205 // MaxNurseryBufferSize.
206 void* allocateBufferSameLocation(JSObject* obj, size_t nbytes);
207
208 // Allocate a zero-initialized buffer for a given zone, using the nursery if
209 // possible. If the buffer isn't allocated in the nursery, the given arena is
210 // used.
211 void* allocateZeroedBuffer(JS::Zone* zone, size_t nbytes,
212 arena_id_t arena = js::MallocArena);
213
214 // Allocate a zero-initialized buffer for a given object, using the nursery if
215 // possible and obj is in the nursery. If the buffer isn't allocated in the
216 // nursery, the given arena is used.
217 void* allocateZeroedBuffer(JSObject* obj, size_t nbytes,
218 arena_id_t arena = js::MallocArena);
219
220 // Resize an existing buffer.
221 void* reallocateBuffer(JS::Zone* zone, gc::Cell* cell, void* oldBuffer,
222 size_t oldBytes, size_t newBytes);
223
224 // Allocate a digits buffer for a given BigInt, using the nursery if possible
225 // and |bi| is in the nursery.
226 void* allocateBuffer(JS::BigInt* bi, size_t nbytes);
227
228 // Free an object buffer.
229 void freeBuffer(void* buffer, size_t nbytes);
230
231 // The maximum number of bytes allowed to reside in nursery buffers.
232 static const size_t MaxNurseryBufferSize = 1024;
233
234 // Do a minor collection.
235 void collect(JS::GCOptions options, JS::GCReason reason);
236
237 // If the thing at |*ref| in the Nursery has been forwarded, set |*ref| to
238 // the new location and return true. Otherwise return false and leave
239 // |*ref| unset.
240 [[nodiscard]] MOZ_ALWAYS_INLINE static bool getForwardedPointer(
241 js::gc::Cell** ref);
242
243 // Forward a slots/elements pointer stored in an Ion frame.
244 void forwardBufferPointer(uintptr_t* pSlotsElems);
245
246 inline void maybeSetForwardingPointer(JSTracer* trc, void* oldData,
247 void* newData, bool direct);
248 inline void setForwardingPointerWhileTenuring(void* oldData, void* newData,
249 bool direct);
250
251 // Register a malloced buffer that is held by a nursery object, which
252 // should be freed at the end of a minor GC. Buffers are unregistered when
253 // their owning objects are tenured.
254 [[nodiscard]] bool registerMallocedBuffer(void* buffer, size_t nbytes);
255
256 // Mark a malloced buffer as no longer needing to be freed.
removeMallocedBuffer(void * buffer,size_t nbytes)257 void removeMallocedBuffer(void* buffer, size_t nbytes) {
258 MOZ_ASSERT(mallocedBuffers.has(buffer));
259 MOZ_ASSERT(nbytes > 0);
260 MOZ_ASSERT(mallocedBufferBytes >= nbytes);
261 mallocedBuffers.remove(buffer);
262 mallocedBufferBytes -= nbytes;
263 }
264
265 // Mark a malloced buffer as no longer needing to be freed during minor
266 // GC. There's no need to account for the size here since all remaining
267 // buffers will soon be freed.
removeMallocedBufferDuringMinorGC(void * buffer)268 void removeMallocedBufferDuringMinorGC(void* buffer) {
269 MOZ_ASSERT(JS::RuntimeHeapIsMinorCollecting());
270 MOZ_ASSERT(mallocedBuffers.has(buffer));
271 mallocedBuffers.remove(buffer);
272 }
273
addedUniqueIdToCell(gc::Cell * cell)274 [[nodiscard]] bool addedUniqueIdToCell(gc::Cell* cell) {
275 MOZ_ASSERT(IsInsideNursery(cell));
276 MOZ_ASSERT(isEnabled());
277 return cellsWithUid_.append(cell);
278 }
279
sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf)280 size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const {
281 size_t total = 0;
282 for (BufferSet::Range r = mallocedBuffers.all(); !r.empty(); r.popFront()) {
283 total += mallocSizeOf(r.front());
284 }
285 total += mallocedBuffers.shallowSizeOfExcludingThis(mallocSizeOf);
286 return total;
287 }
288
289 // The number of bytes from the start position to the end of the nursery.
290 // pass maxChunkCount(), allocatedChunkCount() or chunkCountLimit()
291 // to calculate the nursery size, current lazy-allocated size or nursery
292 // limit respectively.
293 size_t spaceToEnd(unsigned chunkCount) const;
294
capacity()295 size_t capacity() const { return capacity_; }
committed()296 size_t committed() const { return spaceToEnd(allocatedChunkCount()); }
297
298 // Used and free space both include chunk headers for that part of the
299 // nursery.
300 //
301 // usedSpace() + freeSpace() == capacity()
302 //
usedSpace()303 MOZ_ALWAYS_INLINE size_t usedSpace() const {
304 return capacity() - freeSpace();
305 }
freeSpace()306 MOZ_ALWAYS_INLINE size_t freeSpace() const {
307 MOZ_ASSERT(isEnabled());
308 MOZ_ASSERT(currentEnd_ - position_ <= NurseryChunkUsableSize);
309 MOZ_ASSERT(currentChunk_ < maxChunkCount());
310 return (currentEnd_ - position_) +
311 (maxChunkCount() - currentChunk_ - 1) * gc::ChunkSize;
312 }
313
314 #ifdef JS_GC_ZEAL
315 void enterZealMode();
316 void leaveZealMode();
317 #endif
318
319 // Write profile time JSON on JSONPrinter.
320 void renderProfileJSON(JSONPrinter& json) const;
321
322 // Print header line for profile times.
323 void printProfileHeader();
324
325 // Print total profile times on shutdown.
326 void printTotalProfileTimes();
327
addressOfPosition()328 void* addressOfPosition() const { return (void**)&position_; }
addressOfCurrentEnd()329 const void* addressOfCurrentEnd() const { return (void**)¤tEnd_; }
addressOfCurrentStringEnd()330 const void* addressOfCurrentStringEnd() const {
331 return (void*)¤tStringEnd_;
332 }
addressOfCurrentBigIntEnd()333 const void* addressOfCurrentBigIntEnd() const {
334 return (void*)¤tBigIntEnd_;
335 }
addressOfNurseryAllocatedSites()336 void* addressOfNurseryAllocatedSites() {
337 return pretenuringNursery.addressOfAllocatedSites();
338 }
339
340 void requestMinorGC(JS::GCReason reason) const;
341
minorGCRequested()342 bool minorGCRequested() const {
343 return minorGCTriggerReason_ != JS::GCReason::NO_REASON;
344 }
minorGCTriggerReason()345 JS::GCReason minorGCTriggerReason() const { return minorGCTriggerReason_; }
clearMinorGCRequest()346 void clearMinorGCRequest() {
347 minorGCTriggerReason_ = JS::GCReason::NO_REASON;
348 }
349
350 bool shouldCollect() const;
351 bool isNearlyFull() const;
352 bool isUnderused() const;
353
enableProfiling()354 bool enableProfiling() const { return enableProfiling_; }
355
addMapWithNurseryMemory(MapObject * obj)356 bool addMapWithNurseryMemory(MapObject* obj) {
357 MOZ_ASSERT_IF(!mapsWithNurseryMemory_.empty(),
358 mapsWithNurseryMemory_.back() != obj);
359 return mapsWithNurseryMemory_.append(obj);
360 }
addSetWithNurseryMemory(SetObject * obj)361 bool addSetWithNurseryMemory(SetObject* obj) {
362 MOZ_ASSERT_IF(!setsWithNurseryMemory_.empty(),
363 setsWithNurseryMemory_.back() != obj);
364 return setsWithNurseryMemory_.append(obj);
365 }
366
367 // The amount of space in the mapped nursery available to allocations.
368 static const size_t NurseryChunkUsableSize =
369 gc::ChunkSize - sizeof(gc::ChunkBase);
370
joinDecommitTask()371 void joinDecommitTask() { decommitTask.join(); }
372
collectionStartTime()373 mozilla::TimeStamp collectionStartTime() {
374 return startTimes_[ProfileKey::Total];
375 }
376
canCreateAllocSite()377 bool canCreateAllocSite() { return pretenuringNursery.canCreateAllocSite(); }
noteAllocSiteCreated()378 void noteAllocSiteCreated() { pretenuringNursery.noteAllocSiteCreated(); }
reportPretenuring()379 bool reportPretenuring() const { return reportPretenuring_; }
maybeStopPretenuring(gc::GCRuntime * gc)380 void maybeStopPretenuring(gc::GCRuntime* gc) {
381 pretenuringNursery.maybeStopPretenuring(gc);
382 }
383
384 // Round a size in bytes to the nearest valid nursery size.
385 static size_t roundSize(size_t size);
386
387 private:
388 gc::GCRuntime* const gc;
389
390 // Vector of allocated chunks to allocate from.
391 Vector<NurseryChunk*, 0, SystemAllocPolicy> chunks_;
392
393 // Pointer to the first unallocated byte in the nursery.
394 uintptr_t position_;
395
396 // These fields refer to the beginning of the nursery. They're normally 0
397 // and chunk(0).start() respectively. Except when a generational GC zeal
398 // mode is active, then they may be arbitrary (see Nursery::clear()).
399 unsigned currentStartChunk_;
400 uintptr_t currentStartPosition_;
401
402 // Pointer to the last byte of space in the current chunk.
403 uintptr_t currentEnd_;
404
405 // Pointer to the last byte of space in the current chunk, or nullptr if we
406 // are not allocating strings in the nursery.
407 uintptr_t currentStringEnd_;
408
409 // Pointer to the last byte of space in the current chunk, or nullptr if we
410 // are not allocating BigInts in the nursery.
411 uintptr_t currentBigIntEnd_;
412
413 // The index of the chunk that is currently being allocated from.
414 unsigned currentChunk_;
415
416 // The current nursery capacity measured in bytes. It may grow up to this
417 // value without a collection, allocating chunks on demand. This limit may be
418 // changed by maybeResizeNursery() each collection. It includes chunk headers.
419 size_t capacity_;
420
421 gc::PretenuringNursery pretenuringNursery;
422
423 mozilla::TimeDuration timeInChunkAlloc_;
424
425 // Report minor collections taking at least this long, if enabled.
426 bool enableProfiling_;
427 bool profileWorkers_;
428 mozilla::TimeDuration profileThreshold_;
429
430 // Whether we will nursery-allocate strings.
431 bool canAllocateStrings_;
432
433 // Whether we will nursery-allocate BigInts.
434 bool canAllocateBigInts_;
435
436 // Report how many strings were deduplicated.
437 bool reportDeduplications_;
438
439 // Whether to report information on pretenuring, and if so the allocation
440 // threshold at which to report details of each allocation site.
441 bool reportPretenuring_;
442 size_t reportPretenuringThreshold_;
443
444 // Whether and why a collection of this nursery has been requested. This is
445 // mutable as it is set by the store buffer, which otherwise cannot modify
446 // anything in the nursery.
447 mutable JS::GCReason minorGCTriggerReason_;
448
449 // Profiling data.
450
451 enum class ProfileKey {
452 #define DEFINE_TIME_KEY(name, text) name,
453 FOR_EACH_NURSERY_PROFILE_TIME(DEFINE_TIME_KEY)
454 #undef DEFINE_TIME_KEY
455 KeyCount
456 };
457
458 using ProfileTimes =
459 mozilla::EnumeratedArray<ProfileKey, ProfileKey::KeyCount,
460 mozilla::TimeStamp>;
461 using ProfileDurations =
462 mozilla::EnumeratedArray<ProfileKey, ProfileKey::KeyCount,
463 mozilla::TimeDuration>;
464
465 ProfileTimes startTimes_;
466 ProfileDurations profileDurations_;
467 ProfileDurations totalDurations_;
468
469 // Data about the previous collection.
470 struct PreviousGC {
471 JS::GCReason reason = JS::GCReason::NO_REASON;
472 size_t nurseryCapacity = 0;
473 size_t nurseryCommitted = 0;
474 size_t nurseryUsedBytes = 0;
475 size_t tenuredBytes = 0;
476 size_t tenuredCells = 0;
477 mozilla::TimeStamp endTime;
478 };
479 PreviousGC previousGC;
480
481 bool hasRecentGrowthData;
482 double smoothedTargetSize;
483
484 // Calculate the promotion rate of the most recent minor GC.
485 // The valid_for_tenuring parameter is used to return whether this
486 // promotion rate is accurate enough (the nursery was full enough) to be
487 // used for tenuring and other decisions.
488 //
489 // Must only be called if the previousGC data is initialised.
490 double calcPromotionRate(bool* validForTenuring) const;
491
492 // The set of externally malloced buffers potentially kept live by objects
493 // stored in the nursery. Any external buffers that do not belong to a
494 // tenured thing at the end of a minor GC must be freed.
495 BufferSet mallocedBuffers;
496 size_t mallocedBufferBytes = 0;
497
498 // During a collection most hoisted slot and element buffers indicate their
499 // new location with a forwarding pointer at the base. This does not work
500 // for buffers whose length is less than pointer width, or when different
501 // buffers might overlap each other. For these, an entry in the following
502 // table is used.
503 typedef HashMap<void*, void*, PointerHasher<void*>, SystemAllocPolicy>
504 ForwardedBufferMap;
505 ForwardedBufferMap forwardedBuffers;
506
507 // When we assign a unique id to cell in the nursery, that almost always
508 // means that the cell will be in a hash table, and thus, held live,
509 // automatically moving the uid from the nursery to its new home in
510 // tenured. It is possible, if rare, for an object that acquired a uid to
511 // be dead before the next collection, in which case we need to know to
512 // remove it when we sweep.
513 //
514 // Note: we store the pointers as Cell* here, resulting in an ugly cast in
515 // sweep. This is because this structure is used to help implement
516 // stable object hashing and we have to break the cycle somehow.
517 using CellsWithUniqueIdVector = Vector<gc::Cell*, 8, SystemAllocPolicy>;
518 CellsWithUniqueIdVector cellsWithUid_;
519
520 template <typename Key>
521 struct DeduplicationStringHasher {
522 using Lookup = Key;
523
hashDeduplicationStringHasher524 static inline HashNumber hash(const Lookup& lookup) {
525 JS::AutoCheckCannotGC nogc;
526 HashNumber strHash;
527
528 // Include flags in the hash. A string relocation overlay stores either
529 // the nursery root base chars or the dependent string nursery base, but
530 // does not indicate which one. If strings with different string types
531 // were deduplicated, for example, a dependent string gets deduplicated
532 // into an extensible string, the base chain would be broken and the root
533 // base would be unreachable.
534
535 if (lookup->asLinear().hasLatin1Chars()) {
536 strHash = mozilla::HashString(lookup->asLinear().latin1Chars(nogc),
537 lookup->length());
538 } else {
539 MOZ_ASSERT(lookup->asLinear().hasTwoByteChars());
540 strHash = mozilla::HashString(lookup->asLinear().twoByteChars(nogc),
541 lookup->length());
542 }
543
544 return mozilla::HashGeneric(strHash, lookup->zone(), lookup->flags());
545 }
546
matchDeduplicationStringHasher547 static MOZ_ALWAYS_INLINE bool match(const Key& key, const Lookup& lookup) {
548 if (!key->sameLengthAndFlags(*lookup) ||
549 key->asTenured().zone() != lookup->zone() ||
550 key->asTenured().getAllocKind() != lookup->getAllocKind()) {
551 return false;
552 }
553
554 JS::AutoCheckCannotGC nogc;
555
556 if (key->asLinear().hasLatin1Chars()) {
557 MOZ_ASSERT(lookup->asLinear().hasLatin1Chars());
558 return mozilla::ArrayEqual(key->asLinear().latin1Chars(nogc),
559 lookup->asLinear().latin1Chars(nogc),
560 lookup->length());
561 } else {
562 MOZ_ASSERT(key->asLinear().hasTwoByteChars());
563 MOZ_ASSERT(lookup->asLinear().hasTwoByteChars());
564 return EqualChars(key->asLinear().twoByteChars(nogc),
565 lookup->asLinear().twoByteChars(nogc),
566 lookup->length());
567 }
568 }
569 };
570
571 using StringDeDupSet =
572 HashSet<JSString*, DeduplicationStringHasher<JSString*>,
573 SystemAllocPolicy>;
574
575 // deDupSet is emplaced at the beginning of the nursery collection and reset
576 // at the end of the nursery collection. It can also be reset during nursery
577 // collection when out of memory to insert new entries.
578 mozilla::Maybe<StringDeDupSet> stringDeDupSet;
579
580 // Lists of map and set objects allocated in the nursery or with iterators
581 // allocated there. Such objects need to be swept after minor GC.
582 Vector<MapObject*, 0, SystemAllocPolicy> mapsWithNurseryMemory_;
583 Vector<SetObject*, 0, SystemAllocPolicy> setsWithNurseryMemory_;
584
585 NurseryDecommitTask decommitTask;
586
587 #ifdef JS_GC_ZEAL
588 struct Canary;
589 Canary* lastCanary_;
590 #endif
591
chunk(unsigned index)592 NurseryChunk& chunk(unsigned index) const { return *chunks_[index]; }
593
594 // Set the current chunk. This updates the currentChunk_, position_
595 // currentEnd_ and currentStringEnd_ values as approprite. It'll also
596 // poison the chunk, either a portion of the chunk if it is already the
597 // current chunk, or the whole chunk if fullPoison is true or it is not
598 // the current chunk.
599 void setCurrentChunk(unsigned chunkno);
600
601 bool initFirstChunk(AutoLockGCBgAlloc& lock);
602
603 // extent is advisory, it will be ignored in sub-chunk and generational zeal
604 // modes. It will be clamped to Min(NurseryChunkUsableSize, capacity_).
605 void poisonAndInitCurrentChunk(size_t extent = gc::ChunkSize);
606
607 void setCurrentEnd();
608 void setStartPosition();
609
610 // Allocate the next chunk, or the first chunk for initialization.
611 // Callers will probably want to call setCurrentChunk(0) next.
612 [[nodiscard]] bool allocateNextChunk(unsigned chunkno,
613 AutoLockGCBgAlloc& lock);
614
615 MOZ_ALWAYS_INLINE uintptr_t currentEnd() const;
616
position()617 uintptr_t position() const { return position_; }
618
619 MOZ_ALWAYS_INLINE bool isSubChunkMode() const;
620
621 JSRuntime* runtime() const;
622 gcstats::Statistics& stats() const;
623
624 const js::gc::GCSchedulingTunables& tunables() const;
625
626 // Common internal allocator function.
627 void* allocate(size_t size);
628
629 void* moveToNextChunkAndAllocate(size_t size);
630
631 #ifdef JS_GC_ZEAL
632 void writeCanary(uintptr_t address);
633 #endif
634
635 struct CollectionResult {
636 size_t tenuredBytes;
637 size_t tenuredCells;
638 };
639 CollectionResult doCollection(JS::GCReason reason);
640 void traceRoots(gc::AutoGCSession& session, TenuringTracer& mover);
641
642 size_t doPretenuring(JSRuntime* rt, JS::GCReason reason,
643 bool validPromotionRate, double promotionRate);
644
645 // Handle relocation of slots/elements pointers stored in Ion frames.
646 inline void setForwardingPointer(void* oldData, void* newData, bool direct);
647
648 inline void setDirectForwardingPointer(void* oldData, void* newData);
649 void setIndirectForwardingPointer(void* oldData, void* newData);
650
651 inline void setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots,
652 uint32_t nslots);
653 inline void setElementsForwardingPointer(ObjectElements* oldHeader,
654 ObjectElements* newHeader,
655 uint32_t capacity);
656
657 #ifdef DEBUG
658 bool checkForwardingPointerLocation(void* ptr, bool expectedInside);
659 #endif
660
661 // Updates pointers to nursery objects that have been tenured and discards
662 // pointers to objects that have been freed.
663 void sweep();
664
665 // Reset the current chunk and position after a minor collection. Also poison
666 // the nursery on debug & nightly builds.
667 void clear();
668
669 void sweepMapAndSetObjects();
670
671 // Change the allocable space provided by the nursery.
672 void maybeResizeNursery(JS::GCOptions options, JS::GCReason reason);
673 size_t targetSize(JS::GCOptions options, JS::GCReason reason);
674 void clearRecentGrowthData();
675 void growAllocableSpace(size_t newCapacity);
676 void shrinkAllocableSpace(size_t newCapacity);
677 void minimizeAllocableSpace();
678
679 // Free the chunks starting at firstFreeChunk until the end of the chunks
680 // vector. Shrinks the vector but does not update maxChunkCount().
681 void freeChunksFrom(unsigned firstFreeChunk);
682
683 void sendTelemetry(JS::GCReason reason, mozilla::TimeDuration totalTime,
684 bool wasEmpty, double promotionRate,
685 size_t sitesPretenured);
686
687 void printCollectionProfile(JS::GCReason reason, double promotionRate);
688 void printDeduplicationData(js::StringStats& prev, js::StringStats& curr);
689
690 // Profile recording and printing.
691 void maybeClearProfileDurations();
692 void startProfile(ProfileKey key);
693 void endProfile(ProfileKey key);
694 static void printProfileDurations(FILE* file, const ProfileDurations& times);
695
696 mozilla::TimeStamp collectionStartTime() const;
697 mozilla::TimeStamp lastCollectionEndTime() const;
698
699 friend class TenuringTracer;
700 friend class gc::MinorCollectionTracer;
701 friend class jit::MacroAssembler;
702 friend struct NurseryChunk;
703 };
704
705 } // namespace js
706
707 #endif // gc_Nursery_h
708